From 6801ea795cc5ef8e73a077ef9044f7c4a0b06801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Wed, 25 Sep 2024 18:09:33 +0400 Subject: [PATCH] display/rdw: implement Unix.Map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau --- Cargo.lock | 10 ++++ qemu-display/Cargo.toml | 1 + qemu-display/src/lib.rs | 1 + qemu-display/src/util.rs | 7 +++ qemu-rdw/src/display.rs | 120 ++++++++++++++++++++++++++++++--------- 5 files changed, 112 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2d4de5f..3fe0bb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2626,6 +2626,15 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -3359,6 +3368,7 @@ dependencies = [ "futures", "futures-util", "libc", + "memmap2", "once_cell", "pixman-sys", "qapi", diff --git a/qemu-display/Cargo.toml b/qemu-display/Cargo.toml index 09d3dee..2c1c9c0 100644 --- a/qemu-display/Cargo.toml +++ b/qemu-display/Cargo.toml @@ -29,6 +29,7 @@ async-trait = "0.1" async-lock = "3.4" qapi = { version = "0.14", features = ["qmp"], optional = true } base64 = { version = "0.22", optional = true } +memmap2 = "0.9.5" [target.'cfg(windows)'.dependencies] uds_windows = "1.1" diff --git a/qemu-display/src/lib.rs b/qemu-display/src/lib.rs index 271ee1a..d0ee05c 100644 --- a/qemu-display/src/lib.rs +++ b/qemu-display/src/lib.rs @@ -1,6 +1,7 @@ #![allow(clippy::too_many_arguments)] pub use zbus; +pub use memmap2; pub mod util; #[cfg(windows)] diff --git a/qemu-display/src/util.rs b/qemu-display/src/util.rs index 1e2bb9d..fe6ed0a 100644 --- a/qemu-display/src/util.rs +++ b/qemu-display/src/util.rs @@ -1,5 +1,7 @@ use crate::Result; +use std::os::fd::RawFd; + #[cfg(unix)] use std::os::unix::net::UnixStream; #[cfg(windows)] @@ -30,3 +32,8 @@ pub fn prepare_uds_pass(#[cfg(windows)] peer_pid: u32, us: &UnixStream) -> Resul p.duplicate_socket(SOCKET(us.as_raw_socket() as _)) } } + +#[cfg(unix)] +pub unsafe fn mmap(fd: RawFd, len: usize, offset: u64) -> std::io::Result { + memmap2::MmapOptions::new().len(len).offset(offset).map(fd) +} diff --git a/qemu-rdw/src/display.rs b/qemu-rdw/src/display.rs index 715f301..355021e 100644 --- a/qemu-rdw/src/display.rs +++ b/qemu-rdw/src/display.rs @@ -2,9 +2,11 @@ use futures_util::StreamExt; use glib::{clone, subclass::prelude::*, MainContext}; use gtk::glib; use once_cell::sync::OnceCell; -use qemu_display::{Console, ConsoleListenerHandler}; #[cfg(windows)] -use qemu_display::{ConsoleListenerD3d11Handler, ConsoleListenerMapHandler}; +use qemu_display::ConsoleListenerD3d11Handler; +#[cfg(any(windows, unix))] +use qemu_display::ConsoleListenerMapHandler; +use qemu_display::{Console, ConsoleListenerHandler}; use rdw::{gtk, DisplayExt}; use std::cell::Cell; #[cfg(unix)] @@ -13,10 +15,12 @@ use std::os::unix::io::IntoRawFd; mod imp { use super::*; use gtk::subclass::prelude::*; - #[cfg(windows)] + use qemu_display::{memmap2::Mmap, util::mmap}; + #[cfg(any(windows, unix))] use std::cell::RefCell; #[cfg(windows)] use std::ffi::c_void; + use std::os::fd::AsRawFd; #[cfg(windows)] use windows::Win32::Foundation::{CloseHandle, HANDLE}; @@ -80,6 +84,8 @@ mod imp { pub struct Display { pub(crate) console: OnceCell, keymap: Cell>, + #[cfg(unix)] + scanout_map: RefCell>, #[cfg(windows)] scanout_map: RefCell>, } @@ -275,11 +281,10 @@ mod imp { let (sender, mut receiver) = futures::channel::mpsc::unbounded(); let handler = ConsoleHandler { sender }; console.register_listener(handler.clone()).await.unwrap(); + #[cfg(any(windows, unix))] + console.set_map_listener(handler.clone()).await.unwrap(); #[cfg(windows)] - { - console.set_map_listener(handler.clone()).await.unwrap(); - console.set_d3d11_listener(handler.clone()).await.unwrap(); - } + console.set_d3d11_listener(handler.clone()).await.unwrap(); MainContext::default().spawn_local(clone!( #[weak] @@ -318,28 +323,79 @@ mod imp { Some(&u.data), ); } + #[cfg(unix)] + ScanoutMap { scanout, wait_tx } => { + log::debug!("{scanout:?}"); + if scanout.format != 0x20020888 { + log::warn!( + "Format not yet supported: {:X}", + scanout.format + ); + continue; + } + let map = unsafe { + mmap( + scanout.fd.as_raw_fd(), + scanout.height as usize * scanout.stride as usize, + scanout.offset.into(), + ) + } + .unwrap(); + this.obj().set_display_size(Some(( + scanout.width as _, + scanout.height as _, + ))); + this.scanout_map.replace(Some((map, scanout.stride))); + let _ = wait_tx.send(()); + } + #[cfg(unix)] + UpdateMap(u) => { + log::debug!("{u:?}"); + let scanout_map = this.scanout_map.borrow(); + let Some((map, stride)) = scanout_map.as_ref() else { + log::warn!("No mapped scanout!"); + continue; + }; + let stride = *stride; + let bytes = map.as_ref(); + this.obj().update_area( + u.x as _, + u.y as _, + u.w as _, + u.h as _, + stride as _, + Some( + &bytes[u.y as usize * stride as usize + + u.x as usize * 4..], + ), + ); + } #[cfg(windows)] - ScanoutMap(s) => { + ScanoutMap { scanout, wait_tx } => { use windows::Win32::System::Memory::{ MapViewOfFile, FILE_MAP_READ, }; log::debug!("{s:?}"); - if s.format != 0x20020888 { - log::warn!("Format not yet supported: {:X}", s.format); + if scanout.format != 0x20020888 { + log::warn!( + "Format not yet supported: {:X}", + scanout.format + ); continue; } - let handle = HANDLE(s.handle as _); - let size = s.height as usize * s.stride as usize; - let offset = s.offset as isize; + let handle = HANDLE(scanout.handle as _); + let size = + scanout.height as usize * scanout.stride as usize; + let offset = scanout.offset as isize; let ptr = unsafe { MapViewOfFile( handle, FILE_MAP_READ, 0, 0, - s.offset as usize + size, + scanout.offset as usize + size, ) }; if ptr.is_null() { @@ -353,17 +409,20 @@ mod imp { offset, size, }; - this.obj() - .set_display_size(Some((s.width as _, s.height as _))); + this.obj().set_display_size(Some(( + scanout.width as _, + scanout.height as _, + ))); this.obj().update_area( 0, 0, - s.width as _, - s.height as _, - s.stride as _, + scanout.width as _, + scanout.height as _, + scanout.stride as _, Some(map.as_bytes()), ); - this.scanout_map.replace(Some((map, s.stride))); + this.scanout_map.replace(Some((map, scanout.stride))); + let _ = wait_tx.send(()); } #[cfg(windows)] UpdateMap(u) => { @@ -504,9 +563,12 @@ impl Display { enum ConsoleEvent { Scanout(qemu_display::Scanout), Update(qemu_display::Update), - #[cfg(windows)] - ScanoutMap(qemu_display::ScanoutMap), - #[cfg(windows)] + #[cfg(any(windows, unix))] + ScanoutMap { + scanout: qemu_display::ScanoutMap, + wait_tx: futures::channel::oneshot::Sender<()>, + }, + #[cfg(any(windows, unix))] UpdateMap(qemu_display::UpdateMap), #[cfg(windows)] ScanoutD3dTexture2d(qemu_display::ScanoutD3dTexture2d), @@ -541,15 +603,17 @@ impl ConsoleHandler { } } -#[cfg(windows)] +#[cfg(any(windows, unix))] #[async_trait::async_trait] impl ConsoleListenerMapHandler for ConsoleHandler { - #[cfg(windows)] async fn scanout_map(&mut self, scanout: qemu_display::ScanoutMap) { - self.send(ConsoleEvent::ScanoutMap(scanout)); + let (wait_tx, wait_rx) = futures::channel::oneshot::channel(); + self.send(ConsoleEvent::ScanoutMap { scanout, wait_tx }); + if let Err(e) = wait_rx.await { + log::warn!("wait update d3d texture2d failed: {}", e); + } } - #[cfg(windows)] async fn update_map(&mut self, update: qemu_display::UpdateMap) { self.send(ConsoleEvent::UpdateMap(update)); } @@ -620,6 +684,8 @@ impl ConsoleListenerHandler for ConsoleHandler { "org.qemu.Display1.Listener.Win32.Map".to_string(), "org.qemu.Display1.Listener.Win32.D3d11".to_string(), ] + } else if cfg!(unix) { + vec!["org.qemu.Display1.Listener.Unix.Map".to_string()] } else { vec![] }