feat: RDP profiles, sessions and viewer
- Protocol::Rdp with RdpConfig (domain, password mode, certificate policy, TLS-only fallback, fixed or tab-sized resolution, SOCKS5/HTTP proxy, SSH tunnel); migration 008 adds rdp_config and drops the protocol CHECK constraint so new protocols no longer need a rebuild. - Session manager: connect_rdp dials directly, via the shared proxy module (proxy password from the secret store) or an SSH tunnel; the server certificate goes through the known-hosts check (key type rdp-tls); a tab-sized desktop uses the tab's reported pixel size, and resizes wait until the server opens Display Control. Rejected NLA passwords count as auth failures, so a saved one is asked again. - The VNC viewer becomes DesktopView for both protocols; a small adapter turns its keysyms and pointer masks into RDP scancodes, Unicode keys, buttons and wheel events. For RDP, Paste Clipboard types the text. - Profile editor RDP section, Quick Connect, badges, docs. Verified against xrdp in the app: logon, desktop, input, and connections through SOCKS5 and HTTP CONNECT relays. Not verified here: Windows/NLA servers, the SSH tunnel route, and Display Control resizing (xrdp 0.9.24 never opened the channel).
This commit is contained in:
@@ -209,6 +209,8 @@ impl Client {
|
||||
}
|
||||
.build();
|
||||
let mut input = Database::new();
|
||||
// A resize waits here until the Display Control channel is open (it opens after logon).
|
||||
let mut want_size: Option<(u16, u16)> = None;
|
||||
loop {
|
||||
let mut outputs = Vec::new();
|
||||
loop {
|
||||
@@ -219,12 +221,7 @@ impl Client {
|
||||
outputs.extend(stage.process_fastpath_input(&mut image, &events)?);
|
||||
}
|
||||
}
|
||||
Ok(Input::Resize(w, h)) => {
|
||||
let (w, h) = ironrdp::displaycontrol::pdu::MonitorLayoutEntry::adjust_display_size(w.into(), h.into());
|
||||
if let Some(frame) = stage.encode_resize(w, h, None, None) {
|
||||
outputs.push(ActiveStageOutput::ResponseFrame(frame?));
|
||||
}
|
||||
}
|
||||
Ok(Input::Resize(w, h)) => want_size = Some((w, h)).filter(|&s| s != (image.width(), image.height())),
|
||||
Ok(Input::Close) | Err(mpsc::TryRecvError::Disconnected) => {
|
||||
for o in stage.graceful_shutdown()? {
|
||||
if let ActiveStageOutput::ResponseFrame(f) = o {
|
||||
@@ -236,6 +233,13 @@ impl Client {
|
||||
Err(mpsc::TryRecvError::Empty) => break,
|
||||
}
|
||||
}
|
||||
if let Some((w, h)) = want_size {
|
||||
let (w, h) = ironrdp::displaycontrol::pdu::MonitorLayoutEntry::adjust_display_size(w.into(), h.into());
|
||||
if let Some(frame) = stage.encode_resize(w, h, None, None) {
|
||||
outputs.push(ActiveStageOutput::ResponseFrame(frame?));
|
||||
want_size = None;
|
||||
}
|
||||
}
|
||||
match framed.read_pdu() {
|
||||
Ok((action, payload)) => outputs.extend(stage.process(&mut image, action, &payload)?),
|
||||
Err(e) if matches!(e.kind(), std::io::ErrorKind::WouldBlock | std::io::ErrorKind::TimedOut) => {}
|
||||
|
||||
Reference in New Issue
Block a user