Follow zbus API break, everything async /o\

for better or worse...

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2021-09-20 16:52:03 +04:00
parent 7242c46513
commit fabfa85adc
22 changed files with 769 additions and 894 deletions

View File

@ -11,18 +11,8 @@ members = [
default-members = ["qemu-rdw"] default-members = ["qemu-rdw"]
[patch.crates-io] [patch.crates-io]
vnc = { git = 'https://github.com/elmarco/rust-vnc', branch = 'server' } vnc = { git = "https://github.com/elmarco/rust-vnc", branch = "server" }
usbredirhost = { path = "../usbredir-rs/usbredirhost" } zbus = { git = "https://gitlab.freedesktop.org/dbus/zbus.git" }
libusb1-sys = { path = "../rusb/libusb1-sys" } zvariant = { git = "https://gitlab.freedesktop.org/dbus/zbus.git" }
rusb = { path = "../rusb" } #zbus = { path = "../zbus/zbus" }
zbus = { path = "../zbus/zbus" } #zvariant = { path = "../zbus/zvariant" }
zvariant = { path = "../zbus/zvariant" }
[patch."https://gitlab.gnome.org/malureau/rdw.git"]
rdw = { path = '../rdw/rdw' }
[patch."https://github.com/gtk-rs/gtk4-rs"]
gdk4-wayland = { path = '../gtk4-rs/gdk4-wayland' }
gdk4-x11 = { path = '../gtk4-rs/gdk4-x11' }
gtk4 = { path = '../gtk4-rs/gtk4' }
gtk4-sys = { path = '../gtk4-rs/gtk4/sys' }

View File

@ -12,7 +12,6 @@ derivative = "2.2.0"
zbus = { version = "2.0.0-beta", features = ["xml"] } zbus = { version = "2.0.0-beta", features = ["xml"] }
zvariant = { version = "2.4.0", features = ["serde_bytes"] } zvariant = { version = "2.4.0", features = ["serde_bytes"] }
libc = "0.2.86" libc = "0.2.86"
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", optional = true }
enumflags2 = { version = "0.6.4", features = ["serde"] } enumflags2 = { version = "0.6.4", features = ["serde"] }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_repr = "0.1.6" serde_repr = "0.1.6"
@ -22,3 +21,4 @@ once_cell = "1.5"
futures = "0.3.13" futures = "0.3.13"
usbredirhost = "0.0.1" usbredirhost = "0.0.1"
async-broadcast = "0.3.3" async-broadcast = "0.3.3"
async-trait = "0.1.48"

View File

@ -1,13 +1,7 @@
use once_cell::sync::OnceCell; use std::os::unix::{io::AsRawFd, net::UnixStream};
use std::default::Default; use zbus::{dbus_interface, dbus_proxy, zvariant::Fd, Connection};
use std::os::unix::net::UnixStream;
use std::sync::mpsc::{self, Receiver, SendError};
use std::sync::{Arc, Mutex};
use std::{os::unix::io::AsRawFd, thread};
use zbus::{dbus_interface, dbus_proxy, zvariant::Fd}; use crate::Result;
use crate::{EventSender, Result};
#[derive(Debug)] #[derive(Debug)]
pub struct PCMInfo { pub struct PCMInfo {
@ -50,24 +44,6 @@ pub struct Volume {
pub volume: Vec<u8>, pub volume: Vec<u8>,
} }
#[derive(Debug)]
pub enum AudioOutEvent {
Init { id: u64, info: PCMInfo },
Fini { id: u64 },
SetEnabled { id: u64, enabled: bool },
SetVolume { id: u64, volume: Volume },
Write { id: u64, data: Vec<u8> },
}
#[derive(Debug)]
pub enum AudioInEvent {
Init { id: u64, info: PCMInfo },
Fini { id: u64 },
SetEnabled { id: u64, enabled: bool },
SetVolume { id: u64, volume: Volume },
Read { id: u64 },
}
#[dbus_proxy( #[dbus_proxy(
default_service = "org.qemu", default_service = "org.qemu",
default_path = "/org/qemu/Display1/Audio", default_path = "/org/qemu/Display1/Audio",
@ -86,37 +62,31 @@ trait Audio {
pub struct Audio { pub struct Audio {
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
pub proxy: AsyncAudioProxy<'static>, pub proxy: AsyncAudioProxy<'static>,
out_listener: Option<Connection>,
in_listener: Option<Connection>,
} }
#[derive(Debug)] #[async_trait::async_trait]
pub(crate) struct AudioOutListener<E: EventSender<Event = AudioOutEvent>> { pub trait AudioOutHandler: 'static + Send + Sync {
tx: E, async fn init(&mut self, id: u64, info: PCMInfo);
err: Arc<OnceCell<SendError<AudioOutEvent>>>,
async fn fini(&mut self, id: u64);
async fn set_enabled(&mut self, id: u64, enabled: bool);
async fn set_volume(&mut self, id: u64, volume: Volume);
async fn write(&mut self, id: u64, data: Vec<u8>);
} }
impl<E: EventSender<Event = AudioOutEvent>> AudioOutListener<E> { struct AudioOutListener<H: AudioOutHandler> {
pub(crate) fn new(tx: E) -> Self { handler: H,
AudioOutListener {
tx,
err: Default::default(),
}
}
fn send(&mut self, event: AudioOutEvent) {
if let Err(e) = self.tx.send_event(event) {
let _ = self.err.set(e);
}
}
pub fn err(&self) -> Arc<OnceCell<SendError<AudioOutEvent>>> {
self.err.clone()
}
} }
#[dbus_interface(name = "org.qemu.Display1.AudioOutListener")] #[dbus_interface(name = "org.qemu.Display1.AudioOutListener")]
impl<E: 'static + EventSender<Event = AudioOutEvent>> AudioOutListener<E> { impl<H: AudioOutHandler> AudioOutListener<H> {
/// Init method /// Init method
fn init( async fn init(
&mut self, &mut self,
id: u64, id: u64,
bits: u8, bits: u8,
@ -128,80 +98,73 @@ impl<E: 'static + EventSender<Event = AudioOutEvent>> AudioOutListener<E> {
bytes_per_second: u32, bytes_per_second: u32,
be: bool, be: bool,
) { ) {
self.send(AudioOutEvent::Init { self.handler
id, .init(
info: PCMInfo { id,
bits, PCMInfo {
is_signed, bits,
is_float, is_signed,
freq, is_float,
nchannels, freq,
bytes_per_frame, nchannels,
bytes_per_second, bytes_per_frame,
be, bytes_per_second,
}, be,
}) },
)
.await
} }
/// Fini method /// Fini method
fn fini(&mut self, id: u64) { async fn fini(&mut self, id: u64) {
self.send(AudioOutEvent::Fini { id }) self.handler.fini(id).await
} }
/// SetEnabled method /// SetEnabled method
fn set_enabled(&mut self, id: u64, enabled: bool) { async fn set_enabled(&mut self, id: u64, enabled: bool) {
self.send(AudioOutEvent::SetEnabled { id, enabled }) self.handler.set_enabled(id, enabled).await
} }
/// SetVolume method /// SetVolume method
fn set_volume(&mut self, id: u64, mute: bool, volume: serde_bytes::ByteBuf) { async fn set_volume(&mut self, id: u64, mute: bool, volume: serde_bytes::ByteBuf) {
self.send(AudioOutEvent::SetVolume { self.handler
id, .set_volume(
volume: Volume { id,
mute, Volume {
volume: volume.into_vec(), mute,
}, volume: volume.into_vec(),
}); },
)
.await
} }
/// Write method /// Write method
fn write(&mut self, id: u64, data: serde_bytes::ByteBuf) { async fn write(&mut self, id: u64, data: serde_bytes::ByteBuf) {
self.send(AudioOutEvent::Write { self.handler.write(id, data.into_vec()).await
id,
data: data.into_vec(),
})
} }
} }
#[derive(Debug)] #[async_trait::async_trait]
pub(crate) struct AudioInListener<E: EventSender<Event = AudioInEvent>> { pub trait AudioInHandler: 'static + Send + Sync {
tx: E, async fn init(&mut self, id: u64, info: PCMInfo);
err: Arc<OnceCell<SendError<AudioInEvent>>>,
async fn fini(&mut self, id: u64);
async fn set_enabled(&mut self, id: u64, enabled: bool);
async fn set_volume(&mut self, id: u64, volume: Volume);
async fn read(&mut self, id: u64, size: u64) -> Vec<u8>;
} }
impl<E: EventSender<Event = AudioInEvent>> AudioInListener<E> { struct AudioInListener<H: AudioInHandler> {
pub(crate) fn new(tx: E) -> Self { handler: H,
AudioInListener {
tx,
err: Default::default(),
}
}
fn send(&mut self, event: AudioInEvent) {
if let Err(e) = self.tx.send_event(event) {
let _ = self.err.set(e);
}
}
pub fn err(&self) -> Arc<OnceCell<SendError<AudioInEvent>>> {
self.err.clone()
}
} }
#[dbus_interface(name = "org.qemu.Display1.AudioInListener")] #[dbus_interface(name = "org.qemu.Display1.AudioInListener")]
impl<E: 'static + EventSender<Event = AudioInEvent>> AudioInListener<E> { impl<H: AudioInHandler> AudioInListener<H> {
/// Init method /// Init method
fn init( async fn init(
&mut self, &mut self,
id: u64, id: u64,
bits: u8, bits: u8,
@ -213,116 +176,84 @@ impl<E: 'static + EventSender<Event = AudioInEvent>> AudioInListener<E> {
bytes_per_second: u32, bytes_per_second: u32,
be: bool, be: bool,
) { ) {
self.send(AudioInEvent::Init { self.handler
id, .init(
info: PCMInfo { id,
bits, PCMInfo {
is_signed, bits,
is_float, is_signed,
freq, is_float,
nchannels, freq,
bytes_per_frame, nchannels,
bytes_per_second, bytes_per_frame,
be, bytes_per_second,
}, be,
}) },
)
.await
} }
/// Fini method /// Fini method
fn fini(&mut self, id: u64) { async fn fini(&mut self, id: u64) {
self.send(AudioInEvent::Fini { id }) self.handler.fini(id).await
} }
/// SetEnabled method /// SetEnabled method
fn set_enabled(&mut self, id: u64, enabled: bool) { async fn set_enabled(&mut self, id: u64, enabled: bool) {
self.send(AudioInEvent::SetEnabled { id, enabled }) self.handler.set_enabled(id, enabled).await
} }
/// SetVolume method /// SetVolume method
fn set_volume(&mut self, id: u64, mute: bool, volume: serde_bytes::ByteBuf) { async fn set_volume(&mut self, id: u64, mute: bool, volume: serde_bytes::ByteBuf) {
self.send(AudioInEvent::SetVolume { self.handler
id, .set_volume(
volume: Volume { id,
mute, Volume {
volume: volume.into_vec(), mute,
}, volume: volume.into_vec(),
}); },
)
.await
} }
/// Read method /// Read method
fn read(&mut self, id: u64, size: u64) -> Vec<u8> { async fn read(&mut self, id: u64, size: u64) -> Vec<u8> {
dbg!((id, size)); self.handler.read(id, size).await
vec![0; size as usize] // dbg!((id, size));
// vec![0; size as usize]
} }
} }
impl Audio { impl Audio {
pub async fn new(conn: &zbus::azync::Connection) -> Result<Self> { pub async fn new(conn: &zbus::Connection) -> Result<Self> {
let proxy = AsyncAudioProxy::new(conn).await?; let proxy = AsyncAudioProxy::new(conn).await?;
Ok(Self { proxy }) Ok(Self {
proxy,
in_listener: None,
out_listener: None,
})
} }
pub async fn listen_out(&self) -> Result<Receiver<AudioOutEvent>> { pub async fn register_out_listener<H: AudioOutHandler>(&mut self, handler: H) -> Result<()> {
let (p0, p1) = UnixStream::pair()?; let (p0, p1) = UnixStream::pair()?;
let (tx, rx) = mpsc::channel();
self.proxy self.proxy
.register_out_listener(p0.as_raw_fd().into()) .register_out_listener(p0.as_raw_fd().into())
.await?; .await?;
let c = zbus::ConnectionBuilder::unix_stream(p1)
let _thread = thread::spawn(move || { .p2p()
let c = zbus::ConnectionBuilder::unix_stream(p1) .build()
.p2p()
.build()
.unwrap();
let mut s = zbus::ObjectServer::new(&c);
let listener = AudioOutListener::new(Mutex::new(tx));
let err = listener.err();
s.at("/org/qemu/Display1/AudioOutListener", listener)
.unwrap();
loop {
if let Err(e) = s.try_handle_next() {
eprintln!("Listener DBus error: {}", e);
return;
}
if let Some(e) = err.get() {
eprintln!("Listener channel error: {}", e);
return;
}
}
});
Ok(rx)
}
pub async fn listen_in(&self) -> Result<Receiver<AudioInEvent>> {
let (p0, p1) = UnixStream::pair()?;
let (tx, rx) = mpsc::channel();
self.proxy
.register_in_listener(p0.as_raw_fd().into())
.await?; .await?;
{
let _thread = thread::spawn(move || { let mut server = c.object_server_mut().await;
let c = zbus::ConnectionBuilder::unix_stream(p1) server
.p2p() .at(
.build() "/org/qemu/Display1/AudioOutListener",
AudioOutListener { handler },
)
.unwrap(); .unwrap();
let mut s = zbus::ObjectServer::new(&c); server.start_dispatch();
let listener = AudioInListener::new(Mutex::new(tx)); }
let err = listener.err(); self.out_listener.replace(c);
s.at("/org/qemu/Display1/AudioInListener", listener) Ok(())
.unwrap();
loop {
if let Err(e) = s.try_handle_next() {
eprintln!("Listener DBus error: {}", e);
return;
}
if let Some(e) = err.get() {
eprintln!("Listener channel error: {}", e);
return;
}
}
});
Ok(rx)
} }
} }

