mirror of
https://github.com/rustdesk/qemu-display.git
synced 2026-10-26 06:00:44 +00:00
Adding usbredir to the mix
This commit is contained in:
1 parent
458d9887d5
commit
b5bb98ad0e
10 files changed
+512
-88
No files matched your search
@@ -17,9 +17,7 @@ pub struct Handler {
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub async fn new(conn: &zbus::azync::Connection) -> Result<Self, Box<dyn Error>> {
|
||||
let ctxt = Clipboard::new(conn).await?;
|
||||
|
||||
pub async fn new(ctxt: Clipboard) -> Result<Self, Box<dyn Error>> {
|
||||
let rx = ctxt
|
||||
.glib_listen()
|
||||
.await
|
||||
|
||||
@@ -17,7 +17,7 @@ mod imp {
|
||||
}
|
||||
|
||||
unsafe impl ClassStruct for RdwDisplayQemuClass {
|
||||
type Type = DisplayQemu;
|
||||
type Type = Display;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
@@ -34,24 +34,24 @@ mod imp {
|
||||
}
|
||||
|
||||
unsafe impl InstanceStruct for RdwDisplayQemu {
|
||||
type Type = DisplayQemu;
|
||||
type Type = Display;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DisplayQemu {
|
||||
pub struct Display {
|
||||
pub(crate) console: OnceCell<Console>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for DisplayQemu {
|
||||
impl ObjectSubclass for Display {
|
||||
const NAME: &'static str = "RdwDisplayQemu";
|
||||
type Type = super::DisplayQemu;
|
||||
type Type = super::Display;
|
||||
type ParentType = rdw::Display;
|
||||
type Class = RdwDisplayQemuClass;
|
||||
type Instance = RdwDisplayQemu;
|
||||
}
|
||||
|
||||
impl ObjectImpl for DisplayQemu {
|
||||
impl ObjectImpl for Display {
|
||||
fn constructed(&self, obj: &Self::Type) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
@@ -129,7 +129,7 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for DisplayQemu {
|
||||
impl WidgetImpl for Display {
|
||||
fn realize(&self, widget: &Self::Type) {
|
||||
self.parent_realize(widget);
|
||||
|
||||
@@ -228,9 +228,9 @@ mod imp {
|
||||
}
|
||||
}
|
||||
|
||||
impl rdw::DisplayImpl for DisplayQemu {}
|
||||
impl rdw::DisplayImpl for Display {}
|
||||
|
||||
impl DisplayQemu {
|
||||
impl Display {
|
||||
pub(crate) fn set_console(&self, console: Console) {
|
||||
self.console.set(console).unwrap();
|
||||
}
|
||||
@@ -238,19 +238,19 @@ mod imp {
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct DisplayQemu(ObjectSubclass<imp::DisplayQemu>) @extends rdw::Display, gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
||||
pub struct Display(ObjectSubclass<imp::Display>) @extends rdw::Display, gtk::Widget, @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
|
||||
}
|
||||
|
||||
impl DisplayQemu {
|
||||
impl Display {
|
||||
pub fn new(console: Console) -> Self {
|
||||
let obj = glib::Object::new::<Self>(&[]).unwrap();
|
||||
let self_ = imp::DisplayQemu::from_instance(&obj);
|
||||
let self_ = imp::Display::from_instance(&obj);
|
||||
self_.set_console(console);
|
||||
obj
|
||||
}
|
||||
|
||||
pub(crate) fn console(&self) -> &Console {
|
||||
let self_ = imp::DisplayQemu::from_instance(self);
|
||||
let self_ = imp::Display::from_instance(self);
|
||||
self_.console.get().unwrap()
|
||||
}
|
||||
}
|
||||
+137
-64
@@ -1,77 +1,150 @@
|
||||
use gio::ApplicationFlags;
|
||||
use glib::{clone, MainContext};
|
||||
use glib::MainContext;
|
||||
use gtk::{gio, glib, prelude::*};
|
||||
use once_cell::sync::OnceCell;
|
||||
use qemu_display::{Chardev, Console, Display};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::net::UnixStream;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
use zbus::Connection;
|
||||
|
||||
mod audio;
|
||||
mod clipboard;
|
||||
mod display_qemu;
|
||||
mod display;
|
||||
mod usbredir;
|
||||
|
||||
struct Inner {
|
||||
app: gtk::Application,
|
||||
conn: zbus::azync::Connection,
|
||||
usbredir: RefCell<Option<usbredir::Handler>>,
|
||||
audio: RefCell<Option<audio::Handler>>,
|
||||
clipboard: RefCell<Option<clipboard::Handler>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct App {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn new() -> Self {
|
||||
let app = gtk::Application::new(Some("org.qemu.rdw.demo"), ApplicationFlags::NON_UNIQUE);
|
||||
let conn = Connection::session()
|
||||
.expect("Failed to connect to DBus")
|
||||
.into();
|
||||
|
||||
let app = App {
|
||||
inner: Arc::new(Inner {
|
||||
app,
|
||||
conn,
|
||||
usbredir: Default::default(),
|
||||
audio: Default::default(),
|
||||
clipboard: Default::default(),
|
||||
}),
|
||||
};
|
||||
|
||||
let app_clone = app.clone();
|
||||
app.inner.app.connect_activate(move |app| {
|
||||
let ui_src = include_str!("main.ui");
|
||||
let builder = gtk::Builder::new();
|
||||
builder
|
||||
.add_from_string(ui_src)
|
||||
.expect("Couldn't add from string");
|
||||
let window: gtk::ApplicationWindow =
|
||||
builder.object("window").expect("Couldn't get window");
|
||||
window.set_application(Some(app));
|
||||
|
||||
let app_clone = app_clone.clone();
|
||||
MainContext::default().spawn_local(async move {
|
||||
let display = Display::new(app_clone.connection()).await.unwrap();
|
||||
|
||||
let console = Console::new(app_clone.connection(), 0)
|
||||
.await
|
||||
.expect("Failed to get the QEMU console");
|
||||
let rdw = display::Display::new(console);
|
||||
app_clone
|
||||
.inner
|
||||
.app
|
||||
.active_window()
|
||||
.unwrap()
|
||||
.set_child(Some(&rdw));
|
||||
|
||||
app_clone.set_usbredir(usbredir::Handler::new(display.usbredir().await));
|
||||
|
||||
if let Ok(Some(audio)) = display.audio().await {
|
||||
match audio::Handler::new(audio).await {
|
||||
Ok(handler) => app_clone.set_audio(handler),
|
||||
Err(e) => log::warn!("Failed to setup audio: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(Some(clipboard)) = display.clipboard().await {
|
||||
match clipboard::Handler::new(clipboard).await {
|
||||
Ok(handler) => app_clone.set_clipboard(handler),
|
||||
Err(e) => log::warn!("Failed to setup clipboard: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(c) = Chardev::new(app_clone.connection(), "qmp").await {
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
let (p0, p1) = UnixStream::pair().unwrap();
|
||||
if c.proxy.register(p1.as_raw_fd().into()).await.is_ok() {
|
||||
let mut reader = BufReader::new(p0.try_clone().unwrap());
|
||||
let mut line = String::new();
|
||||
std::thread::spawn(move || loop {
|
||||
if reader.read_line(&mut line).unwrap() > 0 {
|
||||
println!("{}", &line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.show();
|
||||
});
|
||||
});
|
||||
|
||||
let action_usb = gio::SimpleAction::new("usb", None);
|
||||
let app_clone = app.clone();
|
||||
action_usb.connect_activate(move |_, _| {
|
||||
let usbredir = app_clone.inner.usbredir.borrow();
|
||||
if let Some(usbredir) = usbredir.as_ref() {
|
||||
let dialog = gtk::Dialog::new();
|
||||
dialog.set_transient_for(app_clone.inner.app.active_window().as_ref());
|
||||
dialog.set_child(Some(&usbredir.widget()));
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
app.inner.app.add_action(&action_usb);
|
||||
|
||||
app
|
||||
}
|
||||
|
||||
fn connection(&self) -> &zbus::azync::Connection {
|
||||
&self.inner.conn
|
||||
}
|
||||
|
||||
fn set_usbredir(&self, usbredir: usbredir::Handler) {
|
||||
self.inner.usbredir.replace(Some(usbredir));
|
||||
}
|
||||
|
||||
fn set_audio(&self, audio: audio::Handler) {
|
||||
self.inner.audio.replace(Some(audio));
|
||||
}
|
||||
|
||||
fn set_clipboard(&self, clipboard: clipboard::Handler) {
|
||||
self.inner.clipboard.replace(Some(clipboard));
|
||||
}
|
||||
|
||||
fn run(&self) -> i32 {
|
||||
self.inner.app.run()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
pretty_env_logger::init();
|
||||
|
||||
let app = gtk::Application::new(Some("org.qemu.rdw.demo"), ApplicationFlags::NON_UNIQUE);
|
||||
|
||||
let conn: zbus::azync::Connection = Connection::session()
|
||||
.expect("Failed to connect to DBus")
|
||||
.into();
|
||||
|
||||
let audio = std::sync::Arc::new(OnceCell::new());
|
||||
let clipboard = std::sync::Arc::new(OnceCell::new());
|
||||
|
||||
app.connect_activate(move |app| {
|
||||
let window = gtk::ApplicationWindow::new(app);
|
||||
|
||||
window.set_title(Some("rdw demo"));
|
||||
window.set_default_size(1024, 768);
|
||||
|
||||
let conn = conn.clone();
|
||||
let audio_clone = audio.clone();
|
||||
let clipboard_clone = clipboard.clone();
|
||||
MainContext::default().spawn_local(clone!(@strong window => async move {
|
||||
let display = Display::new(&conn).await.unwrap();
|
||||
|
||||
let console = Console::new(&conn, 0).await.expect("Failed to get the QEMU console");
|
||||
let rdw = display_qemu::DisplayQemu::new(console);
|
||||
window.set_child(Some(&rdw));
|
||||
|
||||
let usbredir = display.usbredir().await;
|
||||
|
||||
if let Ok(Some(audio)) = display.audio().await {
|
||||
match audio::Handler::new(audio).await {
|
||||
Ok(handler) => audio_clone.set(handler).unwrap(),
|
||||
Err(e) => log::warn!("Failed to setup audio: {}", e),
|
||||
}
|
||||
}
|
||||
|
||||
match clipboard::Handler::new(&conn).await {
|
||||
Ok(handler) => clipboard_clone.set(handler).unwrap(),
|
||||
Err(e) => log::warn!("Failed to setup clipboard: {}", e),
|
||||
}
|
||||
|
||||
if let Ok(c) = Chardev::new(&conn, "qmp").await {
|
||||
use std::io::BufReader;
|
||||
use std::io::prelude::*;
|
||||
|
||||
let (p0, p1) = UnixStream::pair().unwrap();
|
||||
if c.proxy.register(p1.as_raw_fd().into()).await.is_ok() {
|
||||
let mut reader = BufReader::new(p0.try_clone().unwrap());
|
||||
let mut line = String::new();
|
||||
std::thread::spawn(move || loop {
|
||||
if reader.read_line(&mut line).unwrap() > 0 {
|
||||
println!("{}", &line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.show();
|
||||
}));
|
||||
});
|
||||
|
||||
let app = App::new();
|
||||
app.run();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkApplicationWindow" id="window">
|
||||
<property name="title" translatable="yes">qemu-rdw demo</property>
|
||||
<property name="default-width">1024</property>
|
||||
<property name="default-height">768</property>
|
||||
<child type="titlebar">
|
||||
<object class="GtkHeaderBar" id="headerbar">
|
||||
<property name="show_title_buttons">True</property>
|
||||
<child type="end">
|
||||
<object class="GtkButton">
|
||||
<property name="action-name">app.usb</property>
|
||||
<property name="label">USB devices</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
@@ -0,0 +1,53 @@
|
||||
use glib::{clone, MainContext};
|
||||
use gtk::{glib, prelude::*};
|
||||
use qemu_display::UsbRedir;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Handler {
|
||||
usbredir: UsbRedir,
|
||||
}
|
||||
|
||||
impl Handler {
|
||||
pub fn new(usbredir: UsbRedir) -> Self {
|
||||
Self { usbredir }
|
||||
}
|
||||
|
||||
pub fn widget(&self) -> rdw::UsbRedir {
|
||||
let widget = rdw::UsbRedir::new();
|
||||
|
||||
let usbredir = self.usbredir.clone();
|
||||
widget
|
||||
.model()
|
||||
.connect_items_changed(clone!(@weak widget => move |model, pos, _rm, add| {
|
||||
for pos in pos..pos + add {
|
||||
let item = model.item(pos).unwrap();
|
||||
if let Some(dev) = item.downcast_ref::<rdw::UsbDevice>().unwrap().device() {
|
||||
item.set_property("active", usbredir.is_device_connected(&dev)).unwrap();
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
let usbredir = self.usbredir.clone();
|
||||
widget.connect_device_state_set(move |widget, item, state| {
|
||||
let device = match item.device() {
|
||||
Some(it) => it.clone(),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let usbredir = usbredir.clone();
|
||||
MainContext::default().spawn_local(clone!(@weak item, @weak widget => async move {
|
||||
match usbredir.set_device_state(&device, state).await {
|
||||
Ok(active) => item.set_property("active", active).unwrap(),
|
||||
Err(e) => {
|
||||
if state {
|
||||
item.set_property("active", false).unwrap();
|
||||
}
|
||||
widget.emit_by_name("show-error",&[&e.to_string()]).unwrap();
|
||||
},
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
widget
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user