mirror of
https://gitlab.com/marcandre.lureau/qemu-display.git
synced 2024-12-22 13:45:18 +00:00
Handle 2D scanouts
This commit is contained in:
parent
eeea1ce3d7
commit
e0320ca4ab
@ -7,11 +7,12 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
derivative = "2.2.0"
|
||||
zbus = "2.0.0-beta"
|
||||
derivative = "2.1.3"
|
||||
zvariant = "2.4.0"
|
||||
zvariant = { version = "2.4.0", features = ["serde_bytes"] }
|
||||
libc = "0.2.86"
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs", optional = true }
|
||||
enumflags2 = { version = "0.6.4", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = { version = "1.0.123", features = ["derive"] }
|
||||
serde_repr = "0.1.6"
|
||||
serde_bytes = "0.11.5"
|
||||
|
@ -4,10 +4,35 @@ use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::mpsc::{Receiver, RecvError, SendError, Sender};
|
||||
use std::sync::Arc;
|
||||
|
||||
use derivative::Derivative;
|
||||
use zbus::{dbus_interface, export::zvariant::Fd};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct Scanout {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub stride: u32,
|
||||
pub format: u32,
|
||||
#[derivative(Debug = "ignore")]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Debug)]
|
||||
pub struct Update {
|
||||
pub x: i32,
|
||||
pub y: i32,
|
||||
pub w: i32,
|
||||
pub h: i32,
|
||||
pub stride: u32,
|
||||
pub format: u32,
|
||||
#[derivative(Debug = "ignore")]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScanoutDMABUF {
|
||||
pub fd: RawFd,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
@ -17,7 +42,7 @@ pub struct Scanout {
|
||||
pub y0_top: bool,
|
||||
}
|
||||
|
||||
impl Drop for Scanout {
|
||||
impl Drop for ScanoutDMABUF {
|
||||
fn drop(&mut self) {
|
||||
if self.fd >= 0 {
|
||||
unsafe {
|
||||
@ -30,11 +55,10 @@ impl Drop for Scanout {
|
||||
// TODO: replace events mpsc with async traits
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
Switch {
|
||||
width: i32,
|
||||
height: i32,
|
||||
},
|
||||
Update {
|
||||
Scanout(Scanout),
|
||||
Update(Update),
|
||||
ScanoutDMABUF(ScanoutDMABUF),
|
||||
UpdateDMABUF {
|
||||
x: i32,
|
||||
y: i32,
|
||||
w: i32,
|
||||
@ -52,7 +76,6 @@ pub enum Event {
|
||||
hot_y: i32,
|
||||
data: Vec<u8>,
|
||||
},
|
||||
Scanout(Scanout),
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
@ -82,18 +105,46 @@ pub(crate) struct Listener<E: EventSender> {
|
||||
|
||||
#[dbus_interface(name = "org.qemu.Display1.Listener")]
|
||||
impl<E: 'static + EventSender> Listener<E> {
|
||||
fn switch(&mut self, width: i32, height: i32) {
|
||||
self.send(Event::Switch { width, height })
|
||||
}
|
||||
|
||||
fn update(&mut self, x: i32, y: i32, w: i32, h: i32) {
|
||||
self.send(Event::Update { x, y, w, h });
|
||||
if let Err(e) = self.wait() {
|
||||
eprintln!("update returned error: {}", e)
|
||||
}
|
||||
}
|
||||
|
||||
fn scanout(
|
||||
&mut self,
|
||||
width: u32,
|
||||
height: u32,
|
||||
stride: u32,
|
||||
format: u32,
|
||||
data: serde_bytes::ByteBuf,
|
||||
) {
|
||||
self.send(Event::Scanout(Scanout {
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
format,
|
||||
data: data.into_vec(),
|
||||
}))
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
x: i32,
|
||||
y: i32,
|
||||
w: i32,
|
||||
h: i32,
|
||||
stride: u32,
|
||||
format: u32,
|
||||
data: serde_bytes::ByteBuf,
|
||||
) {
|
||||
self.send(Event::Update(Update {
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
stride,
|
||||
format,
|
||||
data: data.into_vec(),
|
||||
}))
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "ScanoutDMABUF")]
|
||||
fn scanout_dmabuf(
|
||||
&mut self,
|
||||
fd: Fd,
|
||||
width: u32,
|
||||
@ -104,7 +155,7 @@ impl<E: 'static + EventSender> Listener<E> {
|
||||
y0_top: bool,
|
||||
) {
|
||||
let fd = unsafe { libc::dup(fd.as_raw_fd()) };
|
||||
self.send(Event::Scanout(Scanout {
|
||||
self.send(Event::ScanoutDMABUF(ScanoutDMABUF {
|
||||
fd,
|
||||
width,
|
||||
height,
|
||||
@ -115,6 +166,14 @@ impl<E: 'static + EventSender> Listener<E> {
|
||||
}))
|
||||
}
|
||||
|
||||
#[dbus_interface(name = "UpdateDMABUF")]
|
||||
fn update_dmabuf(&mut self, x: i32, y: i32, w: i32, h: i32) {
|
||||
self.send(Event::UpdateDMABUF { x, y, w, h });
|
||||
if let Err(e) = self.wait() {
|
||||
eprintln!("update returned error: {}", e)
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_set(&mut self, x: i32, y: i32, on: i32) {
|
||||
self.send(Event::MouseSet { x, y, on })
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ khronos-egl = { version = "3.0.0", features = ["dynamic"] }
|
||||
libloading = "0.6"
|
||||
gl = "0.14.0"
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs", optional = true }
|
||||
derivative = "2.2.0"
|
||||
|
||||
[dependencies.gtk]
|
||||
package = "gtk4"
|
||||
|
@ -169,7 +169,19 @@ impl QemuConsole {
|
||||
clone!(@weak self as con => move |t| {
|
||||
let priv_ = imp::QemuConsole::from_instance(&con);
|
||||
match t {
|
||||
Event::Update { .. } => {
|
||||
Event::Scanout(s) => {
|
||||
priv_.area.set_scanout(s);
|
||||
priv_.area.queue_render();
|
||||
}
|
||||
Event::Update(u) => {
|
||||
priv_.area.update(u);
|
||||
priv_.area.queue_render();
|
||||
}
|
||||
Event::ScanoutDMABUF(s) => {
|
||||
priv_.label.set_label(&format!("{:?}", s));
|
||||
priv_.area.set_scanout_dmabuf(s);
|
||||
}
|
||||
Event::UpdateDMABUF { .. } => {
|
||||
priv_.wait_rendering.set(priv_.wait_rendering.get() + 1);
|
||||
// we don't simply queue_render, as we want a copy immediately
|
||||
priv_.area.make_current();
|
||||
@ -181,10 +193,6 @@ impl QemuConsole {
|
||||
};
|
||||
priv_.area.queue_draw();
|
||||
}
|
||||
Event::Scanout(s) => {
|
||||
priv_.label.set_label(&format!("{:?}", s));
|
||||
priv_.area.set_scanout(s);
|
||||
}
|
||||
Event::Disconnected => {
|
||||
priv_.label.set_label("Console disconnected!");
|
||||
}
|
||||
@ -194,7 +202,7 @@ impl QemuConsole {
|
||||
let cur = gdk::Cursor::from_texture(&tex, hot_x, hot_y, None);
|
||||
priv_.area.set_cursor(Some(&cur));
|
||||
}
|
||||
_ => ()
|
||||
t => { dbg!(t); }
|
||||
}
|
||||
Continue(true)
|
||||
}),
|
||||
|
@ -10,7 +10,7 @@ use std::ffi::{CStr, CString};
|
||||
use crate::egl;
|
||||
use crate::error::*;
|
||||
use gl::{self, types::*};
|
||||
use qemu_display_listener::Scanout;
|
||||
use qemu_display_listener::{Scanout, ScanoutDMABUF, Update};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
@ -22,7 +22,7 @@ mod imp {
|
||||
pub texture_blit_vao: Cell<GLuint>,
|
||||
pub texture_blit_prog: Cell<GLuint>,
|
||||
pub texture_blit_flip_prog: Cell<GLuint>,
|
||||
pub scanout: Cell<Option<Scanout>>,
|
||||
pub scanout: Cell<Option<ScanoutDMABUF>>,
|
||||
pub scanout_size: Cell<(u32, u32)>,
|
||||
}
|
||||
|
||||
@ -229,6 +229,58 @@ mod imp {
|
||||
|
||||
pub fn set_scanout(&self, widget: &super::QemuConsoleArea, s: Scanout) {
|
||||
widget.make_current();
|
||||
|
||||
if s.format != 0x20020888 {
|
||||
todo!();
|
||||
}
|
||||
unsafe {
|
||||
gl::BindTexture(gl::TEXTURE_2D, self.tex_id());
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as _);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
|
||||
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, s.stride as i32 / 4);
|
||||
gl::TexImage2D(
|
||||
gl::TEXTURE_2D,
|
||||
0,
|
||||
gl::RGB as _,
|
||||
s.width as _,
|
||||
s.height as _,
|
||||
0,
|
||||
gl::BGRA,
|
||||
gl::UNSIGNED_BYTE,
|
||||
s.data.as_ptr() as _,
|
||||
);
|
||||
}
|
||||
|
||||
self.scanout_size.set((s.width, s.height));
|
||||
}
|
||||
|
||||
pub fn update(&self, widget: &super::QemuConsoleArea, u: Update) {
|
||||
widget.make_current();
|
||||
|
||||
if u.format != 0x20020888 {
|
||||
todo!();
|
||||
}
|
||||
unsafe {
|
||||
gl::BindTexture(gl::TEXTURE_2D, self.tex_id());
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as _);
|
||||
gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
|
||||
gl::PixelStorei(gl::UNPACK_ROW_LENGTH, u.stride as i32 / 4);
|
||||
gl::TexSubImage2D(
|
||||
gl::TEXTURE_2D,
|
||||
0,
|
||||
u.x,
|
||||
u.y,
|
||||
u.w,
|
||||
u.h,
|
||||
gl::BGRA,
|
||||
gl::UNSIGNED_BYTE,
|
||||
u.data.as_ptr() as _,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_scanout_dmabuf(&self, widget: &super::QemuConsoleArea, s: ScanoutDMABUF) {
|
||||
widget.make_current();
|
||||
let egl = egl::egl();
|
||||
|
||||
let egl_dpy = if let Ok(dpy) = widget.get_display().downcast::<gdk_wl::WaylandDisplay>()
|
||||
@ -315,6 +367,18 @@ impl QemuConsoleArea {
|
||||
priv_.set_scanout(self, s);
|
||||
}
|
||||
|
||||
pub fn update(&self, u: Update) {
|
||||
let priv_ = imp::QemuConsoleArea::from_instance(self);
|
||||
|
||||
priv_.update(self, u);
|
||||
}
|
||||
|
||||
pub fn set_scanout_dmabuf(&self, s: ScanoutDMABUF) {
|
||||
let priv_ = imp::QemuConsoleArea::from_instance(self);
|
||||
|
||||
priv_.set_scanout_dmabuf(self, s);
|
||||
}
|
||||
|
||||
pub fn save_to_png(&self, filename: &str) {
|
||||
let priv_ = imp::QemuConsoleArea::from_instance(self);
|
||||
|
||||
|
@ -1,12 +1,15 @@
|
||||
use std::error::Error;
|
||||
use std::net::{TcpListener, TcpStream};
|
||||
use std::{thread, time, io};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{io, thread, time};
|
||||
|
||||
use qemu_display_listener::{Console, Event};
|
||||
use zbus::Connection;
|
||||
use clap::Clap;
|
||||
use vnc::{server::FramebufferUpdate, server::Event as VncEvent, PixelFormat, Rect, Server as VncServer, Error as VncError};
|
||||
use qemu_display_listener::{Console, Event};
|
||||
use vnc::{
|
||||
server::Event as VncEvent, server::FramebufferUpdate, Error as VncError, PixelFormat, Rect,
|
||||
Server as VncServer,
|
||||
};
|
||||
use zbus::Connection;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub struct SocketAddrArgs {
|
||||
@ -30,7 +33,6 @@ struct Cli {
|
||||
address: SocketAddrArgs,
|
||||
}
|
||||
|
||||
|
||||
struct ServerInner {
|
||||
width: u16,
|
||||
height: u16,
|
||||
@ -43,10 +45,7 @@ struct Server {
|
||||
impl Server {
|
||||
fn new(width: u16, height: u16) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(ServerInner {
|
||||
width,
|
||||
height,
|
||||
}))
|
||||
inner: Arc::new(Mutex::new(ServerInner { width, height })),
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,11 +64,13 @@ impl Server {
|
||||
Ok(e) => e,
|
||||
Err(VncError::Io(ref e)) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
},
|
||||
}
|
||||
Err(VncError::Disconnected) => {
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => { return Err(e.into()); }
|
||||
Err(e) => {
|
||||
return Err(e.into());
|
||||
}
|
||||
};
|
||||
match event {
|
||||
VncEvent::FramebufferUpdateRequest { .. } => {
|
||||
@ -108,16 +109,16 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
let server = Server::new(console.width()? as u16, console.height()? as u16);
|
||||
|
||||
let _thread = thread::spawn(move || {
|
||||
match rx.recv().unwrap() {
|
||||
Event::Scanout(s) => {
|
||||
let _thread = thread::spawn(move || match rx.recv().unwrap() {
|
||||
Event::ScanoutDMABUF(s) => {
|
||||
dbg!(&s);
|
||||
unsafe {
|
||||
libc::close(s.fd);
|
||||
}
|
||||
let _ = ack.send(());
|
||||
},
|
||||
e => { dbg!(e); },
|
||||
}
|
||||
e => {
|
||||
dbg!(e);
|
||||
}
|
||||
});
|
||||
for stream in listener.incoming() {
|
||||
|
Loading…
Reference in New Issue
Block a user