mirror of
https://github.com/rustdesk/qemu-display.git
synced 2025-07-01 15:25:29 +00:00
268 lines
10 KiB
Rust
268 lines
10 KiB
Rust
use glib::{clone, subclass::prelude::*, MainContext};
|
|
use gtk::{glib, prelude::*};
|
|
use once_cell::sync::OnceCell;
|
|
|
|
use keycodemap::KEYMAP_XORGEVDEV2QNUM;
|
|
use qemu_display::Console;
|
|
use rdw::DisplayExt;
|
|
|
|
mod imp {
|
|
use super::*;
|
|
use gtk::subclass::prelude::*;
|
|
use std::{convert::TryInto, os::unix::io::IntoRawFd};
|
|
|
|
#[repr(C)]
|
|
pub struct RdwDisplayQemuClass {
|
|
pub parent_class: rdw::imp::RdwDisplayClass,
|
|
}
|
|
|
|
unsafe impl ClassStruct for RdwDisplayQemuClass {
|
|
type Type = Display;
|
|
}
|
|
|
|
#[repr(C)]
|
|
pub struct RdwDisplayQemu {
|
|
parent: rdw::imp::RdwDisplay,
|
|
}
|
|
|
|
impl std::fmt::Debug for RdwDisplayQemu {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
f.debug_struct("RdwDisplayQemu")
|
|
.field("parent", &self.parent)
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
unsafe impl InstanceStruct for RdwDisplayQemu {
|
|
type Type = Display;
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct Display {
|
|
pub(crate) console: OnceCell<Console>,
|
|
}
|
|
|
|
#[glib::object_subclass]
|
|
impl ObjectSubclass for Display {
|
|
const NAME: &'static str = "RdwDisplayQemu";
|
|
type Type = super::Display;
|
|
type ParentType = rdw::Display;
|
|
type Class = RdwDisplayQemuClass;
|
|
type Instance = RdwDisplayQemu;
|
|
}
|
|
|
|
impl ObjectImpl for Display {
|
|
fn constructed(&self, obj: &Self::Type) {
|
|
self.parent_constructed(obj);
|
|
|
|
obj.set_mouse_absolute(true);
|
|
|
|
obj.connect_key_event(clone!(@weak obj => move |_, keyval, keycode, event| {
|
|
log::debug!("key-event: {:?}", (keyval, keycode, event));
|
|
if let Some(qnum) = KEYMAP_XORGEVDEV2QNUM.get(keycode as usize) {
|
|
MainContext::default().spawn_local(clone!(@weak obj => async move {
|
|
if event.contains(rdw::KeyEvent::PRESS) {
|
|
let _ = obj.console().keyboard.press(*qnum as u32).await;
|
|
}
|
|
if event.contains(rdw::KeyEvent::RELEASE) {
|
|
let _ = obj.console().keyboard.release(*qnum as u32).await;
|
|
}
|
|
}));
|
|
}
|
|
}));
|
|
|
|
obj.connect_motion(clone!(@weak obj => move |_, x, y| {
|
|
log::debug!("motion: {:?}", (x, y));
|
|
MainContext::default().spawn_local(clone!(@weak obj => async move {
|
|
let _ = obj.console().mouse.set_abs_position(x as _, y as _).await;
|
|
}));
|
|
}));
|
|
|
|
obj.connect_motion_relative(clone!(@weak obj => move |_, dx, dy| {
|
|
log::debug!("motion-relative: {:?}", (dx, dy));
|
|
MainContext::default().spawn_local(clone!(@weak obj => async move {
|
|
let _ = obj.console().mouse.rel_motion(dx as _, dy as _).await;
|
|
}));
|
|
}));
|
|
|
|
obj.connect_mouse_press(clone!(@weak obj => move |_, button| {
|
|
log::debug!("mouse-press: {:?}", button);
|
|
MainContext::default().spawn_local(clone!(@weak obj => async move {
|
|
let button = from_gdk_button(button);
|
|
let _ = obj.console().mouse.press(button).await;
|
|
}));
|
|
}));
|
|
|
|
obj.connect_mouse_release(clone!(@weak obj => move |_, button| {
|
|
log::debug!("mouse-release: {:?}", button);
|
|
MainContext::default().spawn_local(clone!(@weak obj => async move {
|
|
let button = from_gdk_button(button);
|
|
let _ = obj.console().mouse.release(button).await;
|
|
}));
|
|
}));
|
|
|
|
obj.connect_scroll_discrete(clone!(@weak obj => move |_, scroll| {
|
|
use qemu_display::MouseButton;
|
|
|
|
log::debug!("scroll-discrete: {:?}", scroll);
|
|
|
|
let button = match scroll {
|
|
rdw::Scroll::Up => MouseButton::WheelUp,
|
|
rdw::Scroll::Down => MouseButton::WheelDown,
|
|
_ => {
|
|
log::warn!("not yet implemented");
|
|
return;
|
|
}
|
|
};
|
|
MainContext::default().spawn_local(clone!(@weak obj => async move {
|
|
let _ = obj.console().mouse.press(button).await;
|
|
let _ = obj.console().mouse.release(button).await;
|
|
}));
|
|
}));
|
|
|
|
obj.connect_resize_request(clone!(@weak obj => move |_, width, height, wmm, hmm| {
|
|
log::debug!("resize-request: {:?}", (width, height, wmm, hmm));
|
|
MainContext::default().spawn_local(clone!(@weak obj => async move {
|
|
let _ = obj.console().proxy.set_ui_info(wmm as _, hmm as _, 0, 0, width, height).await;
|
|
}));
|
|
}));
|
|
}
|
|
}
|
|
|
|
impl WidgetImpl for Display {
|
|
fn realize(&self, widget: &Self::Type) {
|
|
self.parent_realize(widget);
|
|
|
|
MainContext::default().spawn_local(clone!(@weak widget => async move {
|
|
let self_ = Self::from_instance(&widget);
|
|
let console = self_.console.get().unwrap();
|
|
|
|
let (rx, wait_tx) = console
|
|
.glib_listen()
|
|
.await
|
|
.expect("Failed to listen to the console");
|
|
rx.attach(
|
|
None,
|
|
clone!(@weak widget => @default-panic, move |evt| {
|
|
use qemu_display::ConsoleEvent::*;
|
|
|
|
log::debug!("Console event: {:?}", evt);
|
|
match evt {
|
|
Scanout(s) => {
|
|
if s.format != 0x20020888 {
|
|
log::warn!("Format not yet supported: {:X}", s.format);
|
|
return Continue(true);
|
|
}
|
|
widget.set_display_size(Some((s.width as _, s.height as _)));
|
|
widget.update_area(0, 0, s.width as _, s.height as _, s.stride as _, &s.data);
|
|
}
|
|
Update(u) => {
|
|
if u.format != 0x20020888 {
|
|
log::warn!("Format not yet supported: {:X}", u.format);
|
|
return Continue(true);
|
|
}
|
|
widget.update_area(u.x as _, u.y as _, u.w as _, u.h as _, u.stride as _, &u.data);
|
|
}
|
|
ScanoutDMABUF(s) => {
|
|
widget.set_display_size(Some((s.width as _, s.height as _)));
|
|
widget.set_dmabuf_scanout(rdw::DmabufScanout {
|
|
width: s.width,
|
|
height: s.height,
|
|
stride: s.stride,
|
|
fourcc: s.fourcc,
|
|
y0_top: s.y0_top,
|
|
modifier: s.modifier,
|
|
fd: s.into_raw_fd(),
|
|
});
|
|
}
|
|
UpdateDMABUF { .. } => {
|
|
widget.render();
|
|
let _ = wait_tx.send(());
|
|
}
|
|
Disconnected => {
|
|
}
|
|
CursorDefine { width, height, hot_x, hot_y, data }=> {
|
|
let cursor = rdw::Display::make_cursor(
|
|
&data,
|
|
width,
|
|
height,
|
|
hot_x,
|
|
hot_y,
|
|
1,
|
|
);
|
|
widget.define_cursor(Some(cursor));
|
|
}
|
|
MouseSet(m) => {
|
|
if m.on != 0 {
|
|
widget.set_cursor_position(Some((m.x as _, m.y as _)));
|
|
} else {
|
|
widget.set_cursor_position(None);
|
|
}
|
|
}
|
|
}
|
|
Continue(true)
|
|
})
|
|
);
|
|
|
|
let mut abs_changed = console.mouse.receive_is_absolute_changed().await;
|
|
MainContext::default().spawn_local(clone!(@weak widget => async move {
|
|
use futures_util::StreamExt;
|
|
|
|
while let Some(abs) = abs_changed.next().await {
|
|
let abs = if let Some(abs) = abs {
|
|
abs.try_into().unwrap_or(false)
|
|
} else {
|
|
continue;
|
|
};
|
|
widget.set_mouse_absolute(abs);
|
|
}
|
|
}));
|
|
|
|
loop {
|
|
if let Err(e) = console.dispatch_signals().await {
|
|
log::warn!("Console dispatching error: {}", e);
|
|
break;
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
impl rdw::DisplayImpl for Display {}
|
|
|
|
impl Display {
|
|
pub(crate) fn set_console(&self, console: Console) {
|
|
self.console.set(console).unwrap();
|
|
}
|
|
}
|
|
}
|
|
|
|
glib::wrapper! {
|
|
pub struct Display(ObjectSubclass<imp::Display>) @extends rdw::Display, gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
|
}
|
|
|
|
impl Display {
|
|
pub fn new(console: Console) -> Self {
|
|
let obj = glib::Object::new::<Self>(&[]).unwrap();
|
|
let self_ = imp::Display::from_instance(&obj);
|
|
self_.set_console(console);
|
|
obj
|
|
}
|
|
|
|
pub(crate) fn console(&self) -> &Console {
|
|
let self_ = imp::Display::from_instance(self);
|
|
self_.console.get().unwrap()
|
|
}
|
|
}
|
|
|
|
fn from_gdk_button(button: u32) -> qemu_display::MouseButton {
|
|
use qemu_display::MouseButton::*;
|
|
|
|
match button {
|
|
1 => Left,
|
|
2 => Middle,
|
|
3 => Right,
|
|
_ => Extra,
|
|
}
|
|
}
|