gtk: draw client cursor

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2021-03-30 16:01:44 +04:00
parent ced718c264
commit 609a0732b4
5 changed files with 86 additions and 13 deletions

View File

@ -54,6 +54,13 @@ impl Drop for ScanoutDMABUF {
} }
} }
#[derive(Debug, Copy, Clone)]
pub struct MouseSet {
pub x: i32,
pub y: i32,
pub on: i32,
}
// TODO: replace events mpsc with async traits // TODO: replace events mpsc with async traits
#[derive(Debug)] #[derive(Debug)]
pub enum ConsoleEvent { pub enum ConsoleEvent {
@ -66,11 +73,7 @@ pub enum ConsoleEvent {
w: i32, w: i32,
h: i32, h: i32,
}, },
MouseSet { MouseSet(MouseSet),
x: i32,
y: i32,
on: i32,
},
CursorDefine { CursorDefine {
width: i32, width: i32,
height: i32, height: i32,
@ -160,7 +163,7 @@ impl<E: 'static + EventSender<Event = ConsoleEvent>> ConsoleListener<E> {
} }
fn mouse_set(&mut self, x: i32, y: i32, on: i32) { fn mouse_set(&mut self, x: i32, y: i32, on: i32) {
self.send(ConsoleEvent::MouseSet { x, y, on }) self.send(ConsoleEvent::MouseSet(MouseSet { x, y, on }))
} }
fn cursor_define(&mut self, width: i32, height: i32, hot_x: i32, hot_y: i32, data: Vec<u8>) { fn cursor_define(&mut self, width: i32, height: i32, hot_x: i32, hot_y: i32, data: Vec<u8>) {

View File

@ -24,4 +24,7 @@ pub trait Mouse {
/// SetAbsPosition method /// SetAbsPosition method
fn set_abs_position(&self, x: u32, y: u32) -> zbus::Result<()>; fn set_abs_position(&self, x: u32, y: u32) -> zbus::Result<()>;
#[dbus_proxy(property)]
fn is_absolute(&self) -> zbus::Result<bool>;
} }

View File

@ -75,7 +75,14 @@ mod imp {
self.area.add_controller(&ec); self.area.add_controller(&ec);
ec.connect_motion(clone!(@weak obj => move |_, x, y| { ec.connect_motion(clone!(@weak obj => move |_, x, y| {
let priv_ = imp::QemuConsole::from_instance(&obj); let priv_ = imp::QemuConsole::from_instance(&obj);
let c = obj.qemu_console();
if let Ok(abs) = c.mouse.is_absolute() {
if abs {
priv_.motion(x, y); priv_.motion(x, y);
} else {
dbg!()
}
}
})); }));
let ec = gtk::GestureClick::new(); let ec = gtk::GestureClick::new();
@ -284,9 +291,16 @@ mod imp {
let pb = pb.scale_simple(width * scale, height * scale, gdk::gdk_pixbuf::InterpType::Bilinear).unwrap(); let pb = pb.scale_simple(width * scale, height * scale, gdk::gdk_pixbuf::InterpType::Bilinear).unwrap();
let tex = gdk::Texture::new_for_pixbuf(&pb); let tex = gdk::Texture::new_for_pixbuf(&pb);
let cur = gdk::Cursor::from_texture(&tex, hot_x * scale, hot_y * scale, None); let cur = gdk::Cursor::from_texture(&tex, hot_x * scale, hot_y * scale, None);
priv_.area.set_cursor(Some(&cur)); priv_.area.cursor_define(cur);
}
Event::MouseSet(m) => {
priv_.area.mouse_set(m);
let c = obj.qemu_console();
if let Ok(abs) = c.mouse.is_absolute() {
priv_.area.set_cursor_abs(abs);
}
priv_.area.queue_render();
} }
_t => { }
} }
Continue(true) Continue(true)
}), }),

View File

