feat(web): allow dynamic resize for web (#550)

This commit is contained in:
irvingouj @ Devolutions 2024-09-19 15:08:45 -04:00 committed by GitHub
parent 3d3d9f2c56
commit c04bc2d29c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 354 additions and 7 deletions

View file

@ -30,6 +30,7 @@ ironrdp = { workspace = true, features = [
"dvc",
"cliprdr",
"svc",
"displaycontrol"
] }
ironrdp-core.workspace = true
ironrdp-cliprdr-format = { workspace = true }

View file

@ -36,6 +36,11 @@ impl Canvas {
Ok(Self { width, surface })
}
pub(crate) fn resize(&mut self, width: NonZeroU32, height: NonZeroU32) {
self.surface.resize(width, height).expect("surface resize");
self.width = width.get();
}
pub(crate) fn draw(&mut self, buffer: &[u8], region: InclusiveRectangle) -> anyhow::Result<()> {
let region_width = region.width();
let region_height = region.height();

View file

@ -3,6 +3,7 @@
use core::cell::RefCell;
use std::borrow::Cow;
use std::num::NonZeroU32;
use std::rc::Rc;
use std::time::Duration;
@ -18,6 +19,8 @@ use ironrdp::cliprdr::CliprdrClient;
use ironrdp::connector::connection_activation::ConnectionActivationState;
use ironrdp::connector::credssp::KerberosConfig;
use ironrdp::connector::{self, ClientConnector, Credentials};
use ironrdp::displaycontrol::client::DisplayControlClient;
use ironrdp::dvc::DrdynvcClient;
use ironrdp::graphics::image_processing::PixelFormat;
use ironrdp::pdu::input::fast_path::FastPathInputEvent;
use ironrdp::pdu::rdp::client_info::PerformanceFlags;
@ -64,6 +67,8 @@ struct SessionBuilderInner {
remote_clipboard_changed_callback: Option<js_sys::Function>,
remote_received_format_list_callback: Option<js_sys::Function>,
force_clipboard_update_callback: Option<js_sys::Function>,
use_display_control: bool,
}
impl Default for SessionBuilderInner {
@ -89,6 +94,8 @@ impl Default for SessionBuilderInner {
remote_clipboard_changed_callback: None,
remote_received_format_list_callback: None,
force_clipboard_update_callback: None,
use_display_control: false,
}
}
}
@ -209,6 +216,12 @@ impl SessionBuilder {
self.clone()
}
/// Optional
pub fn use_display_control(&self) -> SessionBuilder {
self.0.borrow_mut().use_display_control = true;
self.clone()
}
pub async fn connect(&self) -> Result<Session, IronRdpError> {
let (
username,
@ -301,15 +314,18 @@ impl SessionBuilder {
}
}
let (connection_result, ws) = connect(
let use_display_control = self.0.borrow().use_display_control;
let (connection_result, ws) = connect(ConnectParams {
ws,
config,
auth_token,
proxy_auth_token: auth_token,
destination,
pcb,
kdc_proxy_url,
clipboard.as_ref().map(|clip| clip.backend()),
)
clipboard_backend: clipboard.as_ref().map(|clip| clip.backend()),
use_display_control,
})
.await?;
info!("Connected!");
@ -345,6 +361,12 @@ pub(crate) enum RdpInputEvent {
Cliprdr(ClipboardMessage),
ClipboardBackend(WasmClipboardBackendMessage),
FastPath(FastPathInputEvents),
Resize {
width: u32,
height: u32,
scale_factor: Option<u32>,
physical_size: Option<(u32, u32)>,
},
TerminateSession,
}
@ -489,6 +511,21 @@ impl Session {
active_stage.process_fastpath_input(&mut image, &events)
.context("fast path input events processing")?
}
RdpInputEvent::Resize { width, height, scale_factor, physical_size } => {
debug!(width, height, scale_factor, "Resize event received");
if width == 0 || height == 0 {
warn!("Resize event ignored: width or height is zero");
Vec::new()
} else if let Some(response_frame) = active_stage.encode_resize(width, height, scale_factor, physical_size) {
self.render_canvas.set_width(width);
self.render_canvas.set_height(height);
gui.resize(NonZeroU32::new(width).unwrap(), NonZeroU32::new(height).unwrap());
vec![ActiveStageOutput::ResponseFrame(response_frame?)]
} else {
debug!("Resize event ignored");
Vec::new()
}
},
RdpInputEvent::TerminateSession => {
active_stage.graceful_shutdown()
.context("graceful shutdown")?
@ -757,6 +794,24 @@ impl Session {
Ok(())
}
pub fn resize(
&self,
width: u32,
height: u32,
scale_factor: Option<u32>,
physical_width: Option<u32>,
physical_height: Option<u32>,
) {
self.input_events_tx
.unbounded_send(RdpInputEvent::Resize {
width,
height,
scale_factor,
physical_size: physical_width.and_then(|width| physical_height.map(|height| (width, height))),
})
.expect("send resize event to writer task");
}
#[allow(clippy::unused_self)]
pub fn supports_unicode_keyboard_shortcuts(&self) -> bool {
// RDP does not support Unicode keyboard shortcuts (When key combinations are executed, only
@ -832,7 +887,7 @@ async fn writer_task(rx: mpsc::UnboundedReceiver<Vec<u8>>, rdp_writer: WriteHalf
}
}
async fn connect(
struct ConnectParams {
ws: WebSocket,
config: connector::Config,
proxy_auth_token: String,
@ -840,6 +895,20 @@ async fn connect(
pcb: Option<String>,
kdc_proxy_url: Option<String>,
clipboard_backend: Option<WasmClipboardBackend>,
use_display_control: bool,
}
async fn connect(
ConnectParams {
ws,
config,
proxy_auth_token,
destination,
pcb,
kdc_proxy_url,
clipboard_backend,
use_display_control,
}: ConnectParams,
) -> Result<(connector::ConnectionResult, WebSocket), IronRdpError> {
let mut framed = ironrdp_futures::LocalFuturesFramed::new(ws);
@ -849,6 +918,12 @@ async fn connect(
connector.attach_static_channel(CliprdrClient::new(Box::new(clipboard_backend)));
}
if use_display_control {
connector.attach_static_channel(
DrdynvcClient::new().with_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new()))),
);
}
let (upgraded, server_public_key) =
connect_rdcleanpath(&mut framed, &mut connector, destination.clone(), proxy_auth_token, pcb).await?;