Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: hook up resize for ironrdp-client crate #430

Merged
merged 93 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
7776094
Creates IoChannelPdu enum to be used to account for the potential of …
Mar 9, 2024
4dbb2b3
Abstracts the CapabilitiesExchange and ConnectionFinalization
Mar 11, 2024
08dfb44
Breaks single_connect_step into two separate methods:
Mar 11, 2024
55159db
Sets desktop_resize_flag to true which is required for the Microsoft:…
Mar 12, 2024
89bef14
Removes the explicit state machine from DecodingContext. State comes …
Mar 12, 2024
717d020
Adds todo in ironrdp-web
Mar 20, 2024
d07d522
Proof of concept for the transition from legacy x224::Processor::proc…
Mar 13, 2024
5c22b53
Adds a DynamicChannelSet (similar to the StaticChannelSet) to the DVC
Mar 14, 2024
8a5c5f1
Adds a CommonPdu to consolidate common client/server pdu's for dvcs
Mar 14, 2024
ffb4465
Gets a `DvcClientProcessor` in full working condition
Mar 15, 2024
783b1d4
Starts a display module in ironrdp-dvc
Mar 15, 2024
e1ec8eb
Adds `DvcPduEncode` trait and `MonitorLayoutPdu`
Mar 16, 2024
d72c77c
Adds a `SvcPduEncode` in line with the `DvcPduEncode`
Mar 18, 2024
e93cea0
Removes now no-longer-necesary is_drdynvc. Also fix compilation in se…
Mar 18, 2024
b382e81
Adds CreateResponsePdu, ClosePdu, and CapabilitiesResponsePdu, gets r…
Mar 19, 2024
8ec38e1
Removes all legacy ironrdp-pdu/src/rdp/vc/dvc/ pdus from ironrdp-dvc.…
Mar 19, 2024
c72391a
Moves relevant DataFirst tests from ironrdp-pdu to ironrdp-dvc
Mar 20, 2024
c384c40
Moves relevant Data tests from ironrdp-pdu to ironrdp-dvc
Mar 20, 2024
28c9f68
Simplify naming in tests
Mar 20, 2024
266c735
Moves relevant Create tests from ironrdp-pdu to ironrdp-dvc. Also ena…
Mar 20, 2024
856a0aa
Moves relevant Close tests from ironrdp-pdu to ironrdp-dvc.
Mar 20, 2024
0dea439
Moves relevant Capabilities tests from ironrdp-pdu to ironrdp-dvc.
Mar 20, 2024
69cc89b
Fix test panics
Mar 20, 2024
ead24a5
Delete as much legacy code as possible
Mar 20, 2024
e9cc83a
Creates connection_activation module
Mar 20, 2024
efaaf36
Breaks single_connect_step into two separate methods:
Mar 11, 2024
c4afa98
Sets desktop_resize_flag to true which is required for the Microsoft:…
Mar 12, 2024
4b7725d
Removes the explicit state machine from DecodingContext. State comes …
Mar 12, 2024
422a436
Merge branch 'feat/deactivate-all' into feat/dynamic-resize
Mar 20, 2024
5f0840b
Consolidates the display module into ironrdp-dvc crate.
Mar 20, 2024
bc57654
Consolidate all display control pdus into ironrdp-displaycontrol
Mar 21, 2024
205eacf
cleaning up
Mar 21, 2024
dd4906a
empty flags in lieu of Option
Mar 21, 2024
6e45da9
Changes DATA_MAX_SIZE to be part of DrdynvcDataPdu
Mar 21, 2024
3c493d2
removes all vec![]
Mar 21, 2024
1e2dd93
impl fmt::Display for Cmd
Mar 21, 2024
0a16e2b
add headerless_size
Mar 21, 2024
ceb44b8
removes leftover commented out code
Mar 21, 2024
28b5dc3
Fixes linter errors, adding checked_sum and checked_sum_or_panic util…
Mar 21, 2024
b9669f3
removes pub from FieldType
Mar 21, 2024
fb89407
Removes impl DvcPduEncode for Vec<u8> {} which was no longer necessary
Mar 21, 2024
bc26ef0
Moves tests to ironrdp-testsuite-core
Mar 21, 2024
9503901
Add admonition/TODOs for byteorder, num-derive, num-traits, lazy_static
Mar 21, 2024
368df95
Uses OnceLock instead of lazy_static in the dvc tests
Mar 21, 2024
567e537
Attempts to implement the connection activation sequence rerun upon r…
Mar 21, 2024
83be7c8
Deletes gfx.rs
Mar 21, 2024
764abdd
Adds debug log for Deactivation-Reactivation Sequence completed
Mar 22, 2024
56c05b6
adds break for the deactivation-reactivation loop
Mar 22, 2024
f4658d9
Creates connection_activation module
Mar 20, 2024
bab4181
Breaks single_connect_step into two separate methods:
Mar 11, 2024
074f47d
Sets desktop_resize_flag to true which is required for the Microsoft:…
Mar 12, 2024
66ceda2
Removes the explicit state machine from DecodingContext. State comes …
Mar 12, 2024
d2e315e
Merge branch 'feat/deactivate-all' into feat/dynamic-resize
Mar 23, 2024
371ac3a
Adds size checks to ServerDeactivateAll
Mar 25, 2024
db346b3
Renames *connect_step* to *sequence_step* and moves this family to th…
Mar 25, 2024
3cd8167
Adds missing size check
Mar 25, 2024
1de1666
Fixes ServerDeactivateAll style
Mar 25, 2024
af26842
Merge branch 'feat/deactivate-all' into feat/deactivate-all-impl
Mar 25, 2024
6e51da5
Explain unused_assignments at the point of its declaration
Mar 25, 2024
89b28ac
Merge branch 'feat/deactivate-all' into feat/dynamic-resize
Mar 25, 2024
94c176b
Adds a callback mechanism to the DisplayControlClient which is called…
Mar 26, 2024
6837e94
Automatically adjust the width of a new MonitorLayoutEntry so that it…
Mar 26, 2024
10c2415
Enforces the callback by requiring it in DisplayControlClient::new. A…
Mar 26, 2024
2359d00
Mimics the ironrdp-web deactivation-reactivation sequence in ironrdp-…
Mar 26, 2024
3abe20f
Capitalize debug message
Mar 27, 2024
9921483
Removes Sync from DvcProcessor and OnCapabilitiesReceived
Mar 27, 2024
50c649e
Minor refactors
Mar 28, 2024
fdd2a6b
Merge branch 'master' into feat/deactivate-all-impl
Mar 30, 2024
6945c5d
Adds scale factor and physical dimensions to all monitor layout entri…
Mar 30, 2024
3a30629
Merge branch 'master' into feat/deactivate-all-impl
Apr 1, 2024
0df68b5
Merge branch 'master' into feat/deactivate-all-impl
Apr 2, 2024
622a4f4
Fixes merge error where single_connect_step family was included (now …
Apr 2, 2024
a888bb1
remove unnecessary #[allow(unused_assignments)]
Apr 2, 2024
4811deb
Hooks up resize for `ironrdp-client` crate.
Apr 3, 2024
030469f
Merge branch 'resize-physical-dims' into isaiah/hook-up-resize-ironrd…
Apr 4, 2024
58049ec
Calculates and hooks in scale_factor for resizes
Apr 4, 2024
5229b08
Always use long length for FastPath messages, in line with FreeRDP
Apr 4, 2024
d57965e
broken commit: but adds desktop scale factor to the clientinfo
Apr 8, 2024
2440098
Adds some documentation and cerification to gcc blocks
Apr 17, 2024
83e18b8
Refactors desktop scale factor support, reverts to minimal fast path …
Apr 17, 2024
91c357a
Merge branch 'master' into isaiah/hook-up-resize-ironrdp-client
Apr 24, 2024
7ec0048
Cleaning up the get_dvc_processor api
Apr 24, 2024
347a2d9
Makes `max_unacknowledged_frame_count: 20`, see https://github.com/De…
Apr 24, 2024
8793b16
small tweaks
Apr 24, 2024
1fd4b38
Gets rid of unneeded DynamicVirtualChannel/DynamicVirtualChannelInter…
Apr 25, 2024
a07b8ec
Adds documentation to MonitorLayoutEntry public create methods
Apr 25, 2024
6a7c4f8
return old id
Apr 25, 2024
bda1a96
Removes automatic display adjustment and adds MonitorLayoutEntry::adj…
Apr 25, 2024
4d878fb
reverting rdpversion
Apr 25, 2024
1644ff7
add TODO
Apr 25, 2024
a40adbb
fixing lints
Apr 25, 2024
ab2a8a3
Update crates/ironrdp-connector/src/connection_activation.rs
Apr 26, 2024
09daceb
Removing unneeded Result
Apr 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/ironrdp-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ native-tls = ["ironrdp-tls/native-tls"]
[dependencies]

# Protocols
ironrdp = { workspace = true, features = ["input", "graphics", "dvc", "rdpdr", "rdpsnd", "cliprdr"] }
ironrdp = { workspace = true, features = ["input", "graphics", "dvc", "svc", "rdpdr", "rdpsnd", "cliprdr", "displaycontrol"] }
ironrdp-cliprdr-native.workspace = true
ironrdp-tls.workspace = true
ironrdp-tokio.workspace = true
Expand Down
1 change: 1 addition & 0 deletions crates/ironrdp-client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ impl Config {
width: DEFAULT_WIDTH,
height: DEFAULT_HEIGHT,
},
desktop_scale_factor: 0, // Default to 0 per FreeRDP
bitmap,
client_build: semver::Version::parse(env!("CARGO_PKG_VERSION"))
.map(|version| version.major * 100 + version.minor * 10 + version.patch)
Expand Down
10 changes: 10 additions & 0 deletions crates/ironrdp-client/src/gui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,17 @@ impl GuiContext {
match event {
Event::WindowEvent { window_id, event } if window_id == window.id() => match event {
WindowEvent::Resized(size) => {
let scale_factor = (window.scale_factor() * 100.0) as u32;
// TODO: it should be possible to get the physical size here, however winit doesn't make it straightforward.
// FreeRDP does it based on DPI reading grabbed via [`SDL_GetDisplayDPI`](https://wiki.libsdl.org/SDL2/SDL_GetDisplayDPI):
// https://github.com/FreeRDP/FreeRDP/blob/ba8cf8cf2158018fb7abbedb51ab245f369be813/client/SDL/sdl_monitor.cpp#L250-L262
let (physical_width, physical_height) = (0, 0);
Comment on lines +67 to +70
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Can’t you use this? https://docs.rs/winit/latest/winit/window/struct.Window.html#method.inner_size
Is it really fine to feed in 0s?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inner_size gives you a PhysicalSize, but that only gives you a "size represented in physical pixels." However without knowing the DPI (or other relation to a unit of length), we can't calculate the "physical size" that RDP wants, which is the width/height in millimeters.

If we hand back 0s here, they're simply ignored per the spec:

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ibeckermayer
I think passing in client (0, 0) is not optimal (it is not obvious that they will be ignored), maybe it would be great to change RdpInputEvent::Resize fields to have physical_dimensions: Option<(u32, u32)> instead of separate u32 fields? This will keep this implementation detail as close to PDU encode/decode code as possible

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be fixed in follow-up PR, merging PR to unblock @ibeckermayer

let _ = input_event_sender.send(RdpInputEvent::Resize {
width: u16::try_from(size.width).unwrap(),
height: u16::try_from(size.height).unwrap(),
scale_factor,
physical_width,
physical_height,
});
}
WindowEvent::CloseRequested => {
Expand Down Expand Up @@ -225,6 +233,8 @@ impl GuiContext {
// TODO: is there something we should handle here?
}
Event::UserEvent(RdpOutputEvent::Image { buffer, width, height }) => {
trace!(width = ?width, height = ?height, "Received image with size");
trace!(window_physical_size = ?window.inner_size(), "Drawing image to the window with size");
surface
.resize(
NonZeroU32::new(u32::from(width)).unwrap(),
Expand Down
1 change: 1 addition & 0 deletions crates/ironrdp-client/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ fn main() -> anyhow::Result<()> {
debug!("GUI context initialized");

let window_size = gui.window().inner_size();
config.connector.desktop_scale_factor = 0; // TODO: should this be `(gui.window().scale_factor() * 100.0) as u32`?
config.connector.desktop_size.width = u16::try_from(window_size.width).unwrap();
config.connector.desktop_size.height = u16::try_from(window_size.height).unwrap();

Expand Down
47 changes: 32 additions & 15 deletions crates/ironrdp-client/src/rdp.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use ironrdp::cliprdr::backend::{ClipboardMessage, CliprdrBackendFactory};
use ironrdp::connector::connection_activation::ConnectionActivationState;
use ironrdp::connector::{ConnectionResult, ConnectorResult};
use ironrdp::displaycontrol::client::DisplayControlClient;
use ironrdp::displaycontrol::pdu::MonitorLayoutEntry;
use ironrdp::graphics::image_processing::PixelFormat;
use ironrdp::pdu::input::fast_path::FastPathInputEvent;
use ironrdp::pdu::write_buf::WriteBuf;
Expand Down Expand Up @@ -28,7 +30,13 @@ pub enum RdpOutputEvent {

#[derive(Debug)]
pub enum RdpInputEvent {
Resize { width: u16, height: u16 },
Resize {
width: u16,
height: u16,
scale_factor: u32,
physical_width: u32,
physical_height: u32,
},
FastPath(SmallVec<[FastPathInputEvent; 2]>),
Close,
Clipboard(ClipboardMessage),
Expand Down Expand Up @@ -107,7 +115,9 @@ async fn connect(

let mut connector = connector::ClientConnector::new(config.connector.clone())
.with_server_addr(server_addr)
.with_static_channel(ironrdp::dvc::DrdynvcClient::new())
.with_static_channel(
ironrdp::dvc::DrdynvcClient::new().with_dynamic_channel(DisplayControlClient::new(|_| Ok(Vec::new()))),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From our doc comment:

    /// The `callback` will be called when capabilities are received from the server.
    /// It is important to note that the channel will not be fully operational until the capabilities are received.
    /// Attempting to send messages before the capabilities are received will result in an error or a silent failure.

I think we need to block any dynamic resolution changes from the client until this callback (passed in new) receives some caps. Also, based on caps we should prevent setting resolution to values greater than max monitor area received in caps.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to block any dynamic resolution changes from the client until this callback (passed in new) receives some caps.

This is possibility is already handled here:

if let Some(response_frame) = active_stage.encode_resize(width, height, None, Some((width, height))) { // Set physical width and height to the same as the pixel width and heighbbt per FreeRDP
vec![ActiveStageOutput::ResponseFrame(response_frame?)]
} else {
// TODO(#271): use the "auto-reconnect cookie": https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/15b0d1c9-2891-4adb-a45e-deb4aeeeab7c
debug!("Reconnecting with new size");
return Ok(RdpControlFlow::ReconnectWithNewSize { width: width.try_into().unwrap(), height: height.try_into().unwrap() })
}

I added this callback for Teleport purposes, where we do things a little differently:

  1. Any resizes that occur before the channel is open are held in an internal register
  2. The callback we pass here reads that register and returns the encoded resize, such that the most recent browser size gets sent as soon as the channel is ready for it.

Something similar to that could be implemented in IronRDP, but it's a substantial piece of work compared to what's there now (reconnect if the channel isn't ready).

Also, based on caps we should prevent setting resolution to values greater than max monitor area received in caps.

Seems like a fine check but in practice it's not a big risk, I just added a TODO for this for now 1644ff7

)
.with_static_channel(rdpsnd::Rdpsnd::new())
.with_static_channel(rdpdr::Rdpdr::new(Box::new(NoopRdpdrBackend {}), "IronRDP".to_owned()).with_smartcard(0));

Expand Down Expand Up @@ -177,24 +187,30 @@ async fn active_session(
let input_event = input_event.ok_or_else(|| session::general_err!("GUI is stopped"))?;

match input_event {
RdpInputEvent::Resize { mut width, mut height } => {
// TODO(#105): Add support for Display Update Virtual Channel Extension
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedisp/d2954508-f487-48bc-8731-39743e0854a9
// One approach when this extension is not available is to perform a connection from scratch again.

RdpInputEvent::Resize { mut width, mut height, .. } => {
// Find the last resize event
while let Ok(newer_event) = input_event_receiver.try_recv() {
if let RdpInputEvent::Resize { width: newer_width, height: newer_height } = newer_event {
if let RdpInputEvent::Resize {
width: newer_width,
height: newer_height,
..
} = newer_event {
width = newer_width;
height = newer_height;
}
}

// TODO(#271): use the "auto-reconnect cookie": https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/15b0d1c9-2891-4adb-a45e-deb4aeeeab7c

info!(width, height, "resize event");

return Ok(RdpControlFlow::ReconnectWithNewSize { width, height })
let (width, height) = MonitorLayoutEntry::adjust_display_size(width.into(), height.into());
debug!(width, height, "Adjusted display size");

if let Some(response_frame) = active_stage.encode_resize(width, height, None, Some((width, height))) { // Set physical width and height to the same as the pixel width and heighbbt per FreeRDP
vec![ActiveStageOutput::ResponseFrame(response_frame?)]
} else {
// TODO(#271): use the "auto-reconnect cookie": https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/15b0d1c9-2891-4adb-a45e-deb4aeeeab7c
debug!("Reconnecting with new size");
return Ok(RdpControlFlow::ReconnectWithNewSize { width: width.try_into().unwrap(), height: height.try_into().unwrap() })
}
},
RdpInputEvent::FastPath(events) => {
trace!(?events);
Expand Down Expand Up @@ -307,10 +323,10 @@ async fn active_session(
pointer_software_rendering,
} = connection_activation.state
{
debug!("Deactivation-Reactivation Sequence completed");
debug!(?desktop_size, "Deactivation-Reactivation Sequence completed");
// Update image size with the new desktop size.
image = DecodedImage::new(PixelFormat::RgbA32, desktop_size.width, desktop_size.height);
// Create a new [`FastPathProcessor`] with potentially updated
// io/user channel ids.
// Update the active stage with the new channel IDs and pointer settings.
active_stage.set_fastpath_processor(
fast_path::ProcessorBuilder {
io_channel_id,
Expand All @@ -320,6 +336,7 @@ async fn active_session(
}
.build(),
);
active_stage.set_no_server_pointer(no_server_pointer);
break 'activation_seq;
}
}
Expand Down
22 changes: 16 additions & 6 deletions crates/ironrdp-connector/src/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,11 +660,19 @@ fn create_gcc_blocks<'a>(
dig_product_id: Some(config.dig_product_id.clone()),
connection_type: Some(ConnectionType::Lan),
server_selected_protocol: Some(selected_protocol),
desktop_physical_width: None,
desktop_physical_height: None,
desktop_orientation: None,
desktop_scale_factor: None,
device_scale_factor: None,
desktop_physical_width: Some(0), // 0 per FreeRDP
desktop_physical_height: Some(0), // 0 per FreeRDP
desktop_orientation: if config.desktop_size.width > config.desktop_size.height {
Some(MonitorOrientation::Landscape as u16)
} else {
Some(MonitorOrientation::Portrait as u16)
},
desktop_scale_factor: Some(config.desktop_scale_factor),
device_scale_factor: if config.desktop_scale_factor >= 100 && config.desktop_scale_factor <= 500 {
Some(100)
} else {
Some(0)
},
},
},
security: ClientSecurityData {
Expand Down Expand Up @@ -700,7 +708,9 @@ fn create_client_info_pdu(config: &Config, routing_addr: &SocketAddr) -> rdp::Cl
};

// Default flags for all sessions
let mut flags = ClientInfoFlags::UNICODE
let mut flags = ClientInfoFlags::MOUSE
| ClientInfoFlags::MOUSE_HAS_WHEEL
| ClientInfoFlags::UNICODE
| ClientInfoFlags::DISABLE_CTRL_ALT_DEL
| ClientInfoFlags::LOGON_NOTIFY
| ClientInfoFlags::LOGON_ERRORS
Expand Down
25 changes: 19 additions & 6 deletions crates/ironrdp-connector/src/connection_activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{legacy, Config, ConnectionFinalizationSequence, ConnectorResult, Des
#[derive(Debug, Clone)]
pub struct ConnectionActivationSequence {
pub state: ConnectionActivationState,
pub config: Config,
config: Config,
}

impl ConnectionActivationSequence {
Expand Down Expand Up @@ -127,6 +127,15 @@ impl Sequence for ConnectionActivationSequence {
}
}

// At this point we have already sent a requested desktop size to the server -- either as a part of the
// [`TS_UD_CS_CORE`] (on initial connection) or the [`DISPLAYCONTROL_MONITOR_LAYOUT`] (on resize event).
//
// The server is therefore responding with a desktop size here, which will be close to the requested size but
// may be slightly different due to server-side constraints. We should use this negotiated size for the rest of
// the session.
//
// [TS_UD_CS_CORE]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpbcgr/00f1da4a-ee9c-421a-852f-c19f92343d73
// [DISPLAYCONTROL_MONITOR_LAYOUT]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedisp/ea2de591-9203-42cd-9908-be7a55237d1c
let desktop_size = capability_sets
.iter()
.find_map(|c| match c {
Expand All @@ -142,7 +151,7 @@ impl Sequence for ConnectionActivationSequence {
});

let client_confirm_active = rdp::headers::ShareControlPdu::ClientConfirmActive(
create_client_confirm_active(&self.config, capability_sets),
create_client_confirm_active(&self.config, capability_sets, desktop_size),
);

debug!(message = ?client_confirm_active, "Send");
Expand Down Expand Up @@ -249,6 +258,7 @@ const DEFAULT_POINTER_CACHE_SIZE: u16 = 32;
fn create_client_confirm_active(
config: &Config,
mut server_capability_sets: Vec<CapabilitySet>,
desktop_size: DesktopSize,
) -> rdp::capability_sets::ClientConfirmActive {
use ironrdp_pdu::rdp::capability_sets::*;

Expand Down Expand Up @@ -276,8 +286,8 @@ fn create_client_confirm_active(
}),
CapabilitySet::Bitmap(Bitmap {
pref_bits_per_pix: 32,
desktop_width: config.desktop_size.width,
desktop_height: config.desktop_size.height,
desktop_width: desktop_size.width,
desktop_height: desktop_size.height,
// This is required to be true in order for the Microsoft::Windows::RDS::DisplayControl DVC to work.
desktop_resize_flag: true,
drawing_flags,
Expand Down Expand Up @@ -355,7 +365,10 @@ fn create_client_confirm_active(
})),
}])),
CapabilitySet::FrameAcknowledge(FrameAcknowledge {
max_unacknowledged_frame_count: 2,
// FIXME(#447): Revert this to 2 per FreeRDP.
// This is a temporary hack to fix a resize bug, see:
// https://github.com/Devolutions/IronRDP/issues/447
max_unacknowledged_frame_count: 20,
}),
]);

Expand All @@ -364,7 +377,7 @@ fn create_client_confirm_active(
.any(|c| matches!(&c, CapabilitySet::MultiFragmentUpdate(_)))
{
server_capability_sets.push(CapabilitySet::MultiFragmentUpdate(MultifragmentUpdate {
max_request_size: 1024,
max_request_size: 8 * 1024 * 1024, // 8 MB
}));
}

Expand Down
4 changes: 4 additions & 0 deletions crates/ironrdp-connector/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ impl Credentials {
pub struct Config {
/// The initial desktop size to request
pub desktop_size: DesktopSize,
/// The initial desktop scale factor to request.
///
/// This becomes the `desktop_scale_factor` in the [`TS_UD_CS_CORE`](gcc::ClientCoreOptionalData) structure.
ibeckermayer marked this conversation as resolved.
Show resolved Hide resolved
pub desktop_scale_factor: u32,
/// TLS + Graphical login (legacy)
///
/// Also called SSL or TLS security protocol.
Expand Down
18 changes: 17 additions & 1 deletion crates/ironrdp-displaycontrol/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,29 @@ impl DisplayControlClient {

/// Builds a [`DisplayControlPdu::MonitorLayout`] with a single primary monitor
/// with the given `width` and `height`, and wraps it as an [`SvcMessage`].
///
/// Per [2.2.2.2.1]:
/// - The `width` MUST be greater than or equal to 200 pixels and less than or equal to 8192 pixels, and MUST NOT be an odd value.
/// - The `height` MUST be greater than or equal to 200 pixels and less than or equal to 8192 pixels.
/// - The `scale_factor` MUST be ignored if it is less than 100 percent or greater than 500 percent.
/// - The `physical_dims` (width, height) MUST be ignored if either is less than 10 mm or greater than 10,000 mm.
///
/// Use [`crate::pdu::MonitorLayoutEntry::adjust_display_size`] to adjust `width` and `height` before calling this function
/// to ensure the display size is within the valid range.
///
/// [2.2.2.2.2]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpedisp/ea2de591-9203-42cd-9908-be7a55237d1c
pub fn encode_single_primary_monitor(
&self,
channel_id: u32,
width: u32,
height: u32,
scale_factor: Option<u32>,
physical_dims: Option<(u32, u32)>,
) -> PduResult<Vec<SvcMessage>> {
let pdu: DisplayControlPdu = DisplayControlMonitorLayout::new_single_primary_monitor(width, height)?.into();
// TODO: prevent resolution with values greater than max monitor area received in caps.
let pdu: DisplayControlPdu =
DisplayControlMonitorLayout::new_single_primary_monitor(width, height, scale_factor, physical_dims)?.into();
debug!(?pdu, "Sending monitor layout");
encode_dvc_messages(channel_id, vec![Box::new(pdu)], ChannelFlags::empty())
}
}
Expand Down
Loading