qemu-display: start win32 support

This commit is contained in:
Marc-André Lureau 2022-08-26 21:29:57 +04:00
parent ecbeb857c5
commit 1a24e051d0
13 changed files with 256 additions and 52 deletions

View File

@ -7,6 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cfg-if = "1.0"
log = "0.4"
derivative = "2.2.0"
zbus = { version = "2.0", features = ["xml"] }
@ -23,3 +24,7 @@ usbredirhost = "0.0.1"
async-broadcast = "0.3.3"
async-trait = "0.1.48"
async-lock = "2.3.0"
[target.'cfg(windows)'.dependencies]
uds_windows = "1.0.1"
windows = { version = "0.39.0", features = ["Win32_Networking_WinSock", "Win32_Foundation", "Win32_System_IO", "Win32_System_Threading"] }

View File

@ -1,6 +1,14 @@
use std::os::unix::{io::AsRawFd, net::UnixStream};
use zbus::{dbus_interface, dbus_proxy, zvariant::Fd, Connection};
#[cfg(windows)]
use crate::win32::Fd;
#[cfg(unix)]
use std::os::unix::net::UnixStream;
#[cfg(windows)]
use uds_windows::UnixStream;
#[cfg(unix)]
use zbus::zvariant::Fd;
use zbus::{dbus_interface, dbus_proxy, Connection};
use crate::util;
use crate::Result;
#[derive(Debug)]
@ -236,9 +244,8 @@ impl Audio {
pub async fn register_out_listener<H: AudioOutHandler>(&mut self, handler: H) -> Result<()> {
let (p0, p1) = UnixStream::pair()?;
self.proxy
.register_out_listener(p0.as_raw_fd().into())
.await?;
let p0 = util::prepare_uds_pass(&p0)?;
self.proxy.register_out_listener(p0).await?;
let c = zbus::ConnectionBuilder::unix_stream(p1)
.p2p()
.serve_at(
@ -253,9 +260,8 @@ impl Audio {
pub async fn register_in_listener<H: AudioInHandler>(&mut self, handler: H) -> Result<()> {
let (p0, p1) = UnixStream::pair()?;
self.proxy
.register_in_listener(p0.as_raw_fd().into())
.await?;
let p0 = util::prepare_uds_pass(&p0)?;
self.proxy.register_in_listener(p0).await?;
let c = zbus::ConnectionBuilder::unix_stream(p1)
.p2p()
.serve_at(

View File

@ -1,14 +1,16 @@
#[cfg(windows)]
use crate::win32::Fd;
use std::convert::TryFrom;
use zbus::{
dbus_proxy,
zvariant::{Fd, ObjectPath},
};
#[cfg(unix)]
use zbus::zvariant::Fd;
use zbus::{dbus_proxy, zvariant::ObjectPath};
use crate::Result;
#[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Chardev")]
pub trait Chardev {
/// Register method
#[cfg(unix)]
fn register(&self, stream: Fd) -> zbus::Result<()>;
/// SendBreak method

View File

@ -1,15 +1,15 @@
use std::{
cell::RefCell,
convert::TryFrom,
os::unix::{io::AsRawFd, net::UnixStream},
};
use zbus::{
dbus_proxy,
zvariant::{Fd, ObjectPath},
Connection,
};
#[cfg(windows)]
use crate::win32::Fd;
#[cfg(unix)]
use std::os::unix::net::UnixStream;
use std::{cell::RefCell, convert::TryFrom};
#[cfg(windows)]
use uds_windows::UnixStream;
#[cfg(unix)]
use zbus::zvariant::Fd;
use zbus::{dbus_proxy, zvariant::ObjectPath, Connection};
use crate::{ConsoleListener, ConsoleListenerHandler, KeyboardProxy, MouseProxy, Result};
use crate::{util, ConsoleListener, ConsoleListenerHandler, KeyboardProxy, MouseProxy, Result};
#[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Console")]
pub trait Console {
@ -87,7 +87,8 @@ impl Console {
pub async fn register_listener<H: ConsoleListenerHandler>(&self, handler: H) -> Result<()> {
let (p0, p1) = UnixStream::pair()?;
self.proxy.register_listener(p0.as_raw_fd().into()).await?;
let p0 = util::prepare_uds_pass(&p0)?;
self.proxy.register_listener(p0).await?;
let c = zbus::ConnectionBuilder::unix_stream(p1)
.p2p()
.serve_at("/org/qemu/Display1/Listener", ConsoleListener::new(handler))?

View File

@ -1,9 +1,12 @@
#[cfg(windows)]
use crate::win32::Fd;
use derivative::Derivative;
use std::{
ops::Drop,
os::unix::io::{AsRawFd, IntoRawFd, RawFd},
};
use zbus::{dbus_interface, zvariant::Fd};
use std::ops::Drop;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use zbus::dbus_interface;
#[cfg(unix)]
use zbus::zvariant::Fd;
#[derive(Derivative)]
#[derivative(Debug)]
@ -29,6 +32,7 @@ pub struct Update {
pub data: Vec<u8>,
}
#[cfg(unix)]
#[derive(Debug)]
pub struct ScanoutDMABUF {
pub fd: RawFd,
@ -40,6 +44,10 @@ pub struct ScanoutDMABUF {
pub y0_top: bool,
}
#[cfg(windows)]
#[derive(Debug)]
pub struct ScanoutDMABUF {}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Cursor {
@ -51,6 +59,7 @@ pub struct Cursor {
pub data: Vec<u8>,
}
#[cfg(unix)]
impl Drop for ScanoutDMABUF {
fn drop(&mut self) {
if self.fd >= 0 {
@ -61,6 +70,7 @@ impl Drop for ScanoutDMABUF {
}
}
#[cfg(unix)]
impl IntoRawFd for ScanoutDMABUF {
fn into_raw_fd(mut self) -> RawFd {
std::mem::replace(&mut self.fd, -1)
@ -148,6 +158,22 @@ impl<H: ConsoleListenerHandler> ConsoleListener<H> {
.await;
}
#[cfg(not(unix))]
#[dbus_interface(name = "ScanoutDMABUF")]
async fn scanout_dmabuf(
&mut self,
_fd: Fd,
_width: u32,
_height: u32,
_stride: u32,
_fourcc: u32,
_modifier: u64,
_y0_top: bool,
) {
unimplemented!()
}
#[cfg(unix)]
#[dbus_interface(name = "ScanoutDMABUF")]
async fn scanout_dmabuf(
&mut self,

View File

@ -12,7 +12,9 @@ use zbus::{
};
use zvariant::OwnedObjectPath;
use crate::{Audio, Chardev, Clipboard, Error, Result, UsbRedir, VMProxy};
#[cfg(unix)]
use crate::UsbRedir;
use crate::{Audio, Chardev, Clipboard, Error, Result, VMProxy};
struct Inner<'d> {
proxy: fdo::ObjectManagerProxy<'d>,
@ -144,6 +146,7 @@ impl<'d> Display<'d> {
.await
}
#[cfg(unix)]
pub async fn usbredir(&self) -> UsbRedir {
let chardevs = stream::iter(self.chardevs().await)
.filter_map(|c| async move {

View File

@ -1,5 +1,9 @@
#![allow(clippy::too_many_arguments)]
pub mod util;
#[cfg(windows)]
mod win32;
mod error;
pub use error::*;
@ -30,7 +34,9 @@ pub use mouse::*;
mod display;
pub use display::*;
#[cfg(unix)]
mod usbredir;
#[cfg(unix)]
pub use usbredir::UsbRedir;
#[cfg(test)]

View File

@ -1,19 +1,22 @@
use async_broadcast::{broadcast, Receiver, Sender};
use async_lock::RwLock;
use futures::Stream;
#[cfg(unix)]
use std::os::unix::{
io::{AsRawFd, RawFd},
net::UnixStream,
};
use std::{
collections::HashMap,
default::Default,
io::{Read, Write},
os::unix::{
io::{AsRawFd, RawFd},
net::UnixStream,
},
pin::Pin,
sync::{Arc, Mutex},
task::{Context, Poll},
thread::JoinHandle,
};
#[cfg(windows)]
use uds_windows::UnixStream;
use usbredirhost::{
rusb::{self, UsbContext},
Device, DeviceHandler, LogLevel,
@ -24,6 +27,7 @@ use crate::{Chardev, Error, Result};
#[derive(Debug)]
struct InnerHandler {
#[allow(unused)] // keep the device opened, as rusb doesn't take it
#[cfg(unix)]
device_fd: Option<zvariant::OwnedFd>,
stream: UnixStream,
ctxt: rusb::Context,
@ -73,6 +77,7 @@ impl DeviceHandler for Handler {
fn flush_writes(&mut self) {}
}
#[cfg(unix)]
#[zbus::dbus_proxy(
interface = "org.freedesktop.usbredir1",
default_service = "org.freedesktop.usbredir1",
@ -88,6 +93,7 @@ impl Handler {
let (dev, device_fd) = match device.open() {
Ok(it) => (it, None),
#[cfg(unix)]
Err(rusb::Error::Access) => {
let (bus, dev) = (device.bus_number(), device.address());
let sysbus = zbus::Connection::system().await?;
@ -120,6 +126,7 @@ impl Handler {
let handler = Self {
inner: Arc::new(Mutex::new(InnerHandler {
#[cfg(unix)]
device_fd,
stream,
event,
@ -302,6 +309,7 @@ impl Stream for NFreeChannelsStream {
}
}
#[cfg(unix)]
fn fd_poll_readable(fd: RawFd, wait: Option<RawFd>) -> std::io::Result<bool> {
let mut fds = vec![libc::pollfd {
fd,

45
qemu-display/src/util.rs Normal file
View File

@ -0,0 +1,45 @@
use crate::Result;
#[cfg(unix)]
use std::os::unix::{io::AsRawFd, net::UnixStream};
#[cfg(unix)]
use zbus::zvariant::Fd;
#[cfg(windows)]
use win32::Fd;
#[cfg(windows)]
use crate::win32;
#[cfg(windows)]
use std::os::windows::io::AsRawSocket;
#[cfg(windows)]
use uds_windows::UnixStream;
#[cfg(windows)]
use windows::Win32::Networking::WinSock::{WSADuplicateSocketW, SOCKET, WSAPROTOCOL_INFOW};
#[cfg(windows)]
use windows::Win32::System::Threading::PROCESS_DUP_HANDLE;
pub fn prepare_uds_pass(us: &UnixStream) -> Result<Fd> {
#[cfg(unix)]
{
Ok(us.as_raw_fd().into())
}
#[cfg(windows)]
{
let pid = win32::unix_stream_get_peer_pid(us)?;
let p = win32::ProcessHandle::open(Some(pid), PROCESS_DUP_HANDLE)?;
let mut info = unsafe { std::mem::zeroed() };
if unsafe { WSADuplicateSocketW(SOCKET(us.as_raw_socket() as _), p.process_id(), &mut info) }
!= 0
{
return Err(crate::Error::Io(win32::wsa_last_err()));
}
let info = unsafe {
std::slice::from_raw_parts(
&info as *const _ as *const u8,
std::mem::size_of::<WSAPROTOCOL_INFOW>(),
)
};
Ok(info.to_vec())
}
}

85
qemu-display/src/win32.rs Normal file
View File

@ -0,0 +1,85 @@
use std::io;
use uds_windows::UnixStream;
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::System::Threading::PROCESS_ACCESS_RIGHTS;
pub type Fd = Vec<u8>;
// A process handle
pub struct ProcessHandle(HANDLE);
impl Drop for ProcessHandle {
fn drop(&mut self) {
unsafe { CloseHandle(self.0) };
}
}
impl ProcessHandle {
// Open the process associated with the process_id (if None, the current process)
pub fn open(
process_id: Option<u32>,
desired_access: PROCESS_ACCESS_RIGHTS,
) -> Result<Self, io::Error> {
use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcess};
let process = if let Some(process_id) = process_id {
unsafe { OpenProcess(desired_access, false, process_id)? }
} else {
unsafe { GetCurrentProcess() }
};
Ok(Self(process))
}
pub fn process_id(&self) -> u32 {
use windows::Win32::System::Threading::GetProcessId;
unsafe { GetProcessId(self.0) }
}
}
pub(crate) fn wsa_last_err() -> io::Error {
use windows::Win32::Networking::WinSock::WSAGetLastError;
let err = unsafe { WSAGetLastError() };
io::Error::from_raw_os_error(err.0)
}
// Get the process ID of the connected peer
pub fn unix_stream_get_peer_pid(stream: &UnixStream) -> Result<u32, io::Error> {
use std::os::windows::io::AsRawSocket;
use windows::Win32::Networking::WinSock::{
WSAIoctl, IOC_OUT, IOC_VENDOR, SOCKET, SOCKET_ERROR,
};
macro_rules! _WSAIOR {
($x:expr, $y:expr) => {
IOC_OUT | $x | $y
};
}
let socket = stream.as_raw_socket();
const SIO_AF_UNIX_GETPEERPID: u32 = _WSAIOR!(IOC_VENDOR, 256);
let mut ret = 0 as u32;
let mut bytes = 0;
let r = unsafe {
WSAIoctl(
SOCKET(socket as _),
SIO_AF_UNIX_GETPEERPID,
0 as *mut _,
0,
&mut ret as *mut _ as *mut _,
std::mem::size_of_val(&ret) as u32,
&mut bytes,
0 as *mut _,
None,
)
};
if r == SOCKET_ERROR {
return Err(wsa_last_err());
}
Ok(ret)
}

View File

@ -17,3 +17,4 @@ rdw = { package = "rdw4", version = "0.1", features = ["bindings"] }
futures-util = "0.3"
futures = "0.3"
async-trait = "0.1"
uds_windows = "1.0.2"

View File

@ -5,6 +5,7 @@ use keycodemap::KEYMAP_XORGEVDEV2QNUM;
use once_cell::sync::OnceCell;
use qemu_display::{Console, ConsoleListenerHandler};
use rdw::{gtk, DisplayExt};
#[cfg(unix)]
use std::os::unix::io::IntoRawFd;
mod imp {
@ -158,6 +159,11 @@ mod imp {
}
widget.update_area(u.x as _, u.y as _, u.w as _, u.h as _, u.stride as _, &u.data);
}
#[cfg(windows)]
ScanoutDMABUF(_) => {
unimplemented!()
}
#[cfg(unix)]
ScanoutDMABUF(s) => {
widget.set_display_size(Some((s.width as _, s.height as _)));
widget.set_dmabuf_scanout(rdw::RdwDmabufScanout {

View File

@ -2,17 +2,19 @@ use futures_util::StreamExt;
use gio::ApplicationFlags;
use glib::MainContext;
use gtk::{gio, glib, prelude::*};
use qemu_display::{Chardev, Console, Display};
use qemu_display::{util, Chardev, Console, Display};
use rdw::gtk;
use std::{cell::RefCell, sync::Arc};
mod audio;
mod clipboard;
mod display;
#[cfg(unix)]
mod usbredir;
struct Inner {
app: gtk::Application,
#[cfg(unix)]
usbredir: RefCell<Option<usbredir::Handler>>,
audio: RefCell<Option<audio::Handler>>,
clipboard: RefCell<Option<clipboard::Handler>>,
@ -101,6 +103,7 @@ impl App {
let app = App {
inner: Arc::new(Inner {
app,
#[cfg(unix)]
usbredir: Default::default(),
audio: Default::default(),
clipboard: Default::default(),
@ -176,6 +179,7 @@ impl App {
.unwrap()
.set_child(Some(&rdw));
#[cfg(unix)]
app_clone.set_usbredir(usbredir::Handler::new(display.usbredir().await));
if let Ok(Some(audio)) = display.audio().await {
@ -197,13 +201,15 @@ impl App {
}
if let Ok(c) = Chardev::new(&conn, "qmp").await {
use std::{
io::{prelude::*, BufReader},
os::unix::{io::AsRawFd, net::UnixStream},
};
use std::io::{prelude::*, BufReader};
#[cfg(unix)]
use std::os::unix::net::UnixStream;
#[cfg(windows)]
use uds_windows::UnixStream;
let (p0, p1) = UnixStream::pair().unwrap();
if c.proxy.register(p1.as_raw_fd().into()).await.is_ok() {
let fd = util::prepare_uds_pass(&p1).unwrap();
if c.proxy.register(fd).await.is_ok() {
let mut reader = BufReader::new(p0.try_clone().unwrap());
let mut line = String::new();
std::thread::spawn(move || loop {
@ -218,6 +224,8 @@ impl App {
});
});
#[cfg(unix)]
{
let action_usb = gio::SimpleAction::new("usb", None);
let app_clone = app.clone();
action_usb.connect_activate(move |_, _| {
@ -230,10 +238,12 @@ impl App {
}
});
app.inner.app.add_action(&action_usb);
}
app
}
#[cfg(unix)]
fn set_usbredir(&self, usbredir: usbredir::Handler) {
self.inner.usbredir.replace(Some(usbredir));
}