Compare commits

...

15 Commits

Author SHA1 Message Date
Marc-André Lureau ac188788cf qemu-rdw: fix a clippy warning
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 20:00:09 +04:00
Bilal Elmoussaoui 7fa131c922 qemu-display: Add a MultiTouch interface implementation 2024-04-09 19:58:26 +04:00
Marc-André Lureau 0ff232b771 chore: commit Cargo.lock
This allows to track our working dependencies, and is now a recommended practice, even for lib

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 19:37:23 +04:00
Marc-André Lureau bbd5494316 qemu-rdp: fix compilation with current IronRDP
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 19:34:45 +04:00
mihneabuz 8c3b5e7cba qemu-rdp: initial rdp server 2024-04-09 19:02:25 +04:00
Marc-André Lureau a4b0394f27 qemu-rdw: minor code simplification
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 19:02:01 +04:00
Marc-André Lureau 142148e8db qemu-display: replace needless Arc with Rc
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 19:01:23 +04:00
Marc-André Lureau 9da1aa5aeb qemu-display: replace refcell with rwlock
To have Sync?

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 19:01:23 +04:00
Marc-André Lureau 29926a8df0 qemu-display: misc improvements
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 19:01:14 +04:00
Marc-André Lureau 3e2bb7985c Update to zbus 4
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 18:34:53 +04:00
Marc-André Lureau a2185be540 qemu-rdw: use workspace deps
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 18:33:29 +04:00
Marc-André Lureau abca0e48d5 qemu-vte: fix build, use workspace deps
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 18:33:26 +04:00
Marc-André Lureau 67b334e5bb qemu-vnc: fix build, use workspace deps
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 18:33:25 +04:00
Marc-André Lureau aa10b12bcb qemu-display: update to current zbus git
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-04-09 18:32:55 +04:00
Marc-André Lureau 24cca85289 Cargo.toml: back to rdw.git
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
2024-03-27 15:15:52 +04:00
31 changed files with 5786 additions and 92 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
/target
Cargo.lock
.DS_Store
.idea
*.log

5235
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5,13 +5,17 @@ members = [
"qemu-rdw",
"qemu-vnc",
"qemu-vte",
"qemu-rdp",
"xtask",
]
default-members = ["qemu-rdw"]
resolver = "2"
[workspace.dependencies]
qemu-display = { path = "qemu-display", version = "0.1" }
keycodemap = { path = "keycodemap", version = "0.1" }
zbus = { version = "4", features = ["p2p"] }
[patch.crates-io]
zbus = { git = "https://github.com/dbus2/zbus.git" }
zvariant = { git = "https://github.com/dbus2/zbus.git" }
vnc = { git = "https://github.com/elmarco/rust-vnc", branch = "server" }
rdw4 = { path = "../rdw/rdw4" }
rdw4 = { git = "https://gitlab.gnome.org/malureau/rdw.git" }

View File

@ -13,7 +13,7 @@ qmp = ["dep:qapi", "dep:base64"]
cfg-if = "1.0"
log = "0.4"
derivative = "2.2.0"
zbus = "4"
zbus.workspace = true
zvariant = { version = "4", features = ["serde_bytes"] }
libc = "0.2.86"
enumflags2 = { version = "0.7", features = ["serde"] }

View File

