win32: add shared scanout map handling

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2022-11-16 22:58:51 +04:00
parent f33c9c4659
commit 3712905ec1
3 changed files with 168 additions and 1 deletions

View File

@ -32,6 +32,24 @@ pub struct Update {
pub data: Vec<u8>,
}
#[derive(Debug)]
pub struct ScanoutMap {
pub handle: u64,
pub offset: u32,
pub width: u32,
pub height: u32,
pub stride: u32,
pub format: u32,
}
#[derive(Debug, Copy, Clone)]
pub struct UpdateMap {
pub x: i32,
pub y: i32,
pub w: i32,
pub h: i32,
}
#[cfg(unix)]
#[derive(Debug)]
pub struct ScanoutDMABUF {
@ -98,6 +116,12 @@ pub trait ConsoleListenerHandler: 'static + Send + Sync {
async fn update(&mut self, update: Update);
#[cfg(windows)]
async fn scanout_map(&mut self, scanout: ScanoutMap);
#[cfg(windows)]
async fn update_map(&mut self, update: UpdateMap);
#[cfg(unix)]
async fn scanout_dmabuf(&mut self, scanout: ScanoutDMABUF);
@ -160,6 +184,57 @@ impl<H: ConsoleListenerHandler> ConsoleListener<H> {
.await;
}
#[cfg(windows)]
async fn scanout_map(
&mut self,
handle: u64,
offset: u32,
width: u32,
height: u32,
stride: u32,
format: u32,
) -> zbus::fdo::Result<()> {
let map = ScanoutMap {
handle,
offset,
width,
height,
stride,
format,
};
self.handler.scanout_map(map).await;
Ok(())
}
#[cfg(not(windows))]
async fn scanout_map(
&mut self,
_handle: u64,
_offset: u32,
_width: u32,
_height: u32,
_stride: u32,
_format: u32,
) -> zbus::fdo::Result<()> {
Err(zbus::fdo::Error::NotSupported(
"Shared map is not support on !windows".into(),
))
}
#[cfg(windows)]
async fn update_map(&mut self, x: i32, y: i32, w: i32, h: i32) -> zbus::fdo::Result<()> {
let up = UpdateMap { x, y, w, h };
self.handler.update_map(up).await;
Ok(())
}
#[cfg(not(windows))]
async fn update_map(&mut self, _x: i32, _y: i32, _w: i32, _h: i32) -> zbus::fdo::Result<()> {
Err(zbus::fdo::Error::NotSupported(
"Shared map is not support on !windows".into(),
))
}
#[cfg(not(unix))]
#[dbus_interface(name = "ScanoutDMABUF")]
async fn scanout_dmabuf(

View File

@ -19,5 +19,8 @@ rdw = { package = "rdw4", version = "0.1", features = ["bindings"] }
futures-util = "0.3"
futures = "0.3"
async-trait = "0.1"
uds_windows = "1.0.2"
tracing-subscriber = { version = "0.3.11", features = ["env-filter" , "fmt"], default-features = false }
[target.'cfg(target_os = "windows")'.dependencies]
uds_windows = "1.0.2"
windows = { version = "0.43.0", features = ["Win32_System_Memory", "Win32_Foundation"] }

View File

@ -11,6 +11,12 @@ use std::os::unix::io::IntoRawFd;
mod imp {
use super::*;
use gtk::subclass::prelude::*;
#[cfg(windows)]
use std::cell::RefCell;
#[cfg(windows)]
use std::ffi::c_void;
#[cfg(windows)]
use windows::Win32::Foundation::{CloseHandle, HANDLE};
#[repr(C)]
pub struct RdwDisplayQemuClass {
@ -38,10 +44,42 @@ mod imp {
type Type = Display;
}
#[cfg(windows)]
#[derive(Debug)]
struct MemoryMap {
handle: HANDLE,
ptr: *const c_void,
offset: isize,
size: usize,
}
#[cfg(windows)]
impl Drop for MemoryMap {
fn drop(&mut self) {
unsafe {
use windows::Win32::System::Memory::UnmapViewOfFile;
UnmapViewOfFile(self.ptr);
CloseHandle(self.handle);
}
}
}
#[cfg(windows)]
impl MemoryMap {
fn as_bytes(&self) -> &[u8] {
unsafe {
std::slice::from_raw_parts(self.ptr.cast::<u8>().offset(self.offset), self.size)
}
}
}
#[derive(Debug, Default)]
pub struct Display {
pub(crate) console: OnceCell<Console>,
keymap: Cell<Option<&'static [u16]>>,
#[cfg(windows)]
scanout_map: RefCell<Option<(MemoryMap, u32)>>,
}
#[glib::object_subclass]
@ -174,6 +212,42 @@ mod imp {
}
this.obj().update_area(u.x as _, u.y as _, u.w as _, u.h as _, u.stride as _, &u.data);
}
#[cfg(windows)]
ScanoutMap(s) => {
use windows::Win32::System::Memory::{FILE_MAP_READ, MapViewOfFile};
log::debug!("{s:?}");
if s.format != 0x20020888 {
log::warn!("Format not yet supported: {:X}", s.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 ptr = unsafe { MapViewOfFile(handle, FILE_MAP_READ, 0, 0, s.offset as usize + size) };
if ptr.is_null() {
log::warn!("Failed to map scanout!");
continue;
}
let map = MemoryMap { ptr, handle, offset, size };
this.obj().set_display_size(Some((s.width as _, s.height as _)));
this.obj().update_area(0, 0, s.width as _, s.height as _, s.stride as _, map.as_bytes());
this.scanout_map.replace(Some((map, s.stride)));
}
#[cfg(windows)]
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_bytes();
this.obj().update_area(u.x as _, u.y as _, u.w as _, u.h as _, stride as _, &bytes[u.y as usize * stride as usize + u.x as usize * 4..]);
}
#[cfg(unix)]
ScanoutDMABUF(s) => {
this.obj().set_display_size(Some((s.width as _, s.height as _)));
@ -196,6 +270,7 @@ mod imp {
log::warn!("Console disconnected");
}
CursorDefine(c) => {
log::debug!("{c:?}");
let cursor = rdw::Display::make_cursor(
&c.data,
c.width,
@ -254,6 +329,10 @@ impl Display {
enum ConsoleEvent {
Scanout(qemu_display::Scanout),
Update(qemu_display::Update),
#[cfg(windows)]
ScanoutMap(qemu_display::ScanoutMap),
#[cfg(windows)]
UpdateMap(qemu_display::UpdateMap),
#[cfg(unix)]
ScanoutDMABUF(qemu_display::ScanoutDMABUF),
#[cfg(unix)]
@ -288,6 +367,16 @@ impl ConsoleListenerHandler for ConsoleHandler {
self.send(ConsoleEvent::Update(update));
}
#[cfg(windows)]
async fn scanout_map(&mut self, scanout: qemu_display::ScanoutMap) {
self.send(ConsoleEvent::ScanoutMap(scanout));
}
#[cfg(windows)]
async fn update_map(&mut self, update: qemu_display::UpdateMap) {
self.send(ConsoleEvent::UpdateMap(update));
}
#[cfg(unix)]
async fn scanout_dmabuf(&mut self, scanout: qemu_display::ScanoutDMABUF) {
self.send(ConsoleEvent::ScanoutDMABUF(scanout));