mirror of
https://github.com/rustdesk/qemu-display.git
synced 2025-08-17 16:25:39 +00:00
qemu-rdw: start new widget based on rdw
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
parent
1906db2a3c
commit
b87f460871
@ -2,10 +2,13 @@
|
|||||||
members = [
|
members = [
|
||||||
"keycodemap",
|
"keycodemap",
|
||||||
"qemu-display-listener",
|
"qemu-display-listener",
|
||||||
|
"qemu-rdw",
|
||||||
"qemu-vnc",
|
"qemu-vnc",
|
||||||
"xtask",
|
"xtask",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
default-members = ["qemu-rdw"]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
zbus = { git = 'https://gitlab.freedesktop.org/dbus/zbus.git' }
|
zbus = { git = 'https://gitlab.freedesktop.org/dbus/zbus.git' }
|
||||||
zvariant = { git = 'https://gitlab.freedesktop.org/dbus/zbus.git' }
|
zvariant = { git = 'https://gitlab.freedesktop.org/dbus/zbus.git' }
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::os::unix::net::UnixStream;
|
use std::os::unix::net::UnixStream;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::mpsc::{self, Receiver, SendError};
|
use std::sync::mpsc::{self, Receiver, SendError};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{os::unix::io::AsRawFd, thread};
|
use std::{os::unix::io::AsRawFd, thread};
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use zbus::{dbus_interface, dbus_proxy, export::zvariant::Fd};
|
use zbus::{dbus_interface, dbus_proxy, export::zvariant::Fd};
|
||||||
|
|
||||||
@ -237,9 +237,13 @@ impl Audio {
|
|||||||
|
|
||||||
pub fn available(conn: &zbus::Connection) -> bool {
|
pub fn available(conn: &zbus::Connection) -> bool {
|
||||||
// TODO: we may want to generalize interface detection
|
// TODO: we may want to generalize interface detection
|
||||||
let ip = zbus::fdo::IntrospectableProxy::new_for(&conn, "org.qemu", "/org/qemu/Display1").unwrap();
|
let ip = zbus::fdo::IntrospectableProxy::new_for(&conn, "org.qemu", "/org/qemu/Display1")
|
||||||
|
.unwrap();
|
||||||
let introspect = zbus::xml::Node::from_str(&ip.introspect().unwrap()).unwrap();
|
let introspect = zbus::xml::Node::from_str(&ip.introspect().unwrap()).unwrap();
|
||||||
let has_audio = introspect.nodes().iter().any(|n| n.name().map(|n| n == "Audio").unwrap_or(false));
|
let has_audio = introspect
|
||||||
|
.nodes()
|
||||||
|
.iter()
|
||||||
|
.any(|n| n.name().map(|n| n == "Audio").unwrap_or(false));
|
||||||
has_audio
|
has_audio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ops::Drop;
|
use std::ops::Drop;
|
||||||
|
use std::os::unix::io::IntoRawFd;
|
||||||
use std::os::unix::io::{AsRawFd, RawFd};
|
use std::os::unix::io::{AsRawFd, RawFd};
|
||||||
use std::sync::mpsc::{Receiver, RecvError, SendError};
|
use std::sync::mpsc::{Receiver, RecvError, SendError};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -54,6 +55,12 @@ impl Drop for ScanoutDMABUF {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoRawFd for ScanoutDMABUF {
|
||||||
|
fn into_raw_fd(mut self) -> RawFd {
|
||||||
|
std::mem::replace(&mut self.fd, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct MouseSet {
|
pub struct MouseSet {
|
||||||
pub x: i32,
|
pub x: i32,
|
||||||
|
BIN
qemu-rdw/.Cargo.toml.swp
Normal file
BIN
qemu-rdw/.Cargo.toml.swp
Normal file
Binary file not shown.
17
qemu-rdw/Cargo.toml
Normal file
17
qemu-rdw/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "qemu-rdw"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Marc-André Lureau <marcandre.lureau@redhat.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
pretty_env_logger = "0.4"
|
||||||
|
once_cell = "1.5"
|
||||||
|
zbus = { version = "2.0.0-beta" }
|
||||||
|
qemu-display-listener = { path = "../qemu-display-listener", features = ["glib"] }
|
||||||
|
keycodemap = { path = "../keycodemap" }
|
||||||
|
gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs" }
|
||||||
|
rdw = { git = "https://gitlab.gnome.org/malureau/rdw.git" }
|
230
qemu-rdw/src/display_qemu.rs
Normal file
230
qemu-rdw/src/display_qemu.rs
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
use glib::{clone, subclass::prelude::*, translate::*};
|
||||||
|
use gtk::{glib, prelude::*};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
|
use keycodemap::KEYMAP_XORGEVDEV2QNUM;
|
||||||
|
use qemu_display_listener::Console;
|
||||||
|
use rdw::DisplayExt;
|
||||||
|
|
||||||
|
mod imp {
|
||||||
|
use super::*;
|
||||||
|
use gtk::subclass::prelude::*;
|
||||||
|
use std::os::unix::io::IntoRawFd;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct RdwDisplayQemuClass {
|
||||||
|
pub parent_class: rdw::imp::RdwDisplayClass,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl ClassStruct for RdwDisplayQemuClass {
|
||||||
|
type Type = DisplayQemu;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 = DisplayQemu;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct DisplayQemu {
|
||||||
|
pub(crate) console: OnceCell<Console>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[glib::object_subclass]
|
||||||
|
impl ObjectSubclass for DisplayQemu {
|
||||||
|
const NAME: &'static str = "RdwDisplayQemu";
|
||||||
|
type Type = super::DisplayQemu;
|
||||||
|
type ParentType = rdw::Display;
|
||||||
|
type Class = RdwDisplayQemuClass;
|
||||||
|
type Instance = RdwDisplayQemu;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectImpl for DisplayQemu {
|
||||||
|
fn constructed(&self, obj: &Self::Type) {
|
||||||
|
self.parent_constructed(obj);
|
||||||
|
|
||||||
|
obj.set_mouse_absolute(true);
|
||||||
|
|
||||||
|
obj.connect_key_press(clone!(@weak obj => move |_, keyval, keycode| {
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("key-press: {:?}", (keyval, keycode));
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
if let Some(qnum) = KEYMAP_XORGEVDEV2QNUM.get(keycode as usize) {
|
||||||
|
let _ = console.keyboard.press(*qnum as u32);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_key_release(clone!(@weak obj => move |_, keyval, keycode| {
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("key-release: {:?}", (keyval, keycode));
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
if let Some(qnum) = KEYMAP_XORGEVDEV2QNUM.get(keycode as usize) {
|
||||||
|
let _ = console.keyboard.release(*qnum as u32);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_motion(clone!(@weak obj => move |_, x, y| {
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("motion: {:?}", (x, y));
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
let _ = console.mouse.set_abs_position(x as _, y as _);
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_motion_relative(clone!(@weak obj => move |_, dx, dy| {
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("motion-relative: {:?}", (dx, dy));
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
let _ = console.mouse.rel_motion(dx as _, dy as _);
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_mouse_press(clone!(@weak obj => move |_, button| {
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("mouse-press: {:?}", button);
|
||||||
|
let button = from_gdk_button(button);
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
let _ = console.mouse.press(button);
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_mouse_release(clone!(@weak obj => move |_, button| {
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("mouse-release: {:?}", button);
|
||||||
|
let button = from_gdk_button(button);
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
let _ = console.mouse.release(button);
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_scroll_discrete(clone!(@weak obj => move |_, scroll| {
|
||||||
|
use qemu_display_listener::MouseButton;
|
||||||
|
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("scroll-discrete: {:?}", scroll);
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
|
||||||
|
let button = match scroll {
|
||||||
|
rdw::Scroll::Up => MouseButton::WheelUp,
|
||||||
|
rdw::Scroll::Down => MouseButton::WheelDown,
|
||||||
|
_ => {
|
||||||
|
log::warn!("not yet implemented");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let _ = console.mouse.press(button);
|
||||||
|
let _ = console.mouse.release(button);
|
||||||
|
}));
|
||||||
|
|
||||||
|
obj.connect_resize_request(clone!(@weak obj => move |_, width, height, wmm, hmm| {
|
||||||
|
let self_ = Self::from_instance(&obj);
|
||||||
|
log::debug!("resize-request: {:?}", (width, height, wmm, hmm));
|
||||||
|
let console = self_.console.get().unwrap();
|
||||||
|
let _ = console.proxy.set_ui_info(wmm as _, hmm as _, 0, 0, width, height);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WidgetImpl for DisplayQemu {
|
||||||
|
fn realize(&self, widget: &Self::Type) {
|
||||||
|
self.parent_realize(widget);
|
||||||
|
|
||||||
|
let console = self.console.get().unwrap();
|
||||||
|
let (rx, wait_tx) = console
|
||||||
|
.glib_listen()
|
||||||
|
.expect("Failed to listen to the console");
|
||||||
|
rx.attach(
|
||||||
|
None,
|
||||||
|
clone!(@weak widget => @default-panic, move |evt| {
|
||||||
|
use qemu_display_listener::ConsoleEvent::*;
|
||||||
|
|
||||||
|
let self_ = Self::from_instance(&widget);
|
||||||
|
log::debug!("Console event: {:?}", evt);
|
||||||
|
match evt {
|
||||||
|
Scanout(s) => {
|
||||||
|
}
|
||||||
|
Update(u) => {
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl rdw::DisplayImpl for DisplayQemu {}
|
||||||
|
|
||||||
|
impl DisplayQemu {
|
||||||
|
pub(crate) fn set_console(&self, console: Console) {
|
||||||
|
self.console.set(console).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glib::wrapper! {
|
||||||
|
pub struct DisplayQemu(ObjectSubclass<imp::DisplayQemu>) @extends rdw::Display, gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DisplayQemu {
|
||||||
|
pub fn new(console: Console) -> Self {
|
||||||
|
let obj = glib::Object::new::<Self>(&[]).unwrap();
|
||||||
|
let self_ = imp::DisplayQemu::from_instance(&obj);
|
||||||
|
self_.set_console(console);
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_gdk_button(button: u32) -> qemu_display_listener::MouseButton {
|
||||||
|
use qemu_display_listener::MouseButton::*;
|
||||||
|
|
||||||
|
match button {
|
||||||
|
1 => Left,
|
||||||
|
2 => Middle,
|
||||||
|
3 => Right,
|
||||||
|
_ => Extra,
|
||||||
|
}
|
||||||
|
}
|
29
qemu-rdw/src/main.rs
Normal file
29
qemu-rdw/src/main.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use gio::ApplicationFlags;
|
||||||
|
use gtk::{gio, prelude::*};
|
||||||
|
use qemu_display_listener::Console;
|
||||||
|
use zbus::Connection;
|
||||||
|
|
||||||
|
mod display_qemu;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
pretty_env_logger::init();
|
||||||
|
|
||||||
|
let app = gtk::Application::new(Some("org.qemu.rdw.demo"), ApplicationFlags::NON_UNIQUE);
|
||||||
|
|
||||||
|
let conn = Connection::new_session().expect("Failed to connect to DBus");
|
||||||
|
|
||||||
|
app.connect_activate(move |app| {
|
||||||
|
let window = gtk::ApplicationWindow::new(app);
|
||||||
|
|
||||||
|
window.set_title(Some("rdw demo"));
|
||||||
|
window.set_default_size(1024, 768);
|
||||||
|
|
||||||
|
let console = Console::new(&conn, 0).expect("Failed to get the QEMU console");
|
||||||
|
let display = display_qemu::DisplayQemu::new(console);
|
||||||
|
window.set_child(Some(&display));
|
||||||
|
|
||||||
|
window.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.run();
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user