diff --git a/Cargo.toml b/Cargo.toml index 2ef8312..ab21aaa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,11 @@ members = [ "keycodemap", "qemu-display-listener", "qemu-gtk4", + "qemu-vnc", "xtask", ] [patch.crates-io] zbus = { path = '/home/elmarco/src/zbus/zbus' } zvariant = { path = '/home/elmarco/src/zbus/zvariant' } +vnc = { git = 'https://github.com/elmarco/rust-vnc', branch = 'server' } diff --git a/qemu-vnc/Cargo.toml b/qemu-vnc/Cargo.toml new file mode 100644 index 0000000..2110cd0 --- /dev/null +++ b/qemu-vnc/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "qemu-vnc" +version = "0.1.0" +authors = ["Marc-André Lureau "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +qemu-display-listener = { path = "../qemu-display-listener" } +vnc = "0.4.0" +clap = "3.0.0-beta.2" +zbus = { version = "2.0.0-beta" } +libc = "0.2.86" diff --git a/qemu-vnc/src/main.rs b/qemu-vnc/src/main.rs new file mode 100644 index 0000000..374c780 --- /dev/null +++ b/qemu-vnc/src/main.rs @@ -0,0 +1,127 @@ +use std::error::Error; +use std::net::{TcpListener, TcpStream}; +use std::{thread, time, io}; +use std::sync::{Arc, Mutex}; + +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}; + +#[derive(Clap, Debug)] +pub struct SocketAddrArgs { + /// IP address + #[clap(short, long, default_value = "127.0.0.1")] + address: std::net::IpAddr, + /// IP port number + #[clap(short, long, default_value = "5900")] + port: u16, +} + +impl From for std::net::SocketAddr { + fn from(args: SocketAddrArgs) -> Self { + (args.address, args.port).into() + } +} + +#[derive(Clap, Debug)] +struct Cli { + #[clap(flatten)] + address: SocketAddrArgs, +} + + +struct ServerInner { + width: u16, + height: u16, +} + +struct Server { + inner: Arc>, +} + +impl Server { + fn new(width: u16, height: u16) -> Self { + Self { + inner: Arc::new(Mutex::new(ServerInner { + width, + height, + })) + } + } + + fn handle_client(&self, stream: TcpStream) -> Result<(), Box> { + stream.set_read_timeout(Some(time::Duration::from_millis(100)))?; + let (mut server, _share) = VncServer::from_tcp_stream( + stream, + self.inner.lock().unwrap().width, + self.inner.lock().unwrap().height, + PixelFormat::rgb8888(), + "qemu-vnc experiment".into(), + )?; + let mut last_update: Option = None; + loop { + let event = match server.read_event() { + 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()); } + }; + match event { + VncEvent::FramebufferUpdateRequest { .. } => { + if let Some(last_update) = last_update { + if last_update.elapsed().as_millis() < 100 { + continue; + } + } + last_update = Some(time::Instant::now()); + let mut fbu = FramebufferUpdate::new(&PixelFormat::rgb8888()); + let pixel_data = vec![128; 8 * 8 * 4]; + let rect = Rect { + left: 0, + top: 0, + width: 8, + height: 8, + }; + fbu.add_raw_pixels(rect, &pixel_data); + server.send(&fbu)?; + } + event => { + dbg!(event); + } + } + } + } +} + +fn main() -> Result<(), Box> { + let args = Cli::parse(); + + let listener = TcpListener::bind::(args.address.into()).unwrap(); + let conn = Connection::new_session().expect("Failed to connect to DBus"); + let console = Console::new(&conn, 0).expect("Failed to get the console"); + let (rx, ack) = console.listen()?; + + let server = Server::new(console.width()? as u16, console.height()? as u16); + + let _thread = thread::spawn(move || { + match rx.recv().unwrap() { + Event::Scanout(s) => { + dbg!(&s); + unsafe { + libc::close(s.fd); + } + let _ = ack.send(()); + }, + e => { dbg!(e); }, + } + }); + for stream in listener.incoming() { + server.handle_client(stream?)?; + } + Ok(()) +}