View File

@ -1,6 +1,8 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use zbus::dbus_proxy; use zbus::{
use zbus::zvariant::{Fd, ObjectPath}; dbus_proxy,
zvariant::{Fd, ObjectPath},
};
use crate::Result; use crate::Result;
@ -36,7 +38,7 @@ pub struct Chardev {
} }
impl Chardev { impl Chardev {
pub async fn new(conn: &zbus::azync::Connection, id: &str) -> Result<Self> { pub async fn new(conn: &zbus::Connection, id: &str) -> Result<Self> {
let obj_path = ObjectPath::try_from(format!("/org/qemu/Display1/Chardev_{}", id))?; let obj_path = ObjectPath::try_from(format!("/org/qemu/Display1/Chardev_{}", id))?;
let proxy = AsyncChardevProxy::builder(conn) let proxy = AsyncChardevProxy::builder(conn)
.path(&obj_path)? .path(&obj_path)?

View File

@ -1,12 +1,9 @@
use once_cell::sync::OnceCell;
use serde_repr::{Deserialize_repr, Serialize_repr}; use serde_repr::{Deserialize_repr, Serialize_repr};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex};
use zbus::{dbus_interface, dbus_proxy, zvariant::ObjectPath}; use zbus::{dbus_interface, dbus_proxy, zvariant::ObjectPath};
use zvariant::derive::Type; use zvariant::derive::Type;
use crate::{EventSender, Result}; use crate::Result;
#[repr(u32)] #[repr(u32)]
#[derive(Deserialize_repr, Serialize_repr, Type, Debug, Hash, PartialEq, Eq, Clone, Copy)] #[derive(Deserialize_repr, Serialize_repr, Type, Debug, Hash, PartialEq, Eq, Clone, Copy)]
@ -37,141 +34,88 @@ pub trait Clipboard {
) -> zbus::Result<(String, Vec<u8>)>; ) -> zbus::Result<(String, Vec<u8>)>;
} }
pub type ClipboardReplyTx = Sender<Result<(String, Vec<u8>)>>; #[async_trait::async_trait]
pub trait ClipboardHandler: 'static + Send + Sync {
async fn register(&mut self);
// TODO: replace events mpsc with async traits async fn unregister(&mut self);
#[derive(Debug)]
pub enum ClipboardEvent { async fn grab(&mut self, selection: ClipboardSelection, serial: u32, mimes: Vec<String>);
Register,
Unregister, async fn release(&mut self, selection: ClipboardSelection);
Grab {
selection: ClipboardSelection, async fn request(
serial: u32, &mut self,
mimes: Vec<String>,
},
Release {
selection: ClipboardSelection,
},
Request {
selection: ClipboardSelection, selection: ClipboardSelection,
mimes: Vec<String>, mimes: Vec<String>,
tx: Mutex<ClipboardReplyTx>, ) -> Result<(String, Vec<u8>)>;
},
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct ClipboardListener<E: EventSender<Event = ClipboardEvent>> { pub(crate) struct ClipboardListener<H: ClipboardHandler> {
tx: E, handler: H,
err: Arc<OnceCell<String>>,
} }
#[dbus_interface(name = "org.qemu.Display1.Clipboard")] #[dbus_interface(name = "org.qemu.Display1.Clipboard")]
impl<E: 'static + EventSender<Event = ClipboardEvent>> ClipboardListener<E> { impl<H: ClipboardHandler> ClipboardListener<H> {
fn register(&mut self) { async fn register(&mut self) {
self.send(ClipboardEvent::Register) self.handler.register().await;
} }
fn unregister(&mut self) { async fn unregister(&mut self) {
self.send(ClipboardEvent::Unregister) self.handler.unregister().await;
} }
fn grab(&mut self, selection: ClipboardSelection, serial: u32, mimes: Vec<String>) { async fn grab(&mut self, selection: ClipboardSelection, serial: u32, mimes: Vec<String>) {
self.send(ClipboardEvent::Grab { self.handler.grab(selection, serial, mimes).await;
selection,
serial,
mimes,
})
} }
fn release(&mut self, selection: ClipboardSelection) { async fn release(&mut self, selection: ClipboardSelection) {
self.send(ClipboardEvent::Release { selection }) self.handler.release(selection).await;
} }
fn request( async fn request(
&mut self, &mut self,
selection: ClipboardSelection, selection: ClipboardSelection,
mimes: Vec<String>, mimes: Vec<String>,
) -> zbus::fdo::Result<(String, Vec<u8>)> { ) -> zbus::fdo::Result<(String, Vec<u8>)> {
let (tx, rx) = channel(); self.handler
self.send(ClipboardEvent::Request { .request(selection, mimes)
selection, .await
mimes,
tx: Mutex::new(tx),
});
rx.recv()
.map_err(|e| zbus::fdo::Error::Failed(format!("Request recv failed: {}", e)))?
.map_err(|e| zbus::fdo::Error::Failed(format!("Request failed: {}", e))) .map_err(|e| zbus::fdo::Error::Failed(format!("Request failed: {}", e)))
} }
} }
impl<E: 'static + EventSender<Event = ClipboardEvent>> ClipboardListener<E> {
pub fn new(tx: E) -> Self {
Self {
tx,
err: Default::default(),
}
}
fn send(&mut self, event: ClipboardEvent) {
if let Err(e) = self.tx.send_event(event) {
let _ = self.err.set(e.to_string());
}
}
pub fn err(&self) -> Arc<OnceCell<String>> {
self.err.clone()
}
}
#[derive(derivative::Derivative)] #[derive(derivative::Derivative)]
#[derivative(Debug)] #[derivative(Debug)]
pub struct Clipboard { pub struct Clipboard {
conn: zbus::azync::Connection,
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
pub proxy: AsyncClipboardProxy<'static>, pub proxy: AsyncClipboardProxy<'static>,
conn: zbus::Connection,
} }
impl Clipboard { impl Clipboard {
pub async fn new(conn: &zbus::azync::Connection) -> Result<Self> { pub async fn new(conn: &zbus::Connection) -> Result<Self> {
let obj_path = ObjectPath::try_from("/org/qemu/Display1/Clipboard")?; let obj_path = ObjectPath::try_from("/org/qemu/Display1/Clipboard").unwrap();
let proxy = AsyncClipboardProxy::builder(conn) let proxy = AsyncClipboardProxy::builder(conn)
.path(&obj_path)? .path(&obj_path)?
.build() .build()
.await?; .await?;
Ok(Self { Ok(Self {
conn: conn.clone(),
proxy, proxy,
conn: conn.clone(),
}) })
} }
pub async fn register(&self) -> Result<()> { pub async fn register<H: ClipboardHandler>(&self, handler: H) -> Result<()> {
self.proxy.register().await?; self.conn
Ok(()) .object_server_mut()
} .await
} .at(
"/org/qemu/Display1/Clipboard",
#[cfg(feature = "glib")] ClipboardListener { handler },
impl Clipboard { )
pub async fn glib_listen(&self) -> Result<glib::Receiver<ClipboardEvent>> { .unwrap();
let (tx, rx) = glib::MainContext::channel(glib::source::Priority::default()); Ok(self.proxy.register().await?)
let c = self.conn.clone().into();
let _thread = std::thread::spawn(move || {
let mut s = zbus::ObjectServer::new(&c);
let listener = ClipboardListener::new(tx);
let err = listener.err();
s.at("/org/qemu/Display1/Clipboard", listener).unwrap();
loop {
if let Err(e) = s.try_handle_next() {
eprintln!("Listener DBus error: {}", e);
break;
}
if let Some(e) = err.get() {
eprintln!("Listener channel error: {}", e);
break;
}
}
});
Ok(rx)
} }
} }

