mirror of
https://gitlab.com/marcandre.lureau/qemu-display.git
synced 2024-12-22 05:35:20 +00:00
display/rdp: implement Unix.Map
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
parent
dd7a0d3f60
commit
62b6fb8d0a
@ -36,6 +36,7 @@ windows = { version = "0.58", features = [
|
|||||||
"Win32_Networking_WinSock",
|
"Win32_Networking_WinSock",
|
||||||
"Win32_Foundation",
|
"Win32_Foundation",
|
||||||
"Win32_System_IO",
|
"Win32_System_IO",
|
||||||
|
"Win32_System_Memory",
|
||||||
"Win32_System_Threading",
|
"Win32_System_Threading",
|
||||||
] }
|
] }
|
||||||
|
|
||||||
|
@ -119,19 +119,24 @@ impl Console {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self, handler))]
|
||||||
pub async fn set_map_listener<H: ConsoleListenerMapHandler>(&self, handler: H) -> Result<bool> {
|
pub async fn set_map_listener<H: ConsoleListenerMapHandler>(&self, handler: H) -> Result<bool> {
|
||||||
if let Some(l) = &*self.listener.write().unwrap() {
|
let listener = self.listener.write().unwrap().take();
|
||||||
return l
|
match listener {
|
||||||
.object_server()
|
Some(l) => {
|
||||||
.at(
|
let res = l
|
||||||
"/org/qemu/Display1/Listener",
|
.object_server()
|
||||||
ConsoleListenerMap::new(handler),
|
.at(
|
||||||
)
|
"/org/qemu/Display1/Listener",
|
||||||
.await
|
ConsoleListenerMap::new(handler),
|
||||||
.map_err(|e| e.into());
|
)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.into());
|
||||||
|
*self.listener.write().unwrap() = Some(l);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
_ => Err(crate::Error::Failed("Must call register first!".into())),
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(crate::Error::Failed("Must call register first!".into()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::win32::Fd;
|
use crate::win32::{Fd, Mmap};
|
||||||
use derivative::Derivative;
|
use derivative::Derivative;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
use memmap2::Mmap;
|
||||||
use std::{
|
use std::ops::Drop;
|
||||||
ops::Drop,
|
#[cfg(unix)]
|
||||||
os::fd::{AsFd, OwnedFd},
|
use std::os::{
|
||||||
|
fd::{AsFd, OwnedFd},
|
||||||
|
unix::io::{AsRawFd, IntoRawFd, RawFd},
|
||||||
};
|
};
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use zbus::zvariant::Fd;
|
use zbus::zvariant::Fd;
|
||||||
@ -47,6 +49,58 @@ pub struct ScanoutMap {
|
|||||||
pub format: u32,
|
pub format: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ScanoutMmap {
|
||||||
|
scanout: ScanoutMap,
|
||||||
|
#[cfg(unix)]
|
||||||
|
mmap: Mmap,
|
||||||
|
#[cfg(windows)]
|
||||||
|
mmap: Mmap,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScanoutMap {
|
||||||
|
pub fn mmap(self) -> std::io::Result<ScanoutMmap> {
|
||||||
|
let len = self.height as usize * self.stride as usize;
|
||||||
|
let offset = self.offset;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let mmap = {
|
||||||
|
let fd = self.fd.as_raw_fd();
|
||||||
|
unsafe {
|
||||||
|
memmap2::MmapOptions::new()
|
||||||
|
.len(len)
|
||||||
|
.offset(offset.into())
|
||||||
|
.map(fd)
|
||||||
|
}?
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let mmap = {
|
||||||
|
let handle = windows::Win32::Foundation::HANDLE(self.handle as _); // taking ownership
|
||||||
|
Mmap::new(handle, offset.try_into().unwrap(), len)?
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ScanoutMmap {
|
||||||
|
scanout: self,
|
||||||
|
mmap,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScanoutMmap {
|
||||||
|
pub fn as_ref(&self) -> &[u8] {
|
||||||
|
self.mmap.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stride(&self) -> u32 {
|
||||||
|
self.scanout.stride
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format(&self) -> u32 {
|
||||||
|
self.scanout.format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct UpdateMap {
|
pub struct UpdateMap {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use crate::Result;
|
use crate::Result;
|
||||||
|
|
||||||
use std::os::fd::RawFd;
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -32,8 +30,3 @@ pub fn prepare_uds_pass(#[cfg(windows)] peer_pid: u32, us: &UnixStream) -> Resul
|
|||||||
p.duplicate_socket(SOCKET(us.as_raw_socket() as _))
|
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::Mmap> {
|
|
||||||
memmap2::MmapOptions::new().len(len).offset(offset).map(fd)
|
|
||||||
}
|
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
use std::io;
|
use std::io;
|
||||||
|
use tracing::warn;
|
||||||
|
|
||||||
use windows::Win32::{
|
use windows::Win32::{
|
||||||
Foundation::{CloseHandle, HANDLE},
|
Foundation::{CloseHandle, HANDLE},
|
||||||
Networking::WinSock::{WSADuplicateSocketW, SOCKET, WSAPROTOCOL_INFOW},
|
Networking::WinSock::{WSADuplicateSocketW, SOCKET, WSAPROTOCOL_INFOW},
|
||||||
System::Threading::PROCESS_ACCESS_RIGHTS,
|
System::{
|
||||||
|
Memory::{MapViewOfFile, UnmapViewOfFile, FILE_MAP_READ, MEMORY_MAPPED_VIEW_ADDRESS},
|
||||||
|
Threading::PROCESS_ACCESS_RIGHTS,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "qmp")]
|
#[cfg(feature = "qmp")]
|
||||||
@ -117,3 +122,48 @@ pub(crate) fn unix_stream_get_peer_pid(stream: &UnixStream) -> Result<u32, std::
|
|||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct Mmap {
|
||||||
|
handle: HANDLE,
|
||||||
|
ptr: MEMORY_MAPPED_VIEW_ADDRESS,
|
||||||
|
offset: isize,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl Drop for Mmap {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
if let Err(err) = UnmapViewOfFile(self.ptr) {
|
||||||
|
warn!("error while unmap: {}", err);
|
||||||
|
}
|
||||||
|
if let Err(err) = CloseHandle(self.handle) {
|
||||||
|
warn!("error while closing mmap: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
impl Mmap {
|
||||||
|
// FIXME: remove and replace with memmap2, use offset properly
|
||||||
|
pub(crate) fn new(handle: HANDLE, offset: usize, size: usize) -> std::io::Result<Self> {
|
||||||
|
let ptr = unsafe { MapViewOfFile(handle, FILE_MAP_READ, 0, 0, offset + size) };
|
||||||
|
if ptr.Value.is_null() {
|
||||||
|
return Err(io::Error::last_os_error());
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
handle,
|
||||||
|
ptr,
|
||||||
|
offset: offset.try_into().unwrap(),
|
||||||
|
size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn as_ref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
std::slice::from_raw_parts(self.ptr.Value.cast::<u8>().offset(self.offset), self.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -4,7 +4,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use qemu_display::{Console, ConsoleListenerHandler, Cursor, Display, MouseSet, Scanout, Update};
|
use qemu_display::{
|
||||||
|
Console, ConsoleListenerHandler, ConsoleListenerMapHandler, Cursor, Display, MouseSet, Scanout,
|
||||||
|
ScanoutMap, ScanoutMmap, Update, UpdateMap,
|
||||||
|
};
|
||||||
|
|
||||||
use ironrdp::{
|
use ironrdp::{
|
||||||
connector::DesktopSize,
|
connector::DesktopSize,
|
||||||
@ -14,7 +17,7 @@ use ironrdp::{
|
|||||||
RdpServerDisplayUpdates,
|
RdpServerDisplayUpdates,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use crate::{cast, utils::PixmanFormat};
|
use crate::{cast, utils::PixmanFormat};
|
||||||
|
|
||||||
@ -45,7 +48,9 @@ impl DisplayHandler {
|
|||||||
let desktop_size = DesktopSize { width, height };
|
let desktop_size = DesktopSize { width, height };
|
||||||
let listener = Listener::new(sender, desktop_size);
|
let listener = Listener::new(sender, desktop_size);
|
||||||
self.console.unregister_listener();
|
self.console.unregister_listener();
|
||||||
self.console.register_listener(listener).await?;
|
self.console.register_listener(listener.clone()).await?;
|
||||||
|
#[cfg(any(windows, unix))]
|
||||||
|
self.console.set_map_listener(listener.clone()).await?;
|
||||||
|
|
||||||
Ok(DisplayUpdates { receiver })
|
Ok(DisplayUpdates { receiver })
|
||||||
}
|
}
|
||||||
@ -94,6 +99,7 @@ struct Inner {
|
|||||||
sender: queue::Sender,
|
sender: queue::Sender,
|
||||||
desktop_size: DesktopSize,
|
desktop_size: DesktopSize,
|
||||||
cursor_hot: (i32, i32),
|
cursor_hot: (i32, i32),
|
||||||
|
scanout_mmap: Option<ScanoutMmap>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -107,6 +113,7 @@ impl Listener {
|
|||||||
sender,
|
sender,
|
||||||
desktop_size,
|
desktop_size,
|
||||||
cursor_hot: (0, 0),
|
cursor_hot: (0, 0),
|
||||||
|
scanout_mmap: None,
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
inner: Arc::new(Mutex::new(inner)),
|
inner: Arc::new(Mutex::new(inner)),
|
||||||
@ -143,6 +150,45 @@ impl Listener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ConsoleListenerMapHandler for Listener {
|
||||||
|
async fn scanout_map(&mut self, scanout: ScanoutMap) {
|
||||||
|
self.set_desktop_size(scanout.width, scanout.height).await;
|
||||||
|
match scanout.mmap() {
|
||||||
|
Ok(mmap) => self.inner.lock().unwrap().scanout_mmap = Some(mmap),
|
||||||
|
Err(err) => warn!("Failed to mmap: {}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn update_map(&mut self, update: UpdateMap) {
|
||||||
|
let update = {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
let Some(mmap) = &inner.scanout_mmap else {
|
||||||
|
warn!("Update with no map!");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let (stride, format) = (mmap.stride(), mmap.format());
|
||||||
|
if format != 0x20020888 {
|
||||||
|
warn!("Format not yet supported: {:X}", format);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// FIXME: use arc of data
|
||||||
|
let data = mmap.as_ref()[update.y as usize * stride as usize + update.x as usize * 4..]
|
||||||
|
.to_vec();
|
||||||
|
Update {
|
||||||
|
x: update.x,
|
||||||
|
y: update.y,
|
||||||
|
w: update.w,
|
||||||
|
h: update.h,
|
||||||
|
stride,
|
||||||
|
format,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.update(update).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl ConsoleListenerHandler for Listener {
|
impl ConsoleListenerHandler for Listener {
|
||||||
async fn scanout(&mut self, scanout: Scanout) {
|
async fn scanout(&mut self, scanout: Scanout) {
|
||||||
@ -256,6 +302,10 @@ impl ConsoleListenerHandler for Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn interfaces(&self) -> Vec<String> {
|
fn interfaces(&self) -> Vec<String> {
|
||||||
vec![]
|
if cfg!(unix) {
|
||||||
|
vec!["org.qemu.Display1.Listener.Unix.Map".to_string()]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,11 @@ use std::os::unix::io::IntoRawFd;
|
|||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
use gtk::subclass::prelude::*;
|
use gtk::subclass::prelude::*;
|
||||||
use qemu_display::{memmap2::Mmap, util::mmap};
|
use qemu_display::ScanoutMmap;
|
||||||
#[cfg(any(windows, unix))]
|
#[cfg(any(windows, unix))]
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::os::fd::AsRawFd;
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
||||||
|
|
||||||
@ -84,10 +83,7 @@ mod imp {
|
|||||||
pub struct Display {
|
pub struct Display {
|
||||||
pub(crate) console: OnceCell<Console>,
|
pub(crate) console: OnceCell<Console>,
|
||||||
keymap: Cell<Option<&'static [u16]>>,
|
keymap: Cell<Option<&'static [u16]>>,
|
||||||
#[cfg(unix)]
|
scanout_map: RefCell<Option<ScanoutMmap>>,
|
||||||
scanout_map: RefCell<Option<(Mmap, u32)>>,
|
|
||||||
#[cfg(windows)]
|
|
||||||
scanout_map: RefCell<Option<(MemoryMap, u32)>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
@ -333,39 +329,37 @@ mod imp {
|
|||||||
);
|
);
|
||||||
continue;
|
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((
|
this.obj().set_display_size(Some((
|
||||||
scanout.width as _,
|
scanout.width as _,
|
||||||
scanout.height as _,
|
scanout.height as _,
|
||||||
)));
|
)));
|
||||||
this.scanout_map.replace(Some((map, scanout.stride)));
|
let map = match scanout.mmap() {
|
||||||
|
Ok(map) => Some(map),
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to mmap: {}", err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.scanout_map.replace(map);
|
||||||
let _ = wait_tx.send(());
|
let _ = wait_tx.send(());
|
||||||
}
|
}
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
UpdateMap(u) => {
|
UpdateMap(u) => {
|
||||||
log::debug!("{u:?}");
|
log::debug!("{u:?}");
|
||||||
let scanout_map = this.scanout_map.borrow();
|
let scanout_map = this.scanout_map.borrow();
|
||||||
let Some((map, stride)) = scanout_map.as_ref() else {
|
let Some(map) = scanout_map.as_ref() else {
|
||||||
log::warn!("No mapped scanout!");
|
log::warn!("No mapped scanout!");
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let stride = *stride;
|
|
||||||
let bytes = map.as_ref();
|
let bytes = map.as_ref();
|
||||||
this.obj().update_area(
|
this.obj().update_area(
|
||||||
u.x as _,
|
u.x as _,
|
||||||
u.y as _,
|
u.y as _,
|
||||||
u.w as _,
|
u.w as _,
|
||||||
u.h as _,
|
u.h as _,
|
||||||
stride as _,
|
map.stride() as _,
|
||||||
Some(
|
Some(
|
||||||
&bytes[u.y as usize * stride as usize
|
&bytes[u.y as usize * map.stride() as usize
|
||||||
+ u.x as usize * 4..],
|
+ u.x as usize * 4..],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user