@ -6,7 +6,7 @@ use std::os::unix::net::UnixStream;
use uds_windows::UnixStream;
#[cfg(unix)]
use zbus::zvariant::Fd;
use zbus::{dbus_interface, dbus_proxy, Connection};
use zbus::Connection;
use crate::{util, Result};
@ -51,17 +51,17 @@ pub struct Volume {
pub volume: Vec<u8>,
}
#[dbus_proxy(
#[zbus::proxy(
default_service = "org.qemu",
default_path = "/org/qemu/Display1/Audio",
interface = "org.qemu.Display1.Audio"
)]
trait Audio {
/// RegisterOutListener method
fn register_out_listener(&self, listener: Fd) -> zbus::Result<()>;
fn register_out_listener(&self, listener: Fd<'_>) -> zbus::Result<()>;
/// RegisterInListener method
fn register_in_listener(&self, listener: Fd) -> zbus::Result<()>;
fn register_in_listener(&self, listener: Fd<'_>) -> zbus::Result<()>;
}
#[derive(derivative::Derivative)]
@ -92,7 +92,7 @@ struct AudioOutListener<H: AudioOutHandler> {
handler: H,
}
#[dbus_interface(name = "org.qemu.Display1.AudioOutListener")]
#[zbus::interface(name = "org.qemu.Display1.AudioOutListener")]
impl<H: AudioOutHandler> AudioOutListener<H> {
/// Init method
async fn init(
@ -170,7 +170,7 @@ struct AudioInListener<H: AudioInHandler> {
handler: H,
}
#[dbus_interface(name = "org.qemu.Display1.AudioInListener")]
#[zbus::interface(name = "org.qemu.Display1.AudioInListener")]
impl<H: AudioInHandler> AudioInListener<H> {
/// Init method
async fn init(

View File

@ -3,32 +3,32 @@ use crate::win32::Fd;
use std::convert::TryFrom;
#[cfg(unix)]
use zbus::zvariant::Fd;
use zbus::{dbus_proxy, zvariant::ObjectPath};
use zbus::zvariant::ObjectPath;
use crate::Result;
#[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Chardev")]
#[zbus::proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Chardev")]
pub trait Chardev {
/// Register method
fn register(&self, stream: Fd) -> zbus::Result<()>;
fn register(&self, stream: Fd<'_>) -> zbus::Result<()>;
/// SendBreak method
fn send_break(&self) -> zbus::Result<()>;
/// Echo property
#[dbus_proxy(property)]
#[zbus(property)]
fn echo(&self) -> zbus::Result<bool>;
/// FEOpened property
#[dbus_proxy(property, name = "FEOpened")]
#[zbus(property, name = "FEOpened")]
fn fe_opened(&self) -> zbus::Result<bool>;
/// Name property
#[dbus_proxy(property)]
#[zbus(property)]
fn name(&self) -> zbus::Result<String>;
/// Owner property
#[dbus_proxy(property)]
#[zbus(property)]
fn owner(&self) -> zbus::Result<String>;
}

View File

@ -1,9 +1,6 @@
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::convert::TryFrom;
use zbus::{
dbus_interface, dbus_proxy,
zvariant::{ObjectPath, Type},
};
use zbus::zvariant::{ObjectPath, Type};
use crate::Result;
@ -15,7 +12,7 @@ pub enum ClipboardSelection {
Secondary,
}
#[dbus_proxy(
#[zbus::proxy(
default_service = "org.qemu",
default_path = "/org/qemu/Display1/Clipboard",
interface = "org.qemu.Display1.Clipboard"
@ -58,7 +55,7 @@ pub(crate) struct ClipboardListener<H: ClipboardHandler> {
handler: H,
}
#[dbus_interface(name = "org.qemu.Display1.Clipboard")]
#[zbus::interface(name = "org.qemu.Display1.Clipboard")]
impl<H: ClipboardHandler> ClipboardListener<H> {
async fn register(&mut self) {
self.handler.register().await;

View File

@ -2,27 +2,30 @@
use crate::win32::Fd;
#[cfg(unix)]
use std::os::unix::net::UnixStream;
use std::{cell::RefCell, convert::TryFrom};
use std::{convert::TryFrom, sync::RwLock};
#[cfg(windows)]
use uds_windows::UnixStream;
#[cfg(unix)]
use zbus::zvariant::Fd;
use zbus::{dbus_proxy, zvariant::ObjectPath, Connection};
use zbus::{zvariant::ObjectPath, Connection};
use crate::{util, ConsoleListener, ConsoleListenerHandler, KeyboardProxy, MouseProxy, Result};
use crate::{
util, ConsoleListener, ConsoleListenerHandler, KeyboardProxy, MouseProxy, MultiTouchProxy,
Result,
};
#[cfg(windows)]
use crate::{
ConsoleListenerD3d11, ConsoleListenerD3d11Handler, ConsoleListenerMap,
ConsoleListenerMapHandler,
};
#[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Console")]
#[zbus::proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Console")]
pub trait Console {
/// RegisterListener method
fn register_listener(&self, listener: Fd) -> zbus::Result<()>;
fn register_listener(&self, listener: Fd<'_>) -> zbus::Result<()>;
/// SetUIInfo method
#[dbus_proxy(name = "SetUIInfo")]
#[zbus(name = "SetUIInfo")]
fn set_ui_info(
&self,
width_mm: u16,
@ -33,19 +36,19 @@ pub trait Console {
height: u32,
) -> zbus::Result<()>;
#[dbus_proxy(property)]
#[zbus(property)]
fn label(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
#[zbus(property)]
fn head(&self) -> zbus::Result<u32>;
#[dbus_proxy(property)]
#[zbus(property)]
fn type_(&self) -> zbus::Result<String>;
#[dbus_proxy(property)]
#[zbus(property)]
fn width(&self) -> zbus::Result<u32>;
#[dbus_proxy(property)]
#[zbus(property)]
fn height(&self) -> zbus::Result<u32>;
}
@ -58,7 +61,9 @@ pub struct Console {
pub keyboard: KeyboardProxy<'static>,
#[derivative(Debug = "ignore")]
pub mouse: MouseProxy<'static>,
listener: RefCell<Option<Connection>>,
#[derivative(Debug = "ignore")]
pub multi_touch: MultiTouchProxy<'static>,
listener: RwLock<Option<Connection>>,
#[cfg(windows)]
peer_pid: u32,
}
@ -72,11 +77,16 @@ impl Console {
.build()
.await?;
let mouse = MouseProxy::builder(conn).path(&obj_path)?.build().await?;
let multi_touch = MultiTouchProxy::builder(conn)
.path(&obj_path)?
.build()
.await?;
Ok(Self {
proxy,
keyboard,
mouse,
listener: RefCell::new(None),
multi_touch,
listener: RwLock::new(None),
#[cfg(windows)]
peer_pid,
})
@ -107,13 +117,13 @@ impl Console {
.serve_at("/org/qemu/Display1/Listener", ConsoleListener::new(handler))?
.build()
.await?;
self.listener.replace(Some(c));
*self.listener.write().unwrap() = Some(c);
Ok(())
}
#[cfg(windows)]
pub async fn set_map_listener<H: ConsoleListenerMapHandler>(&self, handler: H) -> Result<bool> {
if let Some(l) = self.listener.borrow_mut().as_mut() {
if let Some(l) = &*self.listener.write().unwrap() {
return l
.object_server()
.at(
@ -132,7 +142,7 @@ impl Console {
&self,
handler: H,
) -> Result<bool> {
if let Some(l) = self.listener.borrow_mut().as_mut() {
if let Some(l) = &*self.listener.write().unwrap() {
return l
.object_server()
.at(
@ -146,7 +156,7 @@ impl Console {
Err(crate::Error::Failed("Must call register first!".into()))
}
pub fn unregister_listener(&mut self) {
self.listener.replace(None);
pub fn unregister_listener(&self) {
*self.listener.write().unwrap() = None;
}
}

View File

@ -4,7 +4,6 @@ use derivative::Derivative;
use std::ops::Drop;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use zbus::dbus_interface;
#[cfg(unix)]
use zbus::zvariant::Fd;
@ -160,7 +159,7 @@ pub(crate) struct ConsoleListener<H: ConsoleListenerHandler> {
handler: H,
}
#[dbus_interface(name = "org.qemu.Display1.Listener")]
#[zbus::interface(name = "org.qemu.Display1.Listener")]
impl<H: ConsoleListenerHandler> ConsoleListener<H> {
async fn scanout(
&mut self,
@ -205,10 +204,10 @@ impl<H: ConsoleListenerHandler> ConsoleListener<H> {
}
#[cfg(not(unix))]
#[dbus_interface(name = "ScanoutDMABUF")]
#[zbus(name = "ScanoutDMABUF")]
async fn scanout_dmabuf(
&mut self,
_fd: Fd,
_fd: Fd<'_>,
_width: u32,
_height: u32,
_stride: u32,
@ -222,10 +221,10 @@ impl<H: ConsoleListenerHandler> ConsoleListener<H> {
}
#[cfg(unix)]
#[dbus_interface(name = "ScanoutDMABUF")]
#[zbus(name = "ScanoutDMABUF")]
async fn scanout_dmabuf(
&mut self,
fd: Fd,
fd: Fd<'_>,
width: u32,
height: u32,
stride: u32,
@ -249,7 +248,7 @@ impl<H: ConsoleListenerHandler> ConsoleListener<H> {
}
#[cfg(not(unix))]
#[dbus_interface(name = "UpdateDMABUF")]
#[zbus(name = "UpdateDMABUF")]
async fn update_dmabuf(&mut self, _x: i32, _y: i32, _w: i32, _h: i32) -> zbus::fdo::Result<()> {
Err(zbus::fdo::Error::NotSupported(
"DMABUF is not support on !unix".into(),
@ -257,7 +256,7 @@ impl<H: ConsoleListenerHandler> ConsoleListener<H> {
}
#[cfg(unix)]
#[dbus_interface(name = "UpdateDMABUF")]
#[zbus(name = "UpdateDMABUF")]
async fn update_dmabuf(&mut self, x: i32, y: i32, w: i32, h: i32) -> zbus::fdo::Result<()> {
self.handler
.update_dmabuf(UpdateDMABUF { x, y, w, h })
@ -292,7 +291,7 @@ impl<H: ConsoleListenerHandler> ConsoleListener<H> {
.await;
}
#[dbus_interface(property)]
#[zbus(property)]
fn interfaces(&self) -> Vec<String> {
self.handler.interfaces()
}
@ -325,7 +324,7 @@ pub(crate) struct ConsoleListenerMap<H: ConsoleListenerMapHandler> {
}
#[cfg(windows)]
#[dbus_interface(name = "org.qemu.Display1.Listener.Win32.Map")]
#[zbus::interface(name = "org.qemu.Display1.Listener.Win32.Map")]
impl<H: ConsoleListenerMapHandler> ConsoleListenerMap<H> {
async fn scanout_map(
&mut self,
@ -377,7 +376,7 @@ pub(crate) struct ConsoleListenerD3d11<H: ConsoleListenerD3d11Handler> {
}
#[cfg(windows)]
#[dbus_interface(name = "org.qemu.Display1.Listener.Win32.D3d11")]
#[zbus::interface(name = "org.qemu.Display1.Listener.Win32.D3d11")]
impl<H: ConsoleListenerD3d11Handler> ConsoleListenerD3d11<H> {
async fn scanout_texture2d(
&mut self,

View File

@ -2,7 +2,7 @@ use futures::stream::{self, StreamExt};
use std::{
collections::HashMap,
convert::{TryFrom, TryInto},
sync::Arc,
rc::Rc,
};
use zbus::{
fdo,
@ -31,7 +31,7 @@ struct Inner<'d> {
#[derive(Clone)]
pub struct Display<'d> {
inner: Arc<Inner<'d>>,
inner: Rc<Inner<'d>>,
}
impl<'d> Display<'d> {
@ -113,7 +113,7 @@ impl<'d> Display<'d> {
};
Ok(Self {
inner: Arc::new(inner),
inner: Rc::new(inner),
})
}
@ -140,6 +140,7 @@ impl<'d> Display<'d> {
{
// FIXME: no ancillary fd API at this point
// https://github.com/rust-lang/rust/issues/76915
let _ = p0;
qmp.execute(&qmp::getfd {
fdname: "fdname".into(),
})?;
@ -200,7 +201,7 @@ impl<'d> Display<'d> {
}
pub async fn receive_owner_changed(&self) -> Result<OwnerChangedStream<'_>> {
Ok(self.inner.proxy.receive_owner_changed().await?)
Ok(self.inner.proxy.inner().receive_owner_changed().await?)
}
pub async fn audio(&self) -> Result<Option<Audio>> {

View File

@ -1,6 +1,6 @@
use enumflags2::{bitflags, BitFlags};
use serde::{Deserialize, Serialize};
use zbus::{dbus_proxy, zvariant::Type};
use zbus::zvariant::Type;
#[bitflags]
#[repr(u32)]
@ -11,7 +11,7 @@ pub enum KeyboardModifiers {
Caps = 0x4,
}
#[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Keyboard")]
#[zbus::proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Keyboard")]
pub trait Keyboard {
/// Press method
fn press(&self, keycode: u32) -> zbus::Result<()>;
@ -19,6 +19,6 @@ pub trait Keyboard {
/// Release method
fn release(&self, keycode: u32) -> zbus::Result<()>;
#[dbus_proxy(property)]
#[zbus(property)]
fn modifiers(&self) -> zbus::Result<BitFlags<KeyboardModifiers>>;
}

View File

@ -36,6 +36,9 @@ pub use mouse::*;
mod display;
pub use display::*;
mod multi_touch;
pub use multi_touch::*;
#[cfg(unix)]
mod usbredir;
#[cfg(unix)]

View File

@ -1,5 +1,5 @@
use serde_repr::{Deserialize_repr, Serialize_repr};
use zbus::{dbus_proxy, zvariant::Type};
use zbus::zvariant::Type;
#[repr(u32)]
#[derive(Deserialize_repr, Serialize_repr, Type, Debug, Hash, PartialEq, Eq, Clone, Copy)]
@ -13,7 +13,7 @@ pub enum MouseButton {
Extra,
}
#[dbus_proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Mouse")]
#[zbus::proxy(default_service = "org.qemu", interface = "org.qemu.Display1.Mouse")]
pub trait Mouse {
/// Press method
fn press(&self, button: MouseButton) -> zbus::Result<()>;
@ -27,6 +27,6 @@ pub trait Mouse {
/// RelMotion method
fn rel_motion(&self, dx: i32, dy: i32) -> zbus::Result<()>;
#[dbus_proxy(property)]
#[zbus(property)]
fn is_absolute(&self) -> zbus::Result<bool>;
}

View File

@ -0,0 +1,22 @@
use serde::{Deserialize, Serialize};
use zbus::zvariant::Type;
#[repr(u32)]
#[derive(Type, Debug, PartialEq, Copy, Clone, Eq, Serialize, Deserialize)]
pub enum TouchEventKind {
Begin = 0,
Update = 1,
End = 2,
Cancel = 3,
}
#[zbus::proxy(
default_service = "org.qemu",
interface = "org.qemu.Display1.MultiTouch"
)]
pub trait MultiTouch {
fn send_event(&self, kind: TouchEventKind, num_slot: u64, x: f64, y: f64) -> zbus::Result<()>;
#[dbus_proxy(property)]
fn max_slots(&self) -> zbus::Result<i32>;
}

View File

@ -78,7 +78,7 @@ impl DeviceHandler for Handler {
}
#[cfg(unix)]
#[zbus::dbus_proxy(
#[zbus::proxy(
interface = "org.freedesktop.usbredir1",
default_service = "org.freedesktop.usbredir1",
default_path = "/org/freedesktop/usbredir1"
@ -109,7 +109,7 @@ impl Handler {
};
let (stream, peer) = UnixStream::pair()?;
chardev.proxy.register(peer.as_raw_fd().into()).await?;
chardev.proxy.register((&peer).into()).await?;
let c = ctxt.clone();
let stream_fd = stream.as_raw_fd();

View File

@ -1,7 +1,7 @@
use crate::Result;
#[cfg(unix)]
use std::os::unix::{io::AsRawFd, net::UnixStream};
use std::os::unix::net::UnixStream;
#[cfg(windows)]
use win32::Fd;
#[cfg(unix)]
@ -21,7 +21,7 @@ use windows::Win32::System::Threading::PROCESS_DUP_HANDLE;
pub fn prepare_uds_pass(#[cfg(windows)] peer_pid: u32, us: &UnixStream) -> Result<Fd> {
#[cfg(unix)]
{
Ok(us.as_raw_fd().into())
Ok(us.into())
}
#[cfg(windows)]

View File

@ -1,16 +1,14 @@
use zbus::dbus_proxy;
#[dbus_proxy(
#[zbus::proxy(
default_service = "org.qemu",
interface = "org.qemu.Display1.VM",
default_path = "/org/qemu/Display1/VM"
)]
pub trait VM {
/// Name property
#[dbus_proxy(property)]
#[zbus(property)]
fn name(&self) -> zbus::Result<String>;
/// UUID property
#[dbus_proxy(property)]
#[zbus(property)]
fn uuid(&self) -> zbus::Result<String>;
}

View File

@ -8,7 +8,7 @@ use windows::Win32::{
#[cfg(feature = "qmp")]
use uds_windows::UnixStream;
pub type Fd = Vec<u8>;
pub type Fd<'a> = Vec<u8>;
// A process handle
pub struct ProcessHandle(HANDLE);

22
qemu-rdp/Cargo.toml Normal file
View File

@ -0,0 +1,22 @@
[package]
name = "qemu-rdp"
version = "0.1.0"
authors = ["Mihnea Buzatu <mihneabuzatu88@gmail.com>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
qemu-display = { path = "../qemu-display" }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.16", features = ["env-filter"] }
keycodemap = { path = "../keycodemap" }
bytes = "1.4"
rustls = { version = "0.21" }
rustls-pemfile = "1.0"
tokio = { version = "1.28", features = ["full"] }
tokio-rustls = "0.24"
anyhow = "1.0"
clap = { version = "4.2", features = ["derive", "cargo"] }
async-trait = "0.1"
ironrdp = { git = "https://github.com/Devolutions/IronRDP", features = ["server"] }

59
qemu-rdp/src/args.rs Normal file
View File

@ -0,0 +1,59 @@
use clap::clap_derive::ValueEnum;
use clap::{crate_name, Parser};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum SecurityProtocol {
Ssl,
Hybrid,
HybridEx,
}
impl From<SecurityProtocol> for ironrdp::pdu::nego::SecurityProtocol {
fn from(value: SecurityProtocol) -> Self {
match value {
SecurityProtocol::Ssl => ironrdp::pdu::nego::SecurityProtocol::SSL,
SecurityProtocol::Hybrid => ironrdp::pdu::nego::SecurityProtocol::HYBRID,
SecurityProtocol::HybridEx => ironrdp::pdu::nego::SecurityProtocol::HYBRID_EX,
}
}
}
#[derive(Parser, Debug)]
pub struct ServerArgs {
/// IP address
#[clap(short, long, default_value = "127.0.0.1")]
pub address: std::net::IpAddr,
/// IP port
#[clap(short, long, default_value = "3389")]
pub port: u16,
/// Specify the security protocols to use
#[clap(long, value_enum, value_parser, default_value_t = SecurityProtocol::Ssl)]
pub security_protocol: SecurityProtocol,
/// Path to tls certificate
#[clap(short, long, value_parser)]
pub cert: Option<String>,
/// Path to private key
#[clap(short, long, value_parser)]
pub key: Option<String>,
}
#[derive(Parser, Debug)]
pub struct Args {
#[clap(short, long, value_parser, default_value_t = format!("{}.log", crate_name!()))]
pub log_file: String,
#[clap(flatten)]
pub server: ServerArgs,
/// DBUS address
#[clap(short, long)]
pub dbus_address: Option<String>,
}
pub fn parse() -> Args {
Args::parse()
}

59
qemu-rdp/src/main.rs Normal file
View File

@ -0,0 +1,59 @@
use anyhow::Context;
use qemu_display::zbus;
mod args;
mod server;
mod util;
#[tokio::main]
async fn main() -> Result<(), anyhow::Error> {
let mut args = args::parse();
setup_logging(args.log_file.as_str()).context("unable to initialize logging")?;
let dbus = match args.dbus_address.take() {
None => zbus::Connection::session().await,
Some(addr) => {
zbus::ConnectionBuilder::address(addr.as_str())?
.build()
.await
}
}
.expect("Failed to connect to DBus");
server::Server::new(dbus, args.server).run().await?;
Ok(())
}
fn setup_logging(log_file: &str) -> anyhow::Result<()> {
use std::fs::OpenOptions;
use tracing::metadata::LevelFilter;
use tracing_subscriber::prelude::*;
use tracing_subscriber::EnvFilter;
let file = OpenOptions::new()
.create(true)
.append(true)
.open(log_file)
.with_context(|| format!("couldnt open {log_file}"))?;
let fmt_layer = tracing_subscriber::fmt::layer()
.compact()
.with_ansi(false)
.with_writer(file);
let env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::WARN.into())
.with_env_var("QEMURDP_LOG_LEVEL")
.from_env_lossy();
tracing_subscriber::registry()
.with(fmt_layer)
.with(env_filter)
.try_init()
.context("failed to set tracing global subscriber")?;
Ok(())
}

View File

@ -0,0 +1,133 @@
use anyhow::Result;
use ironrdp::{connector::DesktopSize, server::RdpServerDisplayUpdates};
use qemu_display::{zbus, Console, ConsoleListenerHandler, Cursor, MouseSet, Scanout, Update};
use ironrdp::server::{BitmapUpdate, DisplayUpdate, PixelFormat, PixelOrder, RdpServerDisplay};
use crate::cast;
pub struct DisplayHandler {
console: Console,
}
struct DisplayUpdates {
receiver: tokio::sync::mpsc::Receiver<DisplayUpdate>,
}
impl DisplayHandler {
pub async fn connect(dbus: zbus::Connection) -> Result<Self> {
let console = Console::new(&dbus, 0).await?;
Ok(Self { console })
}
async fn listen(&self) -> Result<DisplayUpdates> {
let (sender, receiver) = tokio::sync::mpsc::channel::<DisplayUpdate>(32);
let (width, height) = (
self.console.width().await? as _,
self.console.height().await? as _,
);
let desktop_size = DesktopSize { width, height };
let listener = Listener::new(sender, desktop_size);
self.console.unregister_listener();
self.console.register_listener(listener).await?;
Ok(DisplayUpdates { receiver })
}
}
#[async_trait::async_trait]
impl RdpServerDisplayUpdates for DisplayUpdates {
async fn next_update(&mut self) -> Option<DisplayUpdate> {
self.receiver.recv().await
}
}
#[async_trait::async_trait]
impl RdpServerDisplay for DisplayHandler {
async fn size(&mut self) -> DesktopSize {
let width = self.console.proxy.width().await.unwrap() as u16;
let height = self.console.proxy.height().await.unwrap() as u16;
DesktopSize { height, width }
}
async fn updates(&mut self) -> Result<Box<dyn RdpServerDisplayUpdates>> {
Ok(Box::new(self.listen().await?))
}
}
struct Listener {
sender: tokio::sync::mpsc::Sender<DisplayUpdate>,
}
impl Listener {
fn new(sender: tokio::sync::mpsc::Sender<DisplayUpdate>, _desktop_size: DesktopSize) -> Self {
Self { sender }
}
async fn send(&mut self, update: DisplayUpdate) {
if let Err(e) = self.sender.send(update).await {
println!("{:?}", e);
};
}
}
#[async_trait::async_trait]
impl ConsoleListenerHandler for Listener {
async fn scanout(&mut self, scanout: Scanout) {
let format = match scanout.format {
537_004_168 => PixelFormat::BgrA32,
_ => PixelFormat::RgbA32,
};
let width: u16 = cast!(scanout.width);
let height: u16 = cast!(scanout.height);
let bitmap = DisplayUpdate::Bitmap(BitmapUpdate {
top: 0,
left: 0,
width: width.try_into().unwrap(),
height: height.try_into().unwrap(),
format,
order: PixelOrder::TopToBottom,
data: scanout.data,
});
self.send(bitmap).await;
}
async fn update(&mut self, update: Update) {
let format = match update.format {
537_004_168 => PixelFormat::BgrA32,
_ => PixelFormat::RgbA32,
};
let width: u16 = cast!(update.w);
let height: u16 = cast!(update.h);
let bitmap = DisplayUpdate::Bitmap(BitmapUpdate {
// TODO: fix scary conversion
top: cast!(update.y),
left: cast!(update.x),
width: width.try_into().unwrap(),
height: height.try_into().unwrap(),
format,
order: PixelOrder::TopToBottom,
data: update.data,
});
self.send(bitmap).await;
}
#[cfg(unix)]
async fn scanout_dmabuf(&mut self, _scanout: qemu_display::ScanoutDMABUF) {}
#[cfg(unix)]
async fn update_dmabuf(&mut self, _update: qemu_display::UpdateDMABUF) {}
async fn disable(&mut self) {}
async fn mouse_set(&mut self, _set: MouseSet) {}
async fn cursor_define(&mut self, _cursor: Cursor) {}
fn disconnected(&mut self) {}
fn interfaces(&self) -> Vec<String> {
vec![]
}
}

View File

@ -0,0 +1,88 @@
use ironrdp::server::{KeyboardEvent, MouseEvent, RdpServerInputHandler};
use qemu_display::{zbus, Console, MouseButton};
use tokio::{
sync::mpsc::{Receiver, Sender},
task,
};
use crate::cast;
pub struct InputHandler {
tx: Sender<InputEvent>,
_task: task::JoinHandle<()>,
}
#[derive(Debug)]
enum InputEvent {
Keyboard(KeyboardEvent),
Mouse(MouseEvent),
}
impl RdpServerInputHandler for InputHandler {
fn keyboard(&mut self, event: KeyboardEvent) {
tracing::debug!(?event);
if let Err(e) = self.tx.try_send(InputEvent::Keyboard(event)) {
eprintln!("keyboard error: {:?}", e);
}
}
fn mouse(&mut self, event: MouseEvent) {
tracing::debug!(?event);
if let Err(e) = self.tx.try_send(InputEvent::Mouse(event)) {
eprintln!("keyboard error: {:?}", e);
}
}
}
async fn input_receive_task(mut rx: Receiver<InputEvent>, console: Console) {
loop {
let res = match rx.recv().await {
Some(InputEvent::Keyboard(ev)) => match ev {
KeyboardEvent::Pressed { code, .. } => console.keyboard.press(code as u32).await,
KeyboardEvent::Released { code, .. } => console.keyboard.release(code as u32).await,
other => {
eprintln!("unhandled keyboard event: {:?}", other);
Ok(())
}
},
Some(InputEvent::Mouse(ev)) => match ev {
MouseEvent::Move { x, y } => {
tracing::debug!(?x, ?y);
console.mouse.set_abs_position(cast!(x), cast!(y)).await
}
MouseEvent::RightPressed => console.mouse.press(MouseButton::Right).await,
MouseEvent::RightReleased => console.mouse.release(MouseButton::Right).await,
MouseEvent::LeftPressed => console.mouse.press(MouseButton::Left).await,
MouseEvent::LeftReleased => console.mouse.release(MouseButton::Left).await,
MouseEvent::VerticalScroll { value } => {
let motion = if value > 0 {
MouseButton::WheelUp
} else {
MouseButton::WheelDown
};
console.mouse.press(motion).await
}
other => {
eprintln!("unhandled input event: {:?}", other);
Ok(())
}
},
None => break,
};
if let Err(e) = res {
eprintln!("input handling error: {:?}", e);
}
}
}
impl InputHandler {
pub async fn connect(dbus: zbus::Connection) -> anyhow::Result<InputHandler> {
let console = Console::new(&dbus, 0).await?;
let (tx, rx) = tokio::sync::mpsc::channel(30);
let _task = task::spawn(async move { input_receive_task(rx, console).await });
Ok(Self { _task, tx })
}
}

View File

@ -0,0 +1,61 @@
mod display;
mod input;
use anyhow::Error;
use qemu_display::zbus;
use rustls::ServerConfig;
use rustls_pemfile::{certs, pkcs8_private_keys};
use std::{fs::File, io::BufReader, sync::Arc};
use tokio_rustls::TlsAcceptor;
use ironrdp::server::RdpServer;
use crate::args::ServerArgs;
use display::DisplayHandler;
use input::InputHandler;
pub struct Server {
dbus: zbus::Connection,
args: ServerArgs,
}
impl Server {
pub fn new(dbus: zbus::Connection, args: ServerArgs) -> Self {
Self { dbus, args }
}
pub async fn run(&mut self) -> Result<(), Error> {
let tls = self
.args
.cert
.as_ref()
.zip(self.args.key.as_ref())
.map(|(cert, key)| acceptor(cert, key).unwrap());
let handler = InputHandler::connect(self.dbus.clone()).await?;
let display = DisplayHandler::connect(self.dbus.clone()).await?;
let mut server = RdpServer::builder()
.with_addr((self.args.address, self.args.port))
.with_tls(tls.unwrap())
.with_input_handler(handler)
.with_display_handler(display)
.build();
server.run().await
}
}
fn acceptor(cert_path: &str, key_path: &str) -> Result<TlsAcceptor, Error> {
let cert = certs(&mut BufReader::new(File::open(cert_path)?))?[0].clone();
let key = pkcs8_private_keys(&mut BufReader::new(File::open(key_path)?))?[0].clone();
let server_config = ServerConfig::builder()
.with_safe_defaults()
.with_no_client_auth()
.with_single_cert(vec![rustls::Certificate(cert)], rustls::PrivateKey(key))
.expect("bad certificate/key");
Ok(TlsAcceptor::from(Arc::new(server_config)))
}

View File

@ -12,9 +12,9 @@ qmp = ["qemu-display/qmp"]
log = "0.4"
pretty_env_logger = "0.4"
once_cell = "1.5"
zbus = { version = "4" }
qemu-display = { path = "../qemu-display" }
keycodemap = { path = "../keycodemap" }
zbus.workspace = true
qemu-display.workspace = true
keycodemap.workspace = true
futures-util = "0.3"
futures = "0.3"
async-trait = "0.1"

View File

@ -221,10 +221,7 @@ fn watch_clipboard(
}
fn clipboard_from_selection(selection: ClipboardSelection) -> Option<(gdk::Clipboard, usize)> {
let display = match gdk::Display::default() {
Some(display) => display,
None => return None,
};
let display = gdk::Display::default()?;
match selection {
ClipboardSelection::Clipboard => Some((display.clipboard(), 0)),

View File

@ -4,7 +4,7 @@ use glib::MainContext;
use gtk::{gio, glib, prelude::*};
use qemu_display::{util, Chardev, Console, Display};
use rdw::gtk;
use std::{cell::RefCell, convert::TryFrom, sync::Arc};
use std::{cell::RefCell, convert::TryFrom, rc::Rc};
use zbus::names::BusName;
mod audio;
@ -23,7 +23,7 @@ struct Inner {
#[derive(Clone)]
struct App {
inner: Arc<Inner>,
inner: Rc<Inner>,
}
#[derive(Debug, Default)]
@ -36,7 +36,7 @@ struct AppOptions {
wait: bool,
}
async fn display_from_opt(opt: Arc<RefCell<AppOptions>>) -> Option<Display<'static>> {
async fn display_from_opt(opt: Rc<RefCell<AppOptions>>) -> Option<Display<'static>> {
#[cfg(feature = "qmp")]
if let Some(qmp_addr) = &opt.borrow().qmp {
return Some(Display::new_qmp(qmp_addr).await.unwrap());
@ -76,6 +76,9 @@ async fn display_from_opt(opt: Arc<RefCell<AppOptions>>) -> Option<Display<'stat
.unwrap()
.map(Into::into)
} else {
if opt.borrow().wait {
unimplemented!(); // FIXME
}
BusName::try_from("org.qemu").ok()
};
@ -142,7 +145,7 @@ impl App {
None,
);
let opt: Arc<RefCell<AppOptions>> = Default::default();
let opt: Rc<RefCell<AppOptions>> = Default::default();
let opt_clone = opt.clone();
app.connect_handle_local_options(move |_, opt| {
let mut app_opt = opt_clone.borrow_mut();
@ -170,7 +173,7 @@ impl App {
});
let app = App {
inner: Arc::new(Inner {
inner: Rc::new(Inner {
app,
#[cfg(unix)]
usbredir: Default::default(),

View File

@ -7,11 +7,11 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
qemu-display = { path = "../qemu-display" }
keycodemap = { path ="../keycodemap" }
qemu-display.workspace = true
keycodemap.workspace = true
vnc = "0.4.0"
clap = { version = "3.2", features = ["derive"] }
zbus = { version = "3.0" }
zbus.workspace = true
libc = "0.2.86"
image = "0.23.14"
derivative = "2.2.0"

View File

@ -287,6 +287,10 @@ impl ConsoleListenerHandler for ConsoleListener {
fn disconnected(&mut self) {
dbg!();
}
fn interfaces(&self) -> Vec<String> {
vec![]
}
}
#[derive(Debug)]
@ -317,7 +321,7 @@ impl Server {
}
fn stop_console(&self) -> Result<(), Box<dyn Error>> {
let mut inner = self.inner.lock().unwrap();
let inner = self.inner.lock().unwrap();
inner.console.unregister_listener();
Ok(())
}

View File

@ -7,8 +7,8 @@ edition = "2018"
log = "0.4"
pretty_env_logger = "0.4"
once_cell = "1.5"
zbus = { version = "3.0" }
qemu-display = { path = "../qemu-display" }
zbus.workspace = true
qemu-display.workspace = true
futures = "0.3.13"
[dependencies.vte]

View File

@ -37,7 +37,7 @@ fn main() {
c.proxy.name().await.expect("Chardev not found");
let (p0, p1) = UnixStream::pair().unwrap();
if c.proxy.register(p1.as_raw_fd().into()).await.is_ok() {
if c.proxy.register((&p1).into()).await.is_ok() {
let ostream = unsafe { gio::UnixOutputStream::with_fd(p0.as_raw_fd()) };
let istream = unsafe { gio::UnixInputStream::take_fd(p0) }
.dynamic_cast::<gio::PollableInputStream>()