wraith/src-tauri/src/commands/rdp_commands.rs
Vantz Stockwell c75da74ecd feat: Phase 4 complete — RDP via ironrdp
Rust RDP service: ironrdp client with full connection handshake
(TCP -> TLS -> CredSSP -> NLA), pixel buffer frame delivery,
mouse/keyboard input via scancode mapping, graceful disconnect.
Runs in dedicated thread with own tokio runtime to avoid Send
lifetime issues with ironrdp trait objects.

Vue frontend: RdpView canvas renderer with 30fps polling,
mouse/keyboard capture, RdpToolbar with Ctrl+Alt+Del and
clipboard. SessionContainer handles both SSH and RDP tabs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:05:11 -04:00

95 lines
2.5 KiB
Rust

//! Tauri commands for RDP session management.
//!
//! Mirrors the pattern used by `ssh_commands.rs` — thin command wrappers that
//! delegate to the `RdpService` via `State<AppState>`.
use serde::Deserialize;
use tauri::State;
use crate::rdp::{RdpConfig, RdpSessionInfo};
use crate::AppState;
/// Connect to an RDP server.
///
/// Performs the full connection handshake (TCP -> TLS -> CredSSP -> RDP) and
/// starts streaming frame updates in the background.
///
/// Returns the session UUID.
#[tauri::command]
pub fn connect_rdp(
config: RdpConfig,
state: State<'_, AppState>,
) -> Result<String, String> {
state.rdp.connect(config)
}
/// Get the current frame buffer as a base64-encoded RGBA string.
///
/// The frontend decodes this and draws it onto a `<canvas>` element.
/// Pixel format: RGBA, 4 bytes per pixel, row-major, top-left origin.
#[tauri::command]
pub async fn rdp_get_frame(
session_id: String,
state: State<'_, AppState>,
) -> Result<String, String> {
state.rdp.get_frame(&session_id).await
}
/// Send a mouse event to an RDP session.
///
/// `flags` uses MS-RDPBCGR mouse event flags:
/// - 0x0800 = move
/// - 0x1000 = left button
/// - 0x2000 = right button
/// - 0x4000 = middle button
/// - 0x8000 = button pressed (absence = released)
/// - 0x0200 = vertical wheel
/// - 0x0100 = negative wheel direction
/// - 0x0400 = horizontal wheel
#[tauri::command]
pub async fn rdp_send_mouse(
session_id: String,
x: u16,
y: u16,
flags: u32,
state: State<'_, AppState>,
) -> Result<(), String> {
state.rdp.send_mouse(&session_id, x, y, flags)
}
/// Send a keyboard event to an RDP session.
///
/// `scancode` is the RDP hardware scancode (from the scancode map in
/// `rdp::input`). For extended keys (e.g. arrows, numpad enter), the high
/// byte is 0xE0.
///
/// `pressed` is `true` for key-down, `false` for key-up.
#[tauri::command]
pub async fn rdp_send_key(
session_id: String,
scancode: u16,
pressed: bool,
state: State<'_, AppState>,
) -> Result<(), String> {
state.rdp.send_key(&session_id, scancode, pressed)
}
/// Disconnect an RDP session.
///
/// Sends a graceful shutdown to the RDP server and removes the session.
#[tauri::command]
pub async fn disconnect_rdp(
session_id: String,
state: State<'_, AppState>,
) -> Result<(), String> {
state.rdp.disconnect(&session_id)
}
/// List all active RDP sessions (metadata only).
#[tauri::command]
pub async fn list_rdp_sessions(
state: State<'_, AppState>,
) -> Result<Vec<RdpSessionInfo>, String> {
Ok(state.rdp.list_sessions())
}