@ -2,14 +2,14 @@ use glib::subclass::prelude::*;
use glib::translate::*; use glib::translate::*;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::subclass::widget::WidgetImplExt; use gtk::subclass::widget::WidgetImplExt;
use gtk::{gdk, glib}; use gtk::{gdk, glib, graphene};
use std::cell::Cell; use std::cell::{Cell, RefCell};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use crate::egl; use crate::egl;
use crate::error::*; use crate::error::*;
use gl::{self, types::*}; use gl::{self, types::*};
use qemu_display_listener::{Scanout, ScanoutDMABUF, Update}; use qemu_display_listener::{MouseSet, Scanout, ScanoutDMABUF, Update};
mod imp { mod imp {
use super::*; use super::*;
@ -23,6 +23,9 @@ mod imp {
pub texture_blit_flip_prog: Cell<GLuint>, pub texture_blit_flip_prog: Cell<GLuint>,
pub scanout: Cell<Option<ScanoutDMABUF>>, pub scanout: Cell<Option<ScanoutDMABUF>>,
pub scanout_size: Cell<(u32, u32)>, pub scanout_size: Cell<(u32, u32)>,
pub cursor_abs: Cell<bool>,
pub cursor: RefCell<Option<gdk::Cursor>>,
pub mouse: Cell<Option<MouseSet>>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -91,6 +94,31 @@ mod imp {
self.parent_size_allocate(widget, width, height, baseline); self.parent_size_allocate(widget, width, height, baseline);
widget.notify("resize-hack"); widget.notify("resize-hack");
} }
fn snapshot(&self, widget: &Self::Type, snapshot: &gtk::Snapshot) {
self.parent_snapshot(widget, snapshot);
if !self.cursor_abs.get() {
if let Some(mouse) = self.mouse.get() {
if mouse.on != 0 {
if let Some(cursor) = self.cursor.borrow().clone() {
if let Some(texture) = cursor.get_texture() {
let sf = widget.get_scale_factor();
snapshot.append_texture(
&texture,
&graphene::Rect::new(
(mouse.x - cursor.get_hotspot_x() / sf) as f32,
(mouse.y - cursor.get_hotspot_y() / sf) as f32,
(texture.get_width() / sf) as f32,
(texture.get_height() / sf) as f32,
),
)
}
}
}
}
}
}
} }
impl GLAreaImpl for QemuConsoleArea { impl GLAreaImpl for QemuConsoleArea {
@ -104,6 +132,7 @@ mod imp {
gl::Viewport(vp.x, vp.y, vp.width, vp.height); gl::Viewport(vp.x, vp.y, vp.width, vp.height);
self.texture_blit(false); self.texture_blit(false);
} }
// parent will return to update call // parent will return to update call
false false
} }
@ -383,6 +412,30 @@ impl QemuConsoleArea {
let y = (y - vp.y) as f64 * (sh as f64 / vp.height as f64); let y = (y - vp.y) as f64 * (sh as f64 / vp.height as f64);
Some((x as u32, y as u32)) Some((x as u32, y as u32))
} }
pub fn set_cursor_abs(&self, abs: bool) {
let priv_ = imp::QemuConsoleArea::from_instance(self);
priv_.cursor_abs.set(abs);
if abs {
if let Some(cursor) = priv_.cursor.borrow().clone() {
self.set_cursor(Some(&cursor));
}
} else {
self.set_cursor_from_name(Some("none"))
}
self.queue_render();
}
pub fn cursor_define(&self, cursor: gdk::Cursor) {
let priv_ = imp::QemuConsoleArea::from_instance(self);
priv_.cursor.replace(Some(cursor));
}
pub fn mouse_set(&self, mouse: MouseSet) {
let priv_ = imp::QemuConsoleArea::from_instance(self);
priv_.mouse.set(Some(mouse));
}
} }
unsafe fn compile_shader(type_: GLenum, src: &CStr) -> GLuint { unsafe fn compile_shader(type_: GLenum, src: &CStr) -> GLuint {

View File

@ -316,7 +316,7 @@ impl Server {
inner.tx.send(Event::ConsoleUpdate(rect)).unwrap(); inner.tx.send(Event::ConsoleUpdate(rect)).unwrap();
} }
ConsoleEvent::CursorDefine { .. } => {} ConsoleEvent::CursorDefine { .. } => {}
ConsoleEvent::MouseSet { .. } => {} ConsoleEvent::MouseSet(_) => {}
e => { e => {
dbg!(e); dbg!(e);
} }