View File

@ -1,16 +1,15 @@
use std::convert::TryFrom; use std::{
use std::os::unix::net::UnixStream; cell::RefCell,
use std::sync::mpsc::{self, Receiver, Sender}; convert::TryFrom,
use std::sync::Mutex; os::unix::{io::AsRawFd, net::UnixStream},
use std::{os::unix::io::AsRawFd, thread}; };
use zbus::{ use zbus::{
dbus_proxy, dbus_proxy,
zvariant::{Fd, ObjectPath}, zvariant::{Fd, ObjectPath},
Connection,
}; };
use crate::Result; use crate::{AsyncKeyboardProxy, AsyncMouseProxy, ConsoleListener, ConsoleListenerHandler, Result};
use crate::{AsyncKeyboardProxy, AsyncMouseProxy, ConsoleEvent, ConsoleListener};
#[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Console")] #[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Console")]
pub trait Console { pub trait Console {
@ -54,10 +53,11 @@ pub struct Console {
pub keyboard: AsyncKeyboardProxy<'static>, pub keyboard: AsyncKeyboardProxy<'static>,
#[derivative(Debug = "ignore")] #[derivative(Debug = "ignore")]
pub mouse: AsyncMouseProxy<'static>, pub mouse: AsyncMouseProxy<'static>,
listener: RefCell<Option<Connection>>,
} }
impl Console { impl Console {
pub async fn new(conn: &zbus::azync::Connection, idx: u32) -> Result<Self> { pub async fn new(conn: &Connection, idx: u32) -> Result<Self> {
let obj_path = ObjectPath::try_from(format!("/org/qemu/Display1/Console_{}", idx))?; let obj_path = ObjectPath::try_from(format!("/org/qemu/Display1/Console_{}", idx))?;
let proxy = AsyncConsoleProxy::builder(conn) let proxy = AsyncConsoleProxy::builder(conn)
.path(&obj_path)? .path(&obj_path)?
@ -75,30 +75,10 @@ impl Console {
proxy, proxy,
keyboard, keyboard,
mouse, mouse,
listener: RefCell::new(None),
}) })
} }
pub async fn dispatch_signals(&self) -> Result<()> {
use futures_util::{future::FutureExt, select};
if let Some(msg) = select!(
msg = self.proxy.next_signal().fuse() => {
msg?
},
msg = self.keyboard.next_signal().fuse() => {
msg?
},
msg = self.mouse.next_signal().fuse() => {
msg?
}
) {
if msg.primary_header().msg_type() == zbus::MessageType::Signal {
log::debug!("Ignoring {:?}", msg);
}
}
Ok(())
}
pub async fn label(&self) -> Result<String> { pub async fn label(&self) -> Result<String> {
Ok(self.proxy.label().await?) Ok(self.proxy.label().await?)
} }
@ -111,66 +91,25 @@ impl Console {
Ok(self.proxy.height().await?) Ok(self.proxy.height().await?)
} }
pub async fn listen(&self) -> Result<(Receiver<ConsoleEvent>, Sender<()>)> { pub async fn register_listener<H: ConsoleListenerHandler>(&self, handler: H) -> Result<()> {
let (p0, p1) = UnixStream::pair()?; let (p0, p1) = UnixStream::pair()?;
let (tx, rx) = mpsc::channel();
self.proxy.register_listener(p0.as_raw_fd().into()).await?; self.proxy.register_listener(p0.as_raw_fd().into()).await?;
let c = zbus::ConnectionBuilder::unix_stream(p1)
let (wait_tx, wait_rx) = mpsc::channel(); .p2p()
let _thread = thread::spawn(move || { .build()
let c = zbus::ConnectionBuilder::unix_stream(p1) .await?;
.p2p() {
.build() let mut server = c.object_server_mut().await;
server
.at("/org/qemu/Display1/Listener", ConsoleListener::new(handler))
.unwrap(); .unwrap();
let mut s = zbus::ObjectServer::new(&c); server.start_dispatch();
let listener = ConsoleListener::new(Mutex::new(tx), wait_rx); }
let err = listener.err(); self.listener.replace(Some(c));
s.at("/org/qemu/Display1/Listener", listener).unwrap(); Ok(())
loop { }
if let Err(e) = s.try_handle_next() {
eprintln!("Listener DBus error: {}", e);
return;
}
if let Some(e) = err.get() {
eprintln!("Listener channel error: {}", e);
return;
}
}
});
Ok((rx, wait_tx)) pub fn unregister_listener(&mut self) {
} self.listener.replace(None);
}
#[cfg(feature = "glib")]
impl Console {
pub async fn glib_listen(&self) -> Result<(glib::Receiver<ConsoleEvent>, Sender<()>)> {
let (p0, p1) = UnixStream::pair()?;
let (tx, rx) = glib::MainContext::channel(glib::source::Priority::default());
self.proxy.register_listener(p0.as_raw_fd().into()).await?;
let (wait_tx, wait_rx) = mpsc::channel();
let _thread = thread::spawn(move || {
let c = zbus::ConnectionBuilder::unix_stream(p1)
.p2p()
.build()
.unwrap();
let mut s = zbus::ObjectServer::new(&c);
let listener = ConsoleListener::new(tx, wait_rx);
let err = listener.err();
s.at("/org/qemu/Display1/Listener", listener).unwrap();
loop {
if let Err(e) = s.try_handle_next() {
eprintln!("Listener DBus error: {}", e);
break;
}
if let Some(e) = err.get() {
eprintln!("Listener channel error: {}", e);
break;
}
}
});
Ok((rx, wait_tx))
} }
} }

View File

