From f6bbaf0b2dcdd030bf68c6c080947762a19a1157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Fri, 26 Mar 2021 01:44:09 +0400 Subject: [PATCH] gtk: some refactoring --- .cargo/config | 2 + qemu-gtk4/src/console.rs | 191 ++++++++++++++++++++++----------------- 2 files changed, 109 insertions(+), 84 deletions(-) diff --git a/.cargo/config b/.cargo/config index 35049cb..96178ba 100644 --- a/.cargo/config +++ b/.cargo/config @@ -1,2 +1,4 @@ +paths = ['../gtk4-rs/gtk4'] + [alias] xtask = "run --package xtask --" diff --git a/qemu-gtk4/src/console.rs b/qemu-gtk4/src/console.rs index 7ebe6e6..5de9f5d 100644 --- a/qemu-gtk4/src/console.rs +++ b/qemu-gtk4/src/console.rs @@ -74,20 +74,21 @@ mod imp { let ec = gtk::EventControllerMotion::new(); self.area.add_controller(&ec); ec.connect_motion(clone!(@weak obj => move |_, x, y| { - obj.motion(x, y); + let priv_ = imp::QemuConsole::from_instance(&obj); + priv_.motion(x, y); })); let ec = gtk::GestureClick::new(); ec.set_button(0); self.area.add_controller(&ec); ec.connect_pressed(clone!(@weak obj => @default-panic, move |gesture, _n_press, x, y| { + let priv_ = imp::QemuConsole::from_instance(&obj); let c = obj.qemu_console(); let button = from_gdk_button(gesture.get_current_button()); - obj.motion(x, y); + priv_.motion(x, y); let _ = c.mouse.press(button); - let priv_ = imp::QemuConsole::from_instance(&obj); - if let Some(toplevel) = obj.get_toplevel() { + if let Some(toplevel) = priv_.get_toplevel() { if !toplevel.get_property_shortcuts_inhibited() { toplevel.inhibit_system_shortcuts::(None); @@ -135,9 +136,11 @@ mod imp { priv_.area.grab_focus(); })); ec.connect_released(clone!(@weak obj => move |gesture, _n_press, x, y| { + let priv_ = imp::QemuConsole::from_instance(&obj); let c = obj.qemu_console(); let button = from_gdk_button(gesture.get_current_button()); - obj.motion(x, y); + + priv_.motion(x, y); let _ = c.mouse.release(button); })); @@ -164,6 +167,17 @@ mod imp { self.area.set_focusable(true); self.area.set_focus_on_click(true); + self.area + .connect_create_context(clone!(@weak obj => @default-panic, move |_| { + // can't connect-after create-context yet, so idle it + glib::idle_add_local(clone!(@weak ec => @default-panic, move || { + let priv_ = imp::QemuConsole::from_instance(&obj); + priv_.attach_qemu_console(&obj); + glib::Continue(false) + })); + None + })); + unsafe { self.area.connect_notify_unsafe( Some("resize-hack"), @@ -189,6 +203,93 @@ mod imp { } impl WidgetImpl for QemuConsole {} + + impl QemuConsole { + fn get_toplevel(&self) -> Option { + self.area + .get_root() + .and_then(|r| r.get_native()) + .and_then(|n| n.get_surface()) + .and_then(|s| s.downcast::().ok()) + } + + fn motion(&self, x: f64, y: f64) { + if let Some((x, y)) = self.area.transform_input(x, y) { + let c = self.console.get().unwrap(); + let _ = c.mouse.set_abs_position(x, y); + } + } + + pub(crate) fn attach_qemu_console(&self, obj: &super::QemuConsole) { + let console = match self.console.get() { + Some(console) => console, + None => return, + }; + if !obj.get_realized() { + return; + } + + let (rx, wait_tx) = console + .glib_listen() + .expect("Failed to listen to the console"); + self.area + .connect_render(clone!(@weak obj => @default-panic, move |_, _| { + let priv_ = imp::QemuConsole::from_instance(&obj); + let wait_rendering = priv_.wait_rendering.get(); + if wait_rendering > 0 { + if let Err(e) = wait_tx.send(()) { + eprintln!("Failed to ack rendering: {}", e); + } + priv_.wait_rendering.set(wait_rendering - 1); + } + glib::signal::Inhibit(false) + })); + rx.attach( + None, + clone!(@weak obj => @default-panic, move |t| { + let priv_ = imp::QemuConsole::from_instance(&obj); + debug!("Console event: {:?}", t); + match t { + 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(); + priv_.area.attach_buffers(); + let _ = unsafe { + glib::Object::from_glib_borrow(priv_.area.as_ptr() as *mut glib::gobject_ffi::GObject) + .emit_by_name("render", &[&priv_.area.get_context().as_ref()]) + .unwrap() + }; + priv_.area.queue_draw(); + } + Event::Disconnected => { + priv_.label.set_label("Console disconnected!"); + } + Event::CursorDefine { width, height, hot_x, hot_y, data }=> { + let bytes = glib::Bytes::from(&data); + let tex = gdk::MemoryTexture::new(width, height, gdk::MemoryFormat::B8g8r8a8, &bytes, width as usize * 4); + let cur = gdk::Cursor::from_texture(&tex, hot_x, hot_y, None); + priv_.area.set_cursor(Some(&cur)); + } + _t => { } + } + Continue(true) + }), + ); + } + } } glib::wrapper! { @@ -198,92 +299,14 @@ glib::wrapper! { impl QemuConsole { pub fn set_qemu_console(&self, console: Console) { let priv_ = imp::QemuConsole::from_instance(self); - let (rx, wait_tx) = console - .glib_listen() - .expect("Failed to listen to the console"); - priv_ - .area - .connect_render(clone!(@weak self as obj => @default-panic, move |_, _| { - let priv_ = imp::QemuConsole::from_instance(&obj); - let wait_rendering = priv_.wait_rendering.get(); - if wait_rendering > 0 { - if let Err(e) = wait_tx.send(()) { - eprintln!("Failed to ack rendering: {}", e); - } - priv_.wait_rendering.set(wait_rendering - 1); - } - glib::signal::Inhibit(false) - })); - rx.attach( - None, - clone!(@weak self as con => @default-panic, move |t| { - let priv_ = imp::QemuConsole::from_instance(&con); - debug!("Console event: {:?}", t); - match t { - 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(); - priv_.area.attach_buffers(); - let _ = unsafe { - glib::Object::from_glib_borrow(priv_.area.as_ptr() as *mut glib::gobject_ffi::GObject) - .emit_by_name("render", &[&priv_.area.get_context().as_ref()]) - .unwrap() - }; - priv_.area.queue_draw(); - } - Event::Disconnected => { - priv_.label.set_label("Console disconnected!"); - } - Event::CursorDefine { width, height, hot_x, hot_y, data }=> { - let bytes = glib::Bytes::from(&data); - let tex = gdk::MemoryTexture::new(width, height, gdk::MemoryFormat::B8g8r8a8, &bytes, width as usize * 4); - let cur = gdk::Cursor::from_texture(&tex, hot_x, hot_y, None); - priv_.area.set_cursor(Some(&cur)); - } - _t => { } - } - Continue(true) - }), - ); priv_.console.set(console).unwrap(); - } - - fn get_toplevel(&self) -> Option { - let priv_ = imp::QemuConsole::from_instance(self); - priv_ - .area - .get_root() - .and_then(|r| r.get_native()) - .and_then(|n| n.get_surface()) - .and_then(|s| s.downcast::().ok()) + priv_.attach_qemu_console(self); } fn qemu_console(&self) -> &Console { let priv_ = imp::QemuConsole::from_instance(self); priv_.console.get().expect("Console is not yet set!") } - - fn motion(&self, x: f64, y: f64) { - let priv_ = imp::QemuConsole::from_instance(self); - - if let Some((x, y)) = priv_.area.transform_input(x, y) { - let c = self.qemu_console(); - let _ = c.mouse.set_abs_position(x, y); - } - } } fn from_gdk_button(button: u32) -> MouseButton {