use hbb_common::{tokio, ResultType}; use image::GenericImage; use qemu_display::{Console, ConsoleListenerHandler, MouseButton}; use std::{collections::HashSet, sync::Arc}; pub use tokio::sync::{mpsc, Mutex}; #[derive(Debug)] pub enum Event { ConsoleUpdate((i32, i32, i32, i32)), Disconnected, } const PIXMAN_X8R8G8B8: u32 = 0x20020888; pub type BgraImage = image::ImageBuffer, Vec>; #[derive(Debug)] pub struct ConsoleListener { pub image: Arc>, pub tx: mpsc::UnboundedSender, } #[async_trait::async_trait] impl ConsoleListenerHandler for ConsoleListener { async fn scanout(&mut self, s: qemu_display::Scanout) { *self.image.lock().await = image_from_vec(s.format, s.width, s.height, s.stride, s.data); } async fn update(&mut self, u: qemu_display::Update) { let update = image_from_vec(u.format, u.w as _, u.h as _, u.stride, u.data); let mut image = self.image.lock().await; if (u.x, u.y) == (0, 0) && update.dimensions() == image.dimensions() { *image = update; } else { image.copy_from(&update, u.x as _, u.y as _).unwrap(); } self.tx .send(Event::ConsoleUpdate((u.x, u.y, u.w, u.h))) .ok(); } async fn scanout_dmabuf(&mut self, _scanout: qemu_display::ScanoutDMABUF) { unimplemented!() } async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) { unimplemented!() } async fn mouse_set(&mut self, set: qemu_display::MouseSet) { dbg!(set); } async fn cursor_define(&mut self, cursor: qemu_display::Cursor) { dbg!(cursor); } fn disconnected(&mut self) { self.tx.send(Event::Disconnected).ok(); } } pub async fn key_event(console: &mut Console, qnum: u32, down: bool) -> ResultType<()> { if down { console.keyboard.press(qnum).await?; } else { console.keyboard.release(qnum).await?; } Ok(()) } fn image_from_vec(format: u32, width: u32, height: u32, stride: u32, data: Vec) -> BgraImage { if format != PIXMAN_X8R8G8B8 { todo!("unhandled pixman format: {}", format) } if cfg!(target_endian = "big") { todo!("pixman/image in big endian") } let layout = image::flat::SampleLayout { channels: 4, channel_stride: 1, width, width_stride: 4, height, height_stride: stride as _, }; let samples = image::flat::FlatSamples { samples: data, layout, color_hint: None, }; samples .try_into_buffer::>() .or_else::<&str, _>(|(_err, samples)| { let view = samples.as_view::>().unwrap(); let mut img = BgraImage::new(width, height); img.copy_from(&view, 0, 0).unwrap(); Ok(img) }) .unwrap() } fn button_mask_to_set(mask: u8) -> HashSet { let mut set = HashSet::new(); if mask & 0b0000_0001 != 0 { set.insert(MouseButton::Left); } if mask & 0b0000_0010 != 0 { set.insert(MouseButton::Middle); } if mask & 0b0000_0100 != 0 { set.insert(MouseButton::Right); } if mask & 0b0000_1000 != 0 { set.insert(MouseButton::WheelUp); } if mask & 0b0001_0000 != 0 { set.insert(MouseButton::WheelDown); } set }