@ -1,15 +1,10 @@
use once_cell::sync::OnceCell;
use std::ops::Drop;
use std::os::unix::io::IntoRawFd;
use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::mpsc::{Receiver, RecvError, SendError};
use std::sync::{Arc, Mutex};
use derivative::Derivative; use derivative::Derivative;
use std::{
ops::Drop,
os::unix::io::{AsRawFd, IntoRawFd, RawFd},
};
use zbus::{dbus_interface, zvariant::Fd}; use zbus::{dbus_interface, zvariant::Fd};
use crate::EventSender;
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Debug)] #[derivative(Debug)]
pub struct Scanout { pub struct Scanout {
@ -45,6 +40,17 @@ pub struct ScanoutDMABUF {
pub y0_top: bool, pub y0_top: bool,
} }
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Cursor {
pub width: i32,
pub height: i32,
pub hot_x: i32,
pub hot_y: i32,
#[derivative(Debug = "ignore")]
pub data: Vec<u8>,
}
impl Drop for ScanoutDMABUF { impl Drop for ScanoutDMABUF {
fn drop(&mut self) { fn drop(&mut self) {
if self.fd >= 0 { if self.fd >= 0 {
@ -68,39 +74,39 @@ pub struct MouseSet {
pub on: i32, pub on: i32,
} }
// TODO: replace events mpsc with async traits #[derive(Debug, Copy, Clone)]
#[derive(Debug)] pub struct UpdateDMABUF {
pub enum ConsoleEvent { pub x: i32,
Scanout(Scanout), pub y: i32,
Update(Update), pub w: i32,
ScanoutDMABUF(ScanoutDMABUF), pub h: i32,
UpdateDMABUF { }
x: i32,
y: i32, #[async_trait::async_trait]
w: i32, pub trait ConsoleListenerHandler: 'static + Send + Sync {
h: i32, async fn scanout(&mut self, scanout: Scanout);
},
MouseSet(MouseSet), async fn update(&mut self, update: Update);
CursorDefine {
width: i32, async fn scanout_dmabuf(&mut self, scanout: ScanoutDMABUF);
height: i32,
hot_x: i32, async fn update_dmabuf(&mut self, update: UpdateDMABUF);
hot_y: i32,
data: Vec<u8>, async fn mouse_set(&mut self, set: MouseSet);
},
Disconnected, async fn cursor_define(&mut self, cursor: Cursor);
fn disconnected(&mut self);
} }
#[derive(Debug)] #[derive(Debug)]
pub(crate) struct ConsoleListener<E: EventSender<Event = ConsoleEvent>> { pub(crate) struct ConsoleListener<H: ConsoleListenerHandler> {
tx: E, handler: H,
wait_rx: Mutex<Receiver<()>>,
err: Arc<OnceCell<SendError<ConsoleEvent>>>,
} }
#[dbus_interface(name = "org.qemu.Display1.Listener")] #[dbus_interface(name = "org.qemu.Display1.Listener")]
impl<E: 'static + EventSender<Event = ConsoleEvent>> ConsoleListener<E> { impl<H: ConsoleListenerHandler> ConsoleListener<H> {
fn scanout( async fn scanout(
&mut self, &mut self,
width: u32, width: u32,
height: u32, height: u32,
@ -108,16 +114,18 @@ impl<E: 'static + EventSender<Event = ConsoleEvent>> ConsoleListener<E> {
format: u32, format: u32,
data: serde_bytes::ByteBuf, data: serde_bytes::ByteBuf,
) { ) {
self.send(ConsoleEvent::Scanout(Scanout { self.handler
width, .scanout(Scanout {
height, width,
stride, height,
format, stride,
data: data.into_vec(), format,
})) data: data.into_vec(),
})
.await;
} }
fn update( async fn update(
&mut self, &mut self,
x: i32, x: i32,
y: i32, y: i32,
@ -127,19 +135,21 @@ impl<E: 'static + EventSender<Event = ConsoleEvent>> ConsoleListener<E> {
format: u32, format: u32,
data: serde_bytes::ByteBuf, data: serde_bytes::ByteBuf,
) { ) {
self.send(ConsoleEvent::Update(Update { self.handler
x, .update(Update {
y, x,
w, y,
h, w,
stride, h,
format, stride,
data: data.into_vec(), format,
})) data: data.into_vec(),
})
.await;
} }
#[dbus_interface(name = "ScanoutDMABUF")] #[dbus_interface(name = "ScanoutDMABUF")]
fn scanout_dmabuf( async fn scanout_dmabuf(
&mut self, &mut self,
fd: Fd, fd: Fd,
width: u32, width: u32,
@ -150,66 +160,58 @@ impl<E: 'static + EventSender<Event = ConsoleEvent>> ConsoleListener<E> {
y0_top: bool, y0_top: bool,
) { ) {
let fd = unsafe { libc::dup(fd.as_raw_fd()) }; let fd = unsafe { libc::dup(fd.as_raw_fd()) };
self.send(ConsoleEvent::ScanoutDMABUF(ScanoutDMABUF { self.handler
fd, .scanout_dmabuf(ScanoutDMABUF {
width, fd,
height, width,
stride, height,
fourcc, stride,
modifier, fourcc,
y0_top, modifier,
})) y0_top,
})
.await;
} }
#[dbus_interface(name = "UpdateDMABUF")] #[dbus_interface(name = "UpdateDMABUF")]
fn update_dmabuf(&mut self, x: i32, y: i32, w: i32, h: i32) { async fn update_dmabuf(&mut self, x: i32, y: i32, w: i32, h: i32) {
self.send(ConsoleEvent::UpdateDMABUF { x, y, w, h }); self.handler
if let Err(e) = self.wait() { .update_dmabuf(UpdateDMABUF { x, y, w, h })
eprintln!("update returned error: {}", e) .await;
}
} }
fn mouse_set(&mut self, x: i32, y: i32, on: i32) { async fn mouse_set(&mut self, x: i32, y: i32, on: i32) {
self.send(ConsoleEvent::MouseSet(MouseSet { x, y, on })) self.handler.mouse_set(MouseSet { x, y, on }).await;
} }
fn cursor_define(&mut self, width: i32, height: i32, hot_x: i32, hot_y: i32, data: Vec<u8>) { async fn cursor_define(
self.send(ConsoleEvent::CursorDefine { &mut self,
width, width: i32,
height, height: i32,
hot_x, hot_x: i32,
hot_y, hot_y: i32,
data, data: Vec<u8>,
}) ) {
self.handler
.cursor_define(Cursor {
width,
height,
hot_x,
hot_y,
data,
})
.await;
} }
} }
impl<E: EventSender<Event = ConsoleEvent>> ConsoleListener<E> { impl<H: ConsoleListenerHandler> ConsoleListener<H> {
pub(crate) fn new(tx: E, wait_rx: Receiver<()>) -> Self { pub(crate) fn new(handler: H) -> Self {
ConsoleListener { Self { handler }
tx,
wait_rx: Mutex::new(wait_rx),
err: Default::default(),
}
}
fn send(&mut self, event: ConsoleEvent) {
if let Err(e) = self.tx.send_event(event) {
let _ = self.err.set(e);
}
}
fn wait(&mut self) -> Result<(), RecvError> {
self.wait_rx.lock().unwrap().recv()
}
pub fn err(&self) -> Arc<OnceCell<SendError<ConsoleEvent>>> {
self.err.clone()
} }
} }
impl<E: EventSender<Event = ConsoleEvent>> Drop for ConsoleListener<E> { impl<H: ConsoleListenerHandler> Drop for ConsoleListener<H> {
fn drop(&mut self) { fn drop(&mut self) {
self.send(ConsoleEvent::Disconnected) self.handler.disconnected();
} }
} }

View File

@ -1,14 +1,14 @@
use futures::stream::{self, StreamExt}; use futures::stream::{self, StreamExt};
use std::collections::HashMap; use std::{
use std::convert::TryFrom; collections::HashMap,
use std::convert::TryInto; convert::{TryFrom, TryInto},
use zbus::azync::Connection; };
use zbus::fdo; use zbus::{
use zbus::fdo::ManagedObjects; fdo,
use zbus::names::BusName; fdo::ManagedObjects,
use zbus::names::OwnedUniqueName; names::{BusName, OwnedUniqueName, UniqueName, WellKnownName},
use zbus::names::UniqueName; Connection,
use zbus::names::WellKnownName; };
use zvariant::OwnedObjectPath; use zvariant::OwnedObjectPath;
use crate::{AsyncVMProxy, Audio, Chardev, Clipboard, Error, Result, UsbRedir}; use crate::{AsyncVMProxy, Audio, Chardev, Clipboard, Error, Result, UsbRedir};
@ -21,10 +21,15 @@ pub struct Display {
impl Display { impl Display {
pub async fn by_name(conn: &Connection) -> Result<HashMap<String, OwnedUniqueName>> { pub async fn by_name(conn: &Connection) -> Result<HashMap<String, OwnedUniqueName>> {
let mut hm = HashMap::new(); let mut hm = HashMap::new();
let list = fdo::AsyncDBusProxy::new(conn) let list = match fdo::AsyncDBusProxy::new(conn)
.await? .await?
.list_queued_owners(WellKnownName::from_str_unchecked("org.qemu")) .list_queued_owners(WellKnownName::from_str_unchecked("org.qemu"))
.await?; .await
{
Ok(list) => list,
Err(zbus::fdo::Error::NameHasNoOwner(_)) => vec![],
Err(e) => return Err(e.into()),
};
for dest in list.into_iter() { for dest in list.into_iter() {
let name = AsyncVMProxy::builder(conn) let name = AsyncVMProxy::builder(conn)
.destination(UniqueName::from(&dest))? .destination(UniqueName::from(&dest))?
@ -47,7 +52,7 @@ impl Display {
} else { } else {
"org.qemu".try_into().unwrap() "org.qemu".try_into().unwrap()
}; };
let objects = fdo::AsyncObjectManagerProxy::builder(&conn) let objects = fdo::AsyncObjectManagerProxy::builder(conn)
.destination(dest)? .destination(dest)?
.path("/org/qemu/Display1")? .path("/org/qemu/Display1")?
.build() .build()
@ -107,7 +112,6 @@ impl Display {
.collect() .collect()
.await; .await;
let redir = UsbRedir::new(chardevs); UsbRedir::new(chardevs)
redir
} }
} }

View File

@ -1,9 +1,6 @@
use usbredirhost::rusb; use usbredirhost::rusb;
use std::convert::Infallible; use std::{convert::Infallible, error, fmt, io};
use std::error;
use std::fmt;
use std::io;
#[derive(Debug)] #[derive(Debug)]
pub enum Error { pub enum Error {

View File

@ -1,25 +0,0 @@
use std::sync::mpsc::{SendError, Sender};
use std::sync::Mutex;
pub(crate) trait EventSender: Send + Sync {
type Event;
fn send_event(&self, t: Self::Event) -> Result<(), SendError<Self::Event>>;
}
impl<T: Send + Sync> EventSender for Mutex<Sender<T>> {
type Event = T;
fn send_event(&self, t: Self::Event) -> Result<(), SendError<Self::Event>> {
self.lock().unwrap().send(t)
}
}
#[cfg(feature = "glib")]
impl<T: Send + Sync> EventSender for glib::Sender<T> {
type Event = T;
fn send_event(&self, t: Self::Event) -> Result<(), SendError<Self::Event>> {
self.send(t)
}
}

View File

@ -3,9 +3,6 @@
mod error; mod error;
pub use error::*; pub use error::*;
mod event_sender;
use event_sender::*;
mod vm; mod vm;
pub use vm::*; pub use vm::*;

View File

@ -23,9 +23,9 @@ use crate::{Chardev, Error, Result};
#[derive(Debug)] #[derive(Debug)]
struct InnerHandler { struct InnerHandler {
#[allow(unused)] // keep the device opened, as rusb doesn't take it
device_fd: Option<zvariant::OwnedFd>, device_fd: Option<zvariant::OwnedFd>,
stream: UnixStream, stream: UnixStream,
stream_thread: JoinHandle<()>,
ctxt: rusb::Context, ctxt: rusb::Context,
ctxt_thread: Option<JoinHandle<()>>, ctxt_thread: Option<JoinHandle<()>>,
event: (UnixStream, UnixStream), event: (UnixStream, UnixStream),
@ -90,7 +90,7 @@ impl Handler {
Ok(it) => (it, None), Ok(it) => (it, None),
Err(rusb::Error::Access) => { Err(rusb::Error::Access) => {
let (bus, dev) = (device.bus_number(), device.address()); let (bus, dev) = (device.bus_number(), device.address());
let sysbus = zbus::azync::Connection::system().await?; let sysbus = zbus::Connection::system().await?;
let fd = AsyncSystemHelperProxy::new(&sysbus) let fd = AsyncSystemHelperProxy::new(&sysbus)
.await? .await?
.open_bus_dev(bus, dev) .open_bus_dev(bus, dev)
@ -110,7 +110,7 @@ impl Handler {
// really annoying libusb/usbredir APIs... // really annoying libusb/usbredir APIs...
let event = UnixStream::pair()?; let event = UnixStream::pair()?;
let event_fd = event.1.as_raw_fd(); let event_fd = event.1.as_raw_fd();
let stream_thread = std::thread::spawn(move || loop { std::thread::spawn(move || loop {
let ret = fd_poll_readable(stream_fd, Some(event_fd)); let ret = fd_poll_readable(stream_fd, Some(event_fd));
c.interrupt_handle_events(); c.interrupt_handle_events();
if ret.is_err() { if ret.is_err() {
@ -122,7 +122,6 @@ impl Handler {
inner: Arc::new(Mutex::new(InnerHandler { inner: Arc::new(Mutex::new(InnerHandler {
device_fd, device_fd,
stream, stream,
stream_thread,
event, event,
quit: false, quit: false,
ctxt: ctxt.clone(), ctxt: ctxt.clone(),
@ -162,7 +161,7 @@ impl Drop for Handler {
inner.quit = true; inner.quit = true;
inner.ctxt.interrupt_handle_events(); inner.ctxt.interrupt_handle_events();
// stream will be dropped and stream_thread will kick context_thread // stream will be dropped and stream_thread will kick context_thread
inner.event.0.write(&[0]).unwrap(); inner.event.0.write_all(&[0]).unwrap();
} }
} }
@ -234,24 +233,30 @@ impl UsbRedir {
) -> Result<bool> { ) -> Result<bool> {
let mut inner = self.inner.borrow_mut(); let mut inner = self.inner.borrow_mut();
let key = Key::from_device(device); let key = Key::from_device(device);
let handled = inner.handlers.contains_key(&key);
// We should do better and watch for owner properties changes, but this would require tasks
// anticipate result
let mut nfree = inner.n_available_chardev().await as _; let mut nfree = inner.n_available_chardev().await as _;
if state { match (state, handled) {
if !inner.handlers.contains_key(&key) { (true, false) => {
let chardev = inner let chardev = inner
.first_available_chardev() .first_available_chardev()
.await .await
.ok_or_else(|| Error::Failed("There are no free USB channels".into()))?; .ok_or_else(|| Error::Failed("There are no free USB channels".into()))?;
let handler = Handler::new(device, chardev).await?; let handler = Handler::new(device, chardev).await?;
inner.handlers.insert(key, handler); inner.handlers.insert(key, handler);
nfree -= 1;
}
(false, true) => {
inner.handlers.remove(&key);
nfree += 1;
}
_ => {
return Ok(state);
} }
nfree -= 1;
} else {
inner.handlers.remove(&key);
nfree += 1;
} }
// We should do better and watch for owner properties changes, but this would require tasks
let _ = inner.channel.0.broadcast(Event::NFreeChannels(nfree)).await; let _ = inner.channel.0.broadcast(Event::NFreeChannels(nfree)).await;
Ok(state) Ok(state)

View File

@ -11,9 +11,9 @@ log = "0.4"
pretty_env_logger = "0.4" pretty_env_logger = "0.4"
once_cell = "1.5" once_cell = "1.5"
zbus = { version = "2.0.0-beta" } zbus = { version = "2.0.0-beta" }
qemu-display = { path = "../qemu-display", features = ["glib"] } qemu-display = { path = "../qemu-display" }
keycodemap = { path = "../keycodemap" } keycodemap = { path = "../keycodemap" }
gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs" } rdw = { package = "rdw4", git = "https://gitlab.gnome.org/malureau/rdw.git" }
rdw = { git = "https://gitlab.gnome.org/malureau/rdw.git" }
futures-util = "0.3.13" futures-util = "0.3.13"
futures = "0.3.13" futures = "0.3.13"
async-trait = "0.1.48"

View File

@ -1,60 +1,57 @@
use std::error::Error; use std::{error::Error, result::Result};
use std::result::Result;
use std::thread;
use qemu_display::Audio; use qemu_display::{Audio, AudioOutHandler};
#[derive(Debug)]
pub struct Handler {
#[allow(unused)]
audio: Audio,
}
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Handler { pub struct OutListener {
thread: Option<thread::JoinHandle<()>>, gst: rdw::GstAudio,
}
#[async_trait::async_trait]
impl AudioOutHandler for OutListener {
async fn init(&mut self, id: u64, info: qemu_display::PCMInfo) {
if let Err(e) = self.gst.init_out(id, &info.gst_caps()) {
log::warn!("Failed to initialize audio stream: {}", e);
}
}
async fn fini(&mut self, id: u64) {
self.gst.fini_out(id);
}
async fn set_enabled(&mut self, id: u64, enabled: bool) {
if let Err(e) = self.gst.set_enabled_out(id, enabled) {
log::warn!("Failed to set enabled audio stream: {}", e);
}
}
async fn set_volume(&mut self, id: u64, volume: qemu_display::Volume) {
if let Err(e) = self.gst.set_volume_out(
id,
volume.mute,
volume.volume.first().map(|v| *v as f64 / 255f64),
) {
log::warn!("Failed to set volume: {}", e);
}
}
async fn write(&mut self, id: u64, data: Vec<u8>) {
if let Err(e) = self.gst.write_out(id, data) {
log::warn!("Failed to output stream: {}", e);
}
}
} }
impl Handler { impl Handler {
pub async fn new(audio: Audio) -> Result<Self, Box<dyn Error>> { pub async fn new(mut audio: Audio) -> Result<Handler, Box<dyn Error>> {
let rx = audio.listen_out().await?; let gst = rdw::GstAudio::new()?;
let mut gst = rdw::GstAudio::new()?; audio.register_out_listener(OutListener { gst }).await?;
Ok(Handler { audio })
let thread = thread::spawn(move || loop {
match rx.recv() {
Ok(event) => {
use qemu_display::AudioOutEvent::*;
match event {
Init { id, info } => {
if let Err(e) = gst.init_out(id, &info.gst_caps()) {
log::warn!("Failed to initialize audio stream: {}", e);
}
}
Fini { id } => {
gst.fini_out(id);
}
SetEnabled { id, enabled } => {
if let Err(e) = gst.set_enabled_out(id, enabled) {
log::warn!("Failed to set enabled audio stream: {}", e);
}
}
SetVolume { id, volume } => {
if let Err(e) = gst.set_volume_out(
id,
volume.mute,
volume.volume.first().map(|v| *v as f64 / 255f64),
) {
log::warn!("Failed to set volume: {}", e);
}
}
Write { id, data } => {
if let Err(e) = gst.write_out(id, data) {
log::warn!("Failed to output stream: {}", e);
}
}
}
}
Err(e) => log::warn!("Audio thread error: {}", e),
}
});
Ok(Self {
thread: Some(thread),
})
} }
} }

View File

@ -1,145 +1,195 @@
use std::cell::Cell; use std::{
use std::error::Error; error::Error,
use std::rc::Rc; result::Result,
use std::result::Result; sync::{
atomic::{AtomicU32, Ordering},
use crate::glib::{self, clone, prelude::*, SignalHandlerId, SourceId}; Arc,
use gtk::{gdk, gio, prelude::DisplayExt, prelude::*}; },
use qemu_display::{
self as qdl, AsyncClipboardProxy, Clipboard, ClipboardEvent, ClipboardSelection,
}; };
use glib::{clone, SignalHandlerId};
use gtk::{
gdk, gio, glib,
prelude::{DisplayExt, *},
};
use qemu_display::{AsyncClipboardProxy, Clipboard, ClipboardHandler, ClipboardSelection};
use rdw::gtk;
#[derive(Debug)] #[derive(Debug)]
pub struct Handler { pub struct Handler {
rx: SourceId, #[allow(unused)]
clipboard: Clipboard,
cb_handler: Option<SignalHandlerId>, cb_handler: Option<SignalHandlerId>,
cb_primary_handler: Option<SignalHandlerId>, cb_primary_handler: Option<SignalHandlerId>,
} }
impl Handler { #[derive(Debug)]
pub async fn new(ctxt: Clipboard) -> Result<Self, Box<dyn Error>> { struct InnerHandler {
let rx = ctxt proxy: AsyncClipboardProxy<'static>,
.glib_listen() serials: Arc<[AtomicU32; 2]>,
.await }
.expect("Failed to listen to the clipboard");
let proxy = ctxt.proxy.clone();
let serials = Rc::new([Cell::new(0), Cell::new(0)]);
let current_serials = serials.clone();
let rx = rx.attach(None, move |evt| {
use ClipboardEvent::*;
log::debug!("Clipboard event: {:?}", evt); impl InnerHandler {
match evt { fn reset_serials(&mut self) {
Register | Unregister => { self.serials[0].store(0, Ordering::SeqCst);
current_serials[0].set(0); self.serials[1].store(0, Ordering::SeqCst);
current_serials[1].set(0); }
} }
Grab {
selection,
serial,
mimes,
} => {
if let Some((clipboard, idx)) = clipboard_from_selection(selection) {
if serial < current_serials[idx].get() {
log::debug!("Ignored peer grab: {} < {}", serial, current_serials[idx].get());
return Continue(true);
}
current_serials[idx].set(serial); #[async_trait::async_trait]
let m: Vec<_> = mimes.iter().map(|s|s.as_str()).collect(); impl ClipboardHandler for InnerHandler {
let p = proxy.clone(); async fn register(&mut self) {
let content = rdw::ContentProvider::new(&m, move |mime, stream, prio| { self.reset_serials();
log::debug!("content-provider-write: {:?}", (mime, stream)); }
let p = p.clone(); async fn unregister(&mut self) {
let mime = mime.to_string(); self.reset_serials();
Some(Box::pin(clone!(@strong stream => @default-return panic!(), async move { }
match p.request(selection, &[&mime]).await {
Ok((_, data)) => {
let bytes = glib::Bytes::from(&data);
stream.write_bytes_async_future(&bytes, prio).await.map(|_| ())
}
Err(e) => {
let err = format!("failed to request clipboard data: {}", e);
log::warn!("{}", err);
Err(glib::Error::new(gio::IOErrorEnum::Failed, &err))
}
}
})))
});
if let Err(e) = clipboard.set_content(Some(&content)) { async fn grab(&mut self, selection: ClipboardSelection, serial: u32, mimes: Vec<String>) {
log::warn!("Failed to set clipboard grab: {}", e); if let Some((clipboard, idx)) = clipboard_from_selection(selection) {
} let cur_serial = self.serials[idx].load(Ordering::SeqCst);
} if serial < cur_serial {
} log::debug!("Ignored peer grab: {} < {}", serial, cur_serial);
Release { selection } => { return;
if let Some((clipboard, _)) = clipboard_from_selection(selection) {
// TODO: track if the outside/app changed the clipboard
if let Err(e) = clipboard.set_content(gdk::NONE_CONTENT_PROVIDER) {
log::warn!("Failed to release clipboard: {}", e);
}
}
}
Request { selection, mimes, tx } => {
if let Some((clipboard, _)) = clipboard_from_selection(selection) {
glib::MainContext::default().spawn_local(async move {
let m: Vec<_> = mimes.iter().map(|s|s.as_str()).collect();
let res = clipboard.read_async_future(&m, glib::Priority::default()).await;
log::debug!("clipboard-read: {}", res.is_ok());
let reply = match res {
Ok((stream, mime)) => {
let out = gio::MemoryOutputStream::new_resizable();
let res = out.splice_async_future(
&stream,
gio::OutputStreamSpliceFlags::CLOSE_SOURCE | gio::OutputStreamSpliceFlags::CLOSE_TARGET,
glib::Priority::default()).await;
match res {
Ok(_) => {
let data = out.steal_as_bytes();
Ok((mime.to_string(), data.as_ref().to_vec()))
}
Err(e) => {
Err(qdl::Error::Failed(format!("{}", e)))
}
}
}
Err(e) => {
Err(qdl::Error::Failed(format!("{}", e)))
}
};
let _ = tx.lock().unwrap().send(reply);
});
}
}
} }
Continue(true)
});
self.serials[idx].store(serial, Ordering::SeqCst);
let m: Vec<_> = mimes.iter().map(|s| s.as_str()).collect();
let p = self.proxy.clone();
let content = rdw::ContentProvider::new(&m, move |mime, stream, prio| {
log::debug!("content-provider-write: {:?}", (mime, stream));
let p = p.clone();
let mime = mime.to_string();
Some(Box::pin(
clone!(@strong stream => @default-return panic!(), async move {
match p.request(selection, &[&mime]).await {
Ok((_, data)) => {
let bytes = glib::Bytes::from(&data);
stream.write_bytes_async_future(&bytes, prio).await.map(|_| ())
}
Err(e) => {
let err = format!("failed to request clipboard data: {}", e);
log::warn!("{}", err);
Err(glib::Error::new(gio::IOErrorEnum::Failed, &err))
}
}
}),
))
});
if let Err(e) = clipboard.set_content(Some(&content)) {
log::warn!("Failed to set clipboard grab: {}", e);
}
}
}
async fn release(&mut self, selection: ClipboardSelection) {
if let Some((clipboard, _)) = clipboard_from_selection(selection) {
// TODO: track if the outside/app changed the clipboard
if let Err(e) = clipboard.set_content(gdk::NONE_CONTENT_PROVIDER) {
log::warn!("Failed to release clipboard: {}", e);
}
}
}
async fn request(
&mut self,
selection: ClipboardSelection,
mimes: Vec<String>,
) -> qemu_display::Result<(String, Vec<u8>)> {
// we have to spawn a local future, because clipboard is not Send
let (sender, receiver) = futures::channel::oneshot::channel();
glib::MainContext::default().spawn_local(async move {
let res = if let Some((clipboard, _)) = clipboard_from_selection(selection) {
let m: Vec<_> = mimes.iter().map(|s| s.as_str()).collect();
let res = clipboard
.read_async_future(&m, glib::Priority::default())
.await;
log::debug!("clipboard-read: {}", res.is_ok());
match res {
Ok((stream, mime)) => {
let out = gio::MemoryOutputStream::new_resizable();
let res = out
.splice_async_future(
&stream,
gio::OutputStreamSpliceFlags::CLOSE_SOURCE
| gio::OutputStreamSpliceFlags::CLOSE_TARGET,
glib::Priority::default(),
)
.await;
match res {
Ok(_) => {
let data = out.steal_as_bytes();
Ok((mime.to_string(), data.as_ref().to_vec()))
}
Err(e) => Err(qemu_display::Error::Failed(format!("{}", e))),
}
}
Err(e) => Err(qemu_display::Error::Failed(format!("{}", e))),
}
} else {
Err(qemu_display::Error::Failed(
"Clipboard request failed".into(),
))
};
sender.send(res).unwrap()
});
match receiver.await {
Ok(res) => res,
Err(e) => Err(qemu_display::Error::Failed(format!(
"Clipboard request failed: {}",
e
))),
}
}
}
impl Handler {
pub async fn new(clipboard: Clipboard) -> Result<Handler, Box<dyn Error>> {
let proxy = clipboard.proxy.clone();
let serials = Arc::new([AtomicU32::new(0), AtomicU32::new(0)]);
let cb_handler = watch_clipboard( let cb_handler = watch_clipboard(
ctxt.proxy.clone(), clipboard.proxy.clone(),
ClipboardSelection::Clipboard, ClipboardSelection::Clipboard,
serials.clone(), serials.clone(),
); );
let cb_primary_handler = watch_clipboard( let cb_primary_handler = watch_clipboard(
ctxt.proxy.clone(), clipboard.proxy.clone(),
ClipboardSelection::Primary, ClipboardSelection::Primary,
serials.clone(), serials.clone(),
); );
clipboard.register(InnerHandler { proxy, serials }).await?;
ctxt.register().await?; Ok(Handler {
Ok(Self { clipboard,
rx,
cb_handler, cb_handler,
cb_primary_handler, cb_primary_handler,
}) })
} }
} }
impl Drop for Handler {
fn drop(&mut self) {
if let Some(id) = self.cb_primary_handler.take() {
clipboard_from_selection(ClipboardSelection::Primary)
.unwrap()
.0
.disconnect(id);
}
if let Some(id) = self.cb_handler.take() {
clipboard_from_selection(ClipboardSelection::Clipboard)
.unwrap()
.0
.disconnect(id);
}
}
}
fn watch_clipboard( fn watch_clipboard(
proxy: AsyncClipboardProxy<'static>, proxy: AsyncClipboardProxy<'static>,
selection: ClipboardSelection, selection: ClipboardSelection,
serials: Rc<[Cell<u32>; 2]>, serials: Arc<[AtomicU32; 2]>,
) -> Option<SignalHandlerId> { ) -> Option<SignalHandlerId> {
let (clipboard, idx) = match clipboard_from_selection(selection) { let (clipboard, idx) = match clipboard_from_selection(selection) {
Some(it) => it, Some(it) => it,
@ -161,9 +211,9 @@ fn watch_clipboard(
let _ = proxy.release(selection).await; let _ = proxy.release(selection).await;
} else { } else {
let mimes: Vec<_> = types.iter().map(|s| s.as_str()).collect(); let mimes: Vec<_> = types.iter().map(|s| s.as_str()).collect();
let ser = serials[idx].get(); let ser = serials[idx].load(Ordering::SeqCst);
let _ = proxy.grab(selection, ser, &mimes).await; let _ = proxy.grab(selection, ser, &mimes).await;
serials[idx].set(ser + 1); serials[idx].store(ser + 1, Ordering::SeqCst);
} }
}); });
} }

View File

@ -1,15 +1,15 @@
use futures_util::StreamExt;
use glib::{clone, subclass::prelude::*, MainContext}; use glib::{clone, subclass::prelude::*, MainContext};
use gtk::{glib, prelude::*}; use gtk::glib;
use once_cell::sync::OnceCell;
use keycodemap::KEYMAP_XORGEVDEV2QNUM; use keycodemap::KEYMAP_XORGEVDEV2QNUM;
use qemu_display::Console; use once_cell::sync::OnceCell;
use rdw::DisplayExt; use qemu_display::{Console, ConsoleListenerHandler};
use rdw::{gtk, DisplayExt};
use std::os::unix::io::IntoRawFd;
mod imp { mod imp {
use super::*; use super::*;
use gtk::subclass::prelude::*; use gtk::subclass::prelude::*;
use std::{convert::TryInto, os::unix::io::IntoRawFd};
#[repr(C)] #[repr(C)]
pub struct RdwDisplayQemuClass { pub struct RdwDisplayQemuClass {
@ -136,22 +136,17 @@ mod imp {
MainContext::default().spawn_local(clone!(@weak widget => async move { MainContext::default().spawn_local(clone!(@weak widget => async move {
let self_ = Self::from_instance(&widget); let self_ = Self::from_instance(&widget);
let console = self_.console.get().unwrap(); let console = self_.console.get().unwrap();
// we have to use a channel, because widget is not Send..
let (rx, wait_tx) = console let (sender, mut receiver) = futures::channel::mpsc::unbounded();
.glib_listen() console.register_listener(ConsoleHandler { sender }).await.unwrap();
.await MainContext::default().spawn_local(clone!(@weak widget => async move {
.expect("Failed to listen to the console"); while let Some(e) = receiver.next().await {
rx.attach( use ConsoleEvent::*;
None, match e {
clone!(@weak widget => @default-panic, move |evt| {
use qemu_display::ConsoleEvent::*;
log::debug!("Console event: {:?}", evt);
match evt {
Scanout(s) => { Scanout(s) => {
if s.format != 0x20020888 { if s.format != 0x20020888 {
log::warn!("Format not yet supported: {:X}", s.format); log::warn!("Format not yet supported: {:X}", s.format);
return Continue(true); continue;
} }
widget.set_display_size(Some((s.width as _, s.height as _))); widget.set_display_size(Some((s.width as _, s.height as _)));
widget.update_area(0, 0, s.width as _, s.height as _, s.stride as _, &s.data); widget.update_area(0, 0, s.width as _, s.height as _, s.stride as _, &s.data);
@ -159,7 +154,7 @@ mod imp {
Update(u) => { Update(u) => {
if u.format != 0x20020888 { if u.format != 0x20020888 {
log::warn!("Format not yet supported: {:X}", u.format); log::warn!("Format not yet supported: {:X}", u.format);
return Continue(true); continue;
} }
widget.update_area(u.x as _, u.y as _, u.w as _, u.h as _, u.stride as _, &u.data); widget.update_area(u.x as _, u.y as _, u.w as _, u.h as _, u.stride as _, &u.data);
} }
@ -175,19 +170,20 @@ mod imp {
fd: s.into_raw_fd(), fd: s.into_raw_fd(),
}); });
} }
UpdateDMABUF { .. } => { UpdateDMABUF { wait_tx, .. } => {
widget.render(); widget.render();
let _ = wait_tx.send(()); let _ = wait_tx.send(());
} }
Disconnected => { Disconnected => {
log::warn!("Console disconnected");
} }
CursorDefine { width, height, hot_x, hot_y, data }=> { CursorDefine(c) => {
let cursor = rdw::Display::make_cursor( let cursor = rdw::Display::make_cursor(
&data, &c.data,
width, c.width,
height, c.height,
hot_x, c.hot_x,
hot_y, c.hot_y,
1, 1,
); );
widget.define_cursor(Some(cursor)); widget.define_cursor(Some(cursor));
@ -200,41 +196,21 @@ mod imp {
} }
} }
} }
Continue(true)
})
);
let mut abs_changed = console.mouse.receive_is_absolute_changed().await;
MainContext::default().spawn_local(clone!(@weak widget => async move {
use futures_util::StreamExt;
while let Some(abs) = abs_changed.next().await {
let abs = if let Some(abs) = abs {
abs.try_into().unwrap_or(false)
} else {
continue;
};
widget.set_mouse_absolute(abs);
} }
})); }));
let mut abs_changed = console.mouse.receive_is_absolute_changed().await;
loop { MainContext::default().spawn_local(clone!(@weak widget => async move {
if let Err(e) = console.dispatch_signals().await { while let Some(abs) = abs_changed.next().await {
log::warn!("Console dispatching error: {}", e); if let Some(abs) = abs {
break; widget.set_mouse_absolute(abs);
}
} }
} }));
})); }));
} }
} }
impl rdw::DisplayImpl for Display {} impl rdw::DisplayImpl for Display {}
impl Display {
pub(crate) fn set_console(&self, console: Console) {
self.console.set(console).unwrap();
}
}
} }
glib::wrapper! { glib::wrapper! {
@ -245,7 +221,7 @@ impl Display {
pub fn new(console: Console) -> Self { pub fn new(console: Console) -> Self {
let obj = glib::Object::new::<Self>(&[]).unwrap(); let obj = glib::Object::new::<Self>(&[]).unwrap();
let self_ = imp::Display::from_instance(&obj); let self_ = imp::Display::from_instance(&obj);
self_.set_console(console); self_.console.set(console).unwrap();
obj obj
} }
@ -255,6 +231,67 @@ impl Display {
} }
} }
#[derive(Debug)]
enum ConsoleEvent {
Scanout(qemu_display::Scanout),
Update(qemu_display::Update),
ScanoutDMABUF(qemu_display::ScanoutDMABUF),
UpdateDMABUF {
_update: qemu_display::UpdateDMABUF,
wait_tx: futures::channel::oneshot::Sender<()>,
},
MouseSet(qemu_display::MouseSet),
CursorDefine(qemu_display::Cursor),
Disconnected,
}
struct ConsoleHandler {
sender: futures::channel::mpsc::UnboundedSender<ConsoleEvent>,
}
impl ConsoleHandler {
fn send(&self, event: ConsoleEvent) {
if let Err(e) = self.sender.unbounded_send(event) {
log::warn!("failed to send console event: {}", e);
}
}
}
#[async_trait::async_trait]
impl ConsoleListenerHandler for ConsoleHandler {
async fn scanout(&mut self, scanout: qemu_display::Scanout) {
self.send(ConsoleEvent::Scanout(scanout));
}
async fn update(&mut self, update: qemu_display::Update) {
self.send(ConsoleEvent::Update(update));
}
async fn scanout_dmabuf(&mut self, scanout: qemu_display::ScanoutDMABUF) {
self.send(ConsoleEvent::ScanoutDMABUF(scanout));
}
async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) {
let (wait_tx, wait_rx) = futures::channel::oneshot::channel();
self.send(ConsoleEvent::UpdateDMABUF { _update, wait_tx });
if let Err(e) = wait_rx.await {
log::warn!("wait update dmabuf failed: {}", e);
}
}
async fn mouse_set(&mut self, set: qemu_display::MouseSet) {
self.send(ConsoleEvent::MouseSet(set));
}
async fn cursor_define(&mut self, cursor: qemu_display::Cursor) {
self.send(ConsoleEvent::CursorDefine(cursor));
}
fn disconnected(&mut self) {
self.send(ConsoleEvent::Disconnected);
}
}
fn from_gdk_button(button: u32) -> qemu_display::MouseButton { fn from_gdk_button(button: u32) -> qemu_display::MouseButton {
use qemu_display::MouseButton::*; use qemu_display::MouseButton::*;

View File

@ -2,9 +2,8 @@ use gio::ApplicationFlags;
use glib::MainContext; use glib::MainContext;
use gtk::{gio, glib, prelude::*}; use gtk::{gio, glib, prelude::*};
use qemu_display::{Chardev, Console, Display}; use qemu_display::{Chardev, Console, Display};
use std::cell::RefCell; use rdw::gtk;
use std::sync::Arc; use std::{cell::RefCell, sync::Arc};
use zbus::Connection;
mod audio; mod audio;
mod clipboard; mod clipboard;
@ -13,7 +12,6 @@ mod usbredir;
struct Inner { struct Inner {
app: gtk::Application, app: gtk::Application,
conn: zbus::azync::Connection,
usbredir: RefCell<Option<usbredir::Handler>>, usbredir: RefCell<Option<usbredir::Handler>>,
audio: RefCell<Option<audio::Handler>>, audio: RefCell<Option<audio::Handler>>,
clipboard: RefCell<Option<clipboard::Handler>>, clipboard: RefCell<Option<clipboard::Handler>>,
@ -69,20 +67,15 @@ impl App {
if opt.lookup_value("list", None).is_some() { if opt.lookup_value("list", None).is_some() {
app_opt.list = true; app_opt.list = true;
} }
app_opt.vm_name = app_opt.vm_name = opt
opt.lookup_value(&glib::OPTION_REMAINING, None) .lookup_value(&glib::OPTION_REMAINING, None)
.and_then(|args| args.child_value(0).get::<String>()); .and_then(|args| args.child_value(0).get::<String>());
-1 -1
}); });
let conn = Connection::session()
.expect("Failed to connect to DBus")
.into();
let app = App { let app = App {
inner: Arc::new(Inner { inner: Arc::new(Inner {
app, app,
conn,
usbredir: Default::default(), usbredir: Default::default(),
audio: Default::default(), audio: Default::default(),
clipboard: Default::default(), clipboard: Default::default(),
@ -90,7 +83,6 @@ impl App {
}; };
let app_clone = app.clone(); let app_clone = app.clone();
let opt_clone = opt.clone();
app.inner.app.connect_activate(move |app| { app.inner.app.connect_activate(move |app| {
let ui_src = include_str!("main.ui"); let ui_src = include_str!("main.ui");
let builder = gtk::Builder::new(); let builder = gtk::Builder::new();
@ -102,11 +94,24 @@ impl App {
window.set_application(Some(app)); window.set_application(Some(app));
let app_clone = app_clone.clone(); let app_clone = app_clone.clone();
let opt_clone = opt_clone.clone(); let opt_clone = opt.clone();
MainContext::default().spawn_local(async move { MainContext::default().spawn_local(async move {
// let opt = opt_clone.borrow(); let conn = zbus::ConnectionBuilder::session()
.unwrap()
.internal_executor(false)
.build()
.await
.expect("Failed to connect to DBus");
let conn_clone = conn.clone();
MainContext::default().spawn_local(async move {
loop {
conn_clone.executor().tick().await;
}
});
if opt_clone.borrow().list { if opt_clone.borrow().list {
let list = Display::by_name(app_clone.connection()).await.unwrap(); let list = Display::by_name(&conn).await.unwrap();
for (name, dest) in list { for (name, dest) in list {
println!("{} (at {})", name, dest); println!("{} (at {})", name, dest);
} }
@ -114,20 +119,18 @@ impl App {
return; return;
} }
let dest = if let Some(name) = opt_clone.borrow().vm_name.as_ref() { let dest = if let Some(name) = opt_clone.borrow().vm_name.as_ref() {
let list = Display::by_name(app_clone.connection()).await.unwrap(); let list = Display::by_name(&conn).await.unwrap();
Some( Some(
list.get(name) list.get(name)
.expect(&format!("Can't find VM name: {}", name)) .unwrap_or_else(|| panic!("Can't find VM name: {}", name))
.clone(), .clone(),
) )
} else { } else {
None None
}; };
let display = Display::new(app_clone.connection(), dest.as_ref()) let display = Display::new(&conn, dest.as_ref()).await.unwrap();
.await
.unwrap();
let console = Console::new(app_clone.connection(), 0) let console = Console::new(&conn, 0)
.await .await
.expect("Failed to get the QEMU console"); .expect("Failed to get the QEMU console");
let rdw = display::Display::new(console); let rdw = display::Display::new(console);
@ -143,22 +146,26 @@ impl App {
if let Ok(Some(audio)) = display.audio().await { if let Ok(Some(audio)) = display.audio().await {
match audio::Handler::new(audio).await { match audio::Handler::new(audio).await {
Ok(handler) => app_clone.set_audio(handler), Ok(handler) => app_clone.set_audio(handler),
Err(e) => log::warn!("Failed to setup audio: {}", e), Err(e) => {
log::warn!("Failed to setup audio handler: {}", e);
}
} }
} }
if let Ok(Some(clipboard)) = display.clipboard().await { if let Ok(Some(clipboard)) = display.clipboard().await {
match clipboard::Handler::new(clipboard).await { match clipboard::Handler::new(clipboard).await {
Ok(handler) => app_clone.set_clipboard(handler), Ok(handler) => app_clone.set_clipboard(handler),
Err(e) => log::warn!("Failed to setup clipboard: {}", e), Err(e) => {
log::warn!("Failed to setup clipboard handler: {}", e);
}
} }
} }
if let Ok(c) = Chardev::new(app_clone.connection(), "qmp").await { if let Ok(c) = Chardev::new(&conn, "qmp").await {
use std::io::prelude::*; use std::{
use std::io::BufReader; io::{prelude::*, BufReader},
use std::os::unix::io::AsRawFd; os::unix::{io::AsRawFd, net::UnixStream},
use std::os::unix::net::UnixStream; };
let (p0, p1) = UnixStream::pair().unwrap(); let (p0, p1) = UnixStream::pair().unwrap();
if c.proxy.register(p1.as_raw_fd().into()).await.is_ok() { if c.proxy.register(p1.as_raw_fd().into()).await.is_ok() {
@ -192,10 +199,6 @@ impl App {
app app
} }
fn connection(&self) -> &zbus::azync::Connection {
&self.inner.conn
}
fn set_usbredir(&self, usbredir: usbredir::Handler) { fn set_usbredir(&self, usbredir: usbredir::Handler) {
self.inner.usbredir.replace(Some(usbredir)); self.inner.usbredir.replace(Some(usbredir));
} }
@ -204,8 +207,8 @@ impl App {
self.inner.audio.replace(Some(audio)); self.inner.audio.replace(Some(audio));
} }
fn set_clipboard(&self, clipboard: clipboard::Handler) { fn set_clipboard(&self, cb: clipboard::Handler) {
self.inner.clipboard.replace(Some(clipboard)); self.inner.clipboard.replace(Some(cb));
} }
fn run(&self) -> i32 { fn run(&self) -> i32 {

View File

@ -1,6 +1,7 @@
use glib::{clone, MainContext}; use glib::{clone, MainContext};
use gtk::{glib, prelude::*}; use gtk::{glib, prelude::*};
use qemu_display::UsbRedir; use qemu_display::UsbRedir;
use rdw::gtk;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Handler { pub struct Handler {
@ -30,7 +31,7 @@ impl Handler {
let usbredir = self.usbredir.clone(); let usbredir = self.usbredir.clone();
widget.connect_device_state_set(move |widget, item, state| { widget.connect_device_state_set(move |widget, item, state| {
let device = match item.device() { let device = match item.device() {
Some(it) => it.clone(), Some(it) => it,
_ => return, _ => return,
}; };

View File

@ -16,3 +16,4 @@ libc = "0.2.86"
image = "0.23.14" image = "0.23.14"
derivative = "2.2.0" derivative = "2.2.0"
async-io = "1.3.1" async-io = "1.3.1"
async-trait = "0.1.48"

View File

@ -1,20 +1,22 @@
use std::iter::FromIterator; use std::{
use std::net::{TcpListener, TcpStream}; borrow::Borrow,
use std::sync::mpsc; collections::HashSet,
use std::sync::{Arc, Mutex}; error::Error,
use std::{collections::HashSet, convert::TryInto}; io,
use std::{error::Error, thread::JoinHandle}; iter::FromIterator,
use std::{io, thread, time}; net::{TcpListener, TcpStream},
sync::{mpsc, Arc, Mutex},
thread, time,
};
use clap::Clap; use clap::Clap;
use image::GenericImage; use image::GenericImage;
use keycodemap::*; use keycodemap::*;
use qemu_display::{Console, ConsoleEvent, MouseButton, VMProxy}; use qemu_display::{AsyncVMProxy, Console, ConsoleListenerHandler, MouseButton};
use vnc::{ use vnc::{
server::Event as VncEvent, server::FramebufferUpdate, Encoding, Error as VncError, PixelFormat, server::{Event as VncEvent, FramebufferUpdate},
Rect, Screen, Server as VncServer, Encoding, Error as VncError, PixelFormat, Rect, Screen, Server as VncServer,
}; };
use zbus::Connection;
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
pub struct SocketAddrArgs { pub struct SocketAddrArgs {
@ -233,10 +235,59 @@ impl Client {
} }
} }
#[derive(Debug)]
struct ConsoleListener {
server: Server,
}
#[async_trait::async_trait]
impl ConsoleListenerHandler for ConsoleListener {
async fn scanout(&mut self, s: qemu_display::Scanout) {
let mut inner = self.server.inner.lock().unwrap();
inner.image = image_from_vec(s.format, s.width, s.height, s.stride, s.data);
}
async fn update(&mut self, u: qemu_display::Update) {
let mut inner = self.server.inner.lock().unwrap();
let update = image_from_vec(u.format, u.w as _, u.h as _, u.stride, u.data);
if (u.x, u.y) == (0, 0) && update.dimensions() == inner.image.dimensions() {
inner.image = update;
} else {
inner.image.copy_from(&update, u.x as _, u.y as _).unwrap();
}
let rect = Rect {
left: u.x as _,
top: u.y as _,
width: u.w as _,
height: u.h as _,
};
inner.tx.send(Event::ConsoleUpdate(rect)).unwrap();
}
async fn scanout_dmabuf(&mut self, _scanout: qemu_display::ScanoutDMABUF) {
unimplemented!()
}
async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) {
unimplemented!()
}
async fn mouse_set(&mut self, set: qemu_display::MouseSet) {
dbg!(set);
}
async fn cursor_define(&mut self, cursor: qemu_display::Cursor) {
dbg!(cursor);
}
fn disconnected(&mut self) {
dbg!();
}
}
#[derive(Debug)] #[derive(Debug)]
struct ServerInner { struct ServerInner {
console: Console, console: Console,
console_thread: Option<JoinHandle<()>>,
image: BgraImage, image: BgraImage,
tx: mpsc::Sender<Event>, tx: mpsc::Sender<Event>,
} }
@ -257,76 +308,24 @@ impl Server {
Ok(Self { Ok(Self {
vm_name, vm_name,
rx: Arc::new(Mutex::new(rx)), rx: Arc::new(Mutex::new(rx)),
inner: Arc::new(Mutex::new(ServerInner { inner: Arc::new(Mutex::new(ServerInner { console, image, tx })),
console,
console_thread: None,
image,
tx,
})),
}) })
} }
fn stop_console(&self) -> Result<(), Box<dyn Error>> { fn stop_console(&self) -> Result<(), Box<dyn Error>> {
let mut inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock().unwrap();
if let Some(_thread) = inner.console_thread.take() { inner.console.unregister_listener();
todo!("join console thread");
//thread.join().unwrap();
}
Ok(()) Ok(())
} }
async fn run_console(&self) -> Result<(), Box<dyn Error>> { async fn run_console(&self) -> Result<(), Box<dyn Error>> {
let mut inner = self.inner.lock().unwrap(); let mut inner = self.inner.lock().unwrap();
if inner.console_thread.is_some() { inner
return Ok(()); .console
} .register_listener(ConsoleListener {
server: self.clone(),
let server = self.clone(); })
let (console_rx, _ack) = inner.console.listen().await?; .await?;
let thread = thread::spawn(move || loop {
match console_rx.recv().unwrap() {
ConsoleEvent::ScanoutDMABUF(_) | ConsoleEvent::UpdateDMABUF { .. } => {
unimplemented!();
}
ConsoleEvent::Scanout(s) => {
let mut inner = server.inner.lock().unwrap();
inner.image = image_from_vec(s.format, s.width, s.height, s.stride, s.data);
}
ConsoleEvent::Update(u) => {
let mut inner = server.inner.lock().unwrap();
let update = image_from_vec(
u.format,
u.w.try_into().unwrap(),
u.h.try_into().unwrap(),
u.stride,
u.data,
);
if (u.x, u.y) == (0, 0) && update.dimensions() == inner.image.dimensions() {
inner.image = update;
} else {
inner
.image
.copy_from(&update, u.x.try_into().unwrap(), u.y.try_into().unwrap())
.unwrap();
}
let rect = Rect {
left: u.x.try_into().unwrap(),
top: u.y.try_into().unwrap(),
width: u.w.try_into().unwrap(),
height: u.h.try_into().unwrap(),
};
inner.tx.send(Event::ConsoleUpdate(rect)).unwrap();
}
ConsoleEvent::CursorDefine { .. } => {}
ConsoleEvent::MouseSet(_) => {}
e => {
dbg!(e);
}
}
});
inner.console_thread = Some(thread);
Ok(()) Ok(())
} }
@ -471,13 +470,15 @@ async fn run() -> Result<(), Box<dyn Error>> {
let listener = TcpListener::bind::<std::net::SocketAddr>(args.address.into()).unwrap(); let listener = TcpListener::bind::<std::net::SocketAddr>(args.address.into()).unwrap();
let dbus = if let Some(addr) = args.dbus_address { let dbus = if let Some(addr) = args.dbus_address {
Connection::new_for_address(&addr, true) zbus::ConnectionBuilder::address(addr.borrow())?
.build()
.await
} else { } else {
Connection::new_session() zbus::Connection::session().await
} }
.expect("Failed to connect to DBus"); .expect("Failed to connect to DBus");
let vm_name = VMProxy::new(&dbus)?.name()?; let vm_name = AsyncVMProxy::new(&dbus).await?.name().await?;
let console = Console::new(&dbus.into(), 0) let console = Console::new(&dbus.into(), 0)
.await .await

View File

@ -8,9 +8,9 @@ log = "0.4"
pretty_env_logger = "0.4" pretty_env_logger = "0.4"
once_cell = "1.5" once_cell = "1.5"
zbus = { version = "2.0.0-beta" } zbus = { version = "2.0.0-beta" }
qemu-display = { path = "../qemu-display", features = ["glib"] } qemu-display = { path = "../qemu-display" }
futures = "0.3.13" futures = "0.3.13"
[dependencies.vte] [dependencies.vte]
package = "vte4" package = "vte4"
git = "https://gitlab.gnome.org/malureau/vte4-rs" version = "0.0.1"

View File

@ -1,11 +1,10 @@
use futures::prelude::*; use futures::prelude::*;
use glib::{clone, MainContext}; use glib::{clone, MainContext};
use gtk::{gio, glib};
use qemu_display::Chardev; use qemu_display::Chardev;
use std::os::unix::io::AsRawFd; use std::os::unix::{io::AsRawFd, net::UnixStream};
use std::os::unix::net::UnixStream; use vte::{gtk, prelude::*};
use vte::prelude::*; use zbus::Connection;
use vte::{gio, glib, gtk};
use zbus::azync::Connection;
fn main() { fn main() {
pretty_env_logger::init(); pretty_env_logger::init();