diff --git a/Cargo.lock b/Cargo.lock index fe812e43b..ddd2cc331 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2826,8 +2826,6 @@ dependencies = [ "resize", "rgb", "semver", - "serde", - "serde-wasm-bindgen", "smallvec", "softbuffer", "tap", @@ -4715,17 +4713,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8302e169f0eddcc139c70f139d19d6467353af16f9fce27e8c30158036a1e16b" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde_bytes" version = "0.11.17" diff --git a/crates/iron-remote-desktop/src/desktop_size.rs b/crates/iron-remote-desktop/src/desktop_size.rs new file mode 100644 index 000000000..0ad3c5179 --- /dev/null +++ b/crates/iron-remote-desktop/src/desktop_size.rs @@ -0,0 +1,15 @@ +use wasm_bindgen::prelude::wasm_bindgen; + +#[wasm_bindgen] +#[derive(Clone, Copy)] +pub struct DesktopSize { + pub width: u16, + pub height: u16, +} + +#[wasm_bindgen] +impl DesktopSize { + pub fn init(width: u16, height: u16) -> Self { + DesktopSize { width, height } + } +} diff --git a/crates/iron-remote-desktop/src/extension.rs b/crates/iron-remote-desktop/src/extension.rs new file mode 100644 index 000000000..c17c95784 --- /dev/null +++ b/crates/iron-remote-desktop/src/extension.rs @@ -0,0 +1,68 @@ +use wasm_bindgen::prelude::wasm_bindgen; +use wasm_bindgen::JsValue; + +#[macro_export] +macro_rules! extension_match { + ( @ $jsval:expr, $value:ident, String, $operation:block ) => {{ + if let Some($value) = $jsval.as_string() { + $operation + } else { + warn!("Unexpected value for extension {}", stringify!($ident)); + } + }}; + ( @ $jsval:expr, $value:ident, f64, $operation:block ) => {{ + if let Some($value) = $jsval.as_f64() { + $operation + } else { + warn!("Unexpected value for extension {}", stringify!($ident)); + } + }}; + ( @ $jsval:expr, $value:ident, bool, $operation:block ) => {{ + if let Some($value) = $jsval.as_bool() { + $operation + } else { + warn!("Unexpected value for extension {}", stringify!($ident)); + } + }}; + ( @ $jsval:expr, $value:ident, JsValue, $operation:block ) => {{ + let $value = $jsval; + $operation + }}; + + ( match $ext:ident ; $( | $value:ident : $ty:ident | $operation:block ; )* ) => { + let ident = $ext.ident(); + + match ident { + $( stringify!($value) => $crate::extension_match!( @ $ext.into_value(), $value, $ty, $operation ), )* + unknown_extension => ::tracing::warn!("Unknown extension: {unknown_extension}"), + } + }; +} + +#[wasm_bindgen] +pub struct Extension { + ident: String, + value: JsValue, +} + +#[wasm_bindgen] +impl Extension { + #[wasm_bindgen(constructor)] + pub fn new(ident: String, value: JsValue) -> Self { + Self { ident, value } + } +} + +impl Extension { + pub fn ident(&self) -> &str { + self.ident.as_str() + } + + pub fn value(&self) -> &JsValue { + &self.value + } + + pub fn into_value(self) -> JsValue { + self.value + } +} diff --git a/crates/iron-remote-desktop/src/lib.rs b/crates/iron-remote-desktop/src/lib.rs index ad19efb26..adc14e7a8 100644 --- a/crates/iron-remote-desktop/src/lib.rs +++ b/crates/iron-remote-desktop/src/lib.rs @@ -1,29 +1,18 @@ mod clipboard; mod cursor; +mod desktop_size; mod error; +mod extension; mod input; mod session; pub use clipboard::{ClipboardContent, ClipboardTransaction}; pub use cursor::CursorStyle; +pub use desktop_size::DesktopSize; pub use error::{IronError, IronErrorKind}; +pub use extension::Extension; pub use input::{DeviceEvent, InputTransaction}; pub use session::{Session, SessionBuilder, SessionTerminationInfo}; -use wasm_bindgen::prelude::wasm_bindgen; - -#[wasm_bindgen] -#[derive(Clone, Copy)] -pub struct DesktopSize { - pub width: u16, - pub height: u16, -} - -#[wasm_bindgen] -impl DesktopSize { - pub fn init(width: u16, height: u16) -> Self { - DesktopSize { width, height } - } -} pub fn iron_init(log_level: &str) { // When the `console_error_panic_hook` feature is enabled, we can call the @@ -229,8 +218,8 @@ macro_rules! export { self.0.supports_unicode_keyboard_shortcuts() } - pub fn extension_call(value: JsValue) -> Result { - <<$api as RemoteDesktopApi>::Session>::extension_call(value).map_err(IronError) + pub fn extension_call(ext: $crate::Extension) -> Result { + <<$api as RemoteDesktopApi>::Session>::extension_call(ext).map_err(IronError) } } @@ -295,8 +284,8 @@ macro_rules! export { Self(self.0.force_clipboard_update_callback(callback)) } - pub fn extension(&self, value: JsValue) -> Self { - Self(self.0.extension(value)) + pub fn extension(&self, ext: $crate::Extension) -> Self { + Self(self.0.extension(ext)) } pub async fn connect(&self) -> Result { diff --git a/crates/iron-remote-desktop/src/session.rs b/crates/iron-remote-desktop/src/session.rs index 657095138..705c32460 100644 --- a/crates/iron-remote-desktop/src/session.rs +++ b/crates/iron-remote-desktop/src/session.rs @@ -4,7 +4,7 @@ use web_sys::{js_sys, HtmlCanvasElement}; use crate::clipboard::ClipboardTransaction; use crate::error::IronError; use crate::input::InputTransaction; -use crate::DesktopSize; +use crate::{DesktopSize, Extension}; pub trait SessionBuilder { type Session: Session; @@ -38,7 +38,7 @@ pub trait SessionBuilder { #[must_use] fn force_clipboard_update_callback(&self, callback: js_sys::Function) -> Self; #[must_use] - fn extension(&self, value: JsValue) -> Self; + fn extension(&self, ext: Extension) -> Self; #[expect(async_fn_in_trait)] async fn connect(&self) -> Result; } @@ -74,7 +74,7 @@ pub trait Session { physical_height: Option, ); fn supports_unicode_keyboard_shortcuts(&self) -> bool; - fn extension_call(value: JsValue) -> Result; + fn extension_call(ext: Extension) -> Result; } pub trait SessionTerminationInfo { diff --git a/crates/ironrdp-web/Cargo.toml b/crates/ironrdp-web/Cargo.toml index 927db28ea..a2d0f9d26 100644 --- a/crates/ironrdp-web/Cargo.toml +++ b/crates/ironrdp-web/Cargo.toml @@ -46,8 +46,6 @@ web-sys = { version = "0.3", features = ["HtmlCanvasElement"] } js-sys = "0.3" gloo-net = { version = "0.6", default-features = false, features = ["websocket", "http", "io-util"] } gloo-timers = { version = "0.3", default-features = false, features = ["futures"] } -serde = { version = "1.0", features = ["derive"] } -serde-wasm-bindgen = "0.6" # Rendering softbuffer = { version = "0.4", default-features = false } diff --git a/crates/ironrdp-web/src/session.rs b/crates/ironrdp-web/src/session.rs index d0230c3ba..391821f9e 100644 --- a/crates/ironrdp-web/src/session.rs +++ b/crates/ironrdp-web/src/session.rs @@ -1,6 +1,3 @@ -// https://github.com/rustwasm/wasm-bindgen/issues/4080 -#![allow(non_snake_case)] - use core::cell::RefCell; use core::num::NonZeroU32; use core::time::Duration; @@ -14,7 +11,7 @@ use futures_util::io::{ReadHalf, WriteHalf}; use futures_util::{select, AsyncWriteExt as _, FutureExt as _, StreamExt as _}; use gloo_net::websocket; use gloo_net::websocket::futures::WebSocket; -use iron_remote_desktop::{CursorStyle, DesktopSize, IronErrorKind}; +use iron_remote_desktop::{CursorStyle, DesktopSize, Extension, IronErrorKind}; use ironrdp::cliprdr::backend::ClipboardMessage; use ironrdp::cliprdr::CliprdrClient; use ironrdp::connector::connection_activation::ConnectionActivationState; @@ -30,7 +27,6 @@ use ironrdp::session::{fast_path, ActiveStage, ActiveStageOutput, GracefulDiscon use ironrdp_core::WriteBuf; use ironrdp_futures::{single_sequence_step_read, FramedWrite}; use rgb::AsPixels as _; -use serde::{Deserialize, Serialize}; use tap::prelude::*; use wasm_bindgen::JsValue; use wasm_bindgen_futures::spawn_local; @@ -207,16 +203,12 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder { self.clone() } - fn extension(&self, value: JsValue) -> Self { - match serde_wasm_bindgen::from_value::(value) { - Ok(value) => match value { - Extension::KdcProxyUrl(kdc_proxy_url) => self.0.borrow_mut().kdc_proxy_url = Some(kdc_proxy_url), - Extension::Pcb(pcb) => self.0.borrow_mut().pcb = Some(pcb), - Extension::DisplayControl(use_display_control) => { - self.0.borrow_mut().use_display_control = use_display_control - } - }, - Err(error) => error!(%error, "Unsupported extension value"), + fn extension(&self, ext: Extension) -> Self { + iron_remote_desktop::extension_match! { + match ext; + |pcb: String| { self.0.borrow_mut().pcb = Some(pcb) }; + |kdc_proxy_url: String| { self.0.borrow_mut().kdc_proxy_url = Some(kdc_proxy_url) }; + |display_control: bool| { self.0.borrow_mut().use_display_control = display_control }; } self.clone() @@ -354,13 +346,6 @@ impl iron_remote_desktop::SessionBuilder for SessionBuilder { } } -#[derive(Debug, Serialize, Deserialize)] -enum Extension { - KdcProxyUrl(String), - Pcb(String), - DisplayControl(bool), -} - pub(crate) type FastPathInputEvents = smallvec::SmallVec<[FastPathInputEvent; 2]>; #[derive(Debug)] @@ -813,13 +798,16 @@ impl iron_remote_desktop::Session for Session { } fn supports_unicode_keyboard_shortcuts(&self) -> bool { - // RDP does not support Unicode keyboard shortcuts (When key combinations are executed, only - // plain scancode events are allowed to function correctly). + // RDP does not support Unicode keyboard shortcuts. + // When key combinations are executed, only plain scancode events are allowed to function correctly. false } - fn extension_call(_value: JsValue) -> Result { - Ok(JsValue::null()) + fn extension_call(ext: Extension) -> Result { + Err( + IronError::from(anyhow::Error::msg(format!("unknown extension: {}", ext.ident()))) + .with_kind(IronErrorKind::General), + ) } } diff --git a/web-client/iron-remote-desktop-rdp/src/interfaces/Extension.ts b/web-client/iron-remote-desktop-rdp/src/interfaces/Extension.ts deleted file mode 100644 index c912a2808..000000000 --- a/web-client/iron-remote-desktop-rdp/src/interfaces/Extension.ts +++ /dev/null @@ -1,28 +0,0 @@ -type ExtensionValue = { Pcb: string } | { KdcProxyUrl: string } | { DisplayControl: boolean }; - -export class Extension { - static init(ident: string, value: unknown): ExtensionValue { - switch (ident) { - case 'Pcb': - if (typeof value === 'string') { - return { Pcb: value }; - } else { - throw new Error('Pcb must be a string'); - } - case 'KdcProxyUrl': - if (typeof value === 'string') { - return { KdcProxyUrl: value }; - } else { - throw new Error('KdcProxyUrl must be a string'); - } - case 'DisplayControl': - if (typeof value === 'boolean') { - return { DisplayControl: value }; - } else { - throw new Error('DisplayControl must be a boolean'); - } - default: - throw new Error(`Invalid extension type: ${ident}`); - } - } -} diff --git a/web-client/iron-remote-desktop-rdp/src/main.ts b/web-client/iron-remote-desktop-rdp/src/main.ts index feab35f48..67c59cb45 100644 --- a/web-client/iron-remote-desktop-rdp/src/main.ts +++ b/web-client/iron-remote-desktop-rdp/src/main.ts @@ -9,8 +9,8 @@ import init, { SessionTerminationInfo, ClipboardTransaction, ClipboardContent, + Extension, } from '../../../crates/ironrdp-web/pkg/ironrdp_web'; -import { Extension } from './interfaces/Extension'; export default { init, @@ -26,3 +26,15 @@ export default { SessionTerminationInfo, Extension, }; + +export function preConnectionBlob(pcb: string): Extension { + return new Extension('pcb', pcb); +} + +export function displayControl(enable: boolean): Extension { + return new Extension('display_control', enable); +} + +export function kdcProxyUrl(url: string): Extension { + return new Extension('kdc_proxy_url', url); +} diff --git a/web-client/iron-remote-desktop/src/interfaces/Extension.ts b/web-client/iron-remote-desktop/src/interfaces/Extension.ts index 7c792c15c..ad711ea55 100644 --- a/web-client/iron-remote-desktop/src/interfaces/Extension.ts +++ b/web-client/iron-remote-desktop/src/interfaces/Extension.ts @@ -1,5 +1 @@ -import type { ExtensionValue } from './ExtensionValue'; - -export interface Extension { - init(ident: string, value: unknown): ExtensionValue; -} +export type Extension = unknown; diff --git a/web-client/iron-remote-desktop/src/interfaces/ExtensionValue.ts b/web-client/iron-remote-desktop/src/interfaces/ExtensionValue.ts deleted file mode 100644 index a43a26eba..000000000 --- a/web-client/iron-remote-desktop/src/interfaces/ExtensionValue.ts +++ /dev/null @@ -1 +0,0 @@ -export type ExtensionValue = unknown; diff --git a/web-client/iron-remote-desktop/src/interfaces/RemoteDesktopModule.ts b/web-client/iron-remote-desktop/src/interfaces/RemoteDesktopModule.ts index c12bddca3..48151b70d 100644 --- a/web-client/iron-remote-desktop/src/interfaces/RemoteDesktopModule.ts +++ b/web-client/iron-remote-desktop/src/interfaces/RemoteDesktopModule.ts @@ -7,7 +7,6 @@ import type { SessionBuilder } from './SessionBuilder'; import type { SessionTerminationInfo } from './SessionTerminationInfo'; import type { ClipboardTransaction } from './ClipboardTransaction'; import type { ClipboardContent } from './ClipboardContent'; -import type { Extension } from './Extension'; export interface RemoteDesktopModule { init: () => Promise; @@ -21,5 +20,4 @@ export interface RemoteDesktopModule { SessionTerminationInfo: SessionTerminationInfo; ClipboardTransaction: ClipboardTransaction; ClipboardContent: ClipboardContent; - Extension: Extension; } diff --git a/web-client/iron-remote-desktop/src/services/Config.ts b/web-client/iron-remote-desktop/src/services/Config.ts index b3adf647b..c60a7c87f 100644 --- a/web-client/iron-remote-desktop/src/services/Config.ts +++ b/web-client/iron-remote-desktop/src/services/Config.ts @@ -1,5 +1,5 @@ import type { DesktopSize } from '../interfaces/DesktopSize'; -import type { ExtensionValue } from '../interfaces/ExtensionValue'; +import type { Extension } from '../interfaces/Extension'; export class Config { readonly username: string; @@ -9,7 +9,7 @@ export class Config { readonly serverDomain: string; readonly authToken: string; readonly desktopSize?: DesktopSize; - readonly extensions: ExtensionValue[]; + readonly extensions: Extension[]; constructor( userData: { username: string; password: string }, @@ -17,7 +17,7 @@ export class Config { configOptions: { destination: string; serverDomain: string; - extensions: ExtensionValue[]; + extensions: Extension[]; desktopSize?: DesktopSize; }, ) { diff --git a/web-client/iron-remote-desktop/src/services/ConfigBuilder.ts b/web-client/iron-remote-desktop/src/services/ConfigBuilder.ts index ad291ac51..341deefb0 100644 --- a/web-client/iron-remote-desktop/src/services/ConfigBuilder.ts +++ b/web-client/iron-remote-desktop/src/services/ConfigBuilder.ts @@ -1,8 +1,6 @@ import type { DesktopSize } from '../interfaces/DesktopSize'; import { Config } from './Config'; -import type { ExtensionValue } from '../interfaces/ExtensionValue'; - -type ExtensionConstructor = (ident: string, value: unknown) => ExtensionValue; +import type { Extension } from '../interfaces/Extension'; /** * Builder class for creating Config objects with a fluent interface. @@ -19,8 +17,6 @@ type ExtensionConstructor = (ident: string, value: unknown) => ExtensionValue; * ``` */ export class ConfigBuilder { - private extensionConstructor: ExtensionConstructor; - private username: string = ''; private password: string = ''; private destination: string = ''; @@ -28,17 +24,12 @@ export class ConfigBuilder { private serverDomain: string = ''; private authToken: string = ''; private desktopSize?: DesktopSize; - - private extensions: ExtensionValue[] = []; + private extensions: Extension[] = []; /** * Creates a new ConfigBuilder instance. - * - * @param extensionConstructor - Function that creates extension values from identifiers and values. */ - constructor(extensionConstructor: ExtensionConstructor) { - this.extensionConstructor = extensionConstructor; - } + constructor() {} /** * Optional parameter @@ -109,12 +100,11 @@ export class ConfigBuilder { /** * Optional parameter * - * @param ident - The identifier for the extension - * @param value - The value for the extension + * @param ext - The extension * @returns The builder instance for method chaining */ - withExtension(ident: string, value: unknown): ConfigBuilder { - this.extensions.push(this.extensionConstructor(ident, value)); + withExtension(ext: Extension): ConfigBuilder { + this.extensions.push(ext); return this; } diff --git a/web-client/iron-remote-desktop/src/services/remote-desktop.service.ts b/web-client/iron-remote-desktop/src/services/remote-desktop.service.ts index c4e451725..7b74899f0 100644 --- a/web-client/iron-remote-desktop/src/services/remote-desktop.service.ts +++ b/web-client/iron-remote-desktop/src/services/remote-desktop.service.ts @@ -137,7 +137,7 @@ export class RemoteDesktopService { } configBuilder(): ConfigBuilder { - return new ConfigBuilder(this.module.Extension.init); + return new ConfigBuilder(); } connect(config: Config): Observable { diff --git a/web-client/iron-svelte-client/src/lib/login/login.svelte b/web-client/iron-svelte-client/src/lib/login/login.svelte index e2c49ac1b..4000ae2e2 100644 --- a/web-client/iron-svelte-client/src/lib/login/login.svelte +++ b/web-client/iron-svelte-client/src/lib/login/login.svelte @@ -2,6 +2,7 @@ import { currentSession, userInteractionService } from '../../services/session.service'; import { catchError, filter } from 'rxjs/operators'; import type { UserInteraction, NewSessionInfo } from '../../../static/iron-remote-desktop'; + import { preConnectionBlob, displayControl, kdcProxyUrl } from '../../../static/iron-remote-desktop-rdp'; import { from, of } from 'rxjs'; import { toast } from '$lib/messages/message-store'; import { showLogin } from '$lib/login/login-store'; @@ -125,15 +126,16 @@ .withServerDomain(domain) .withAuthToken(authtoken) .withDesktopSize(desktopSize) - .withExtension('DisplayControl', true); + .withExtension(displayControl(true)); if (pcb !== '') { - configBuilder.withExtension('Pcb', pcb); + configBuilder.withExtension(preConnectionBlob(pcb)); } if (kdc_proxy_url !== '') { - configBuilder.withExtension('KdcProxyUrl', kdc_proxy_url); + configBuilder.withExtension(kdcProxyUrl(kdc_proxy_url)); } + const config = configBuilder.build(); from(userInteraction.connect(config)) diff --git a/web-client/iron-svelte-client/src/lib/popup-screen/popup-screen.svelte b/web-client/iron-svelte-client/src/lib/popup-screen/popup-screen.svelte index aa016a73d..6ec7b69f9 100644 --- a/web-client/iron-svelte-client/src/lib/popup-screen/popup-screen.svelte +++ b/web-client/iron-svelte-client/src/lib/popup-screen/popup-screen.svelte @@ -3,17 +3,18 @@ import { setCurrentSessionActive, userInteractionService } from '../../services/session.service'; import type { UserInteraction } from '../../../static/iron-remote-desktop'; import IronRdp from '../../../static/iron-remote-desktop-rdp'; + import { preConnectionBlob, displayControl, kdcProxyUrl } from '../../../static/iron-remote-desktop-rdp'; - let uiService: UserInteraction; + let userInteraction: UserInteraction; let cursorOverrideActive = false; let showUtilityBar = false; - userInteractionService.subscribe((uis) => { - if (uis != null) { - uiService = uis; - uiService.onSessionEvent((event) => { + userInteractionService.subscribe((val) => { + if (val != null) { + userInteraction = val; + userInteraction.onSessionEvent((event) => { if (event.type === 0) { - uiService.setVisibility(true); + userInteraction.setVisibility(true); } else if (event.type === 1) { setCurrentSessionActive(false); } @@ -23,7 +24,7 @@ userInteractionService.subscribe((uis) => { if (uis != null) { - uiService = uis; + userInteraction = uis; //read query params named data const urlParams = new URLSearchParams(window.location.search); const data = urlParams.get('data'); @@ -36,7 +37,7 @@ const { hostname, gatewayAddress, domain, username, password, authtoken, kdc_proxy_url, pcb, desktopSize } = parsedData; - const configBuilder = uiService + const configBuilder = userInteraction .configBuilder() .withUsername(username) .withPassword(password) @@ -45,19 +46,20 @@ .withServerDomain(domain) .withAuthToken(authtoken) .withDesktopSize(desktopSize) - .withExtension('DisplayControl', true); + .withExtension(displayControl(true)); if (pcb !== '') { - configBuilder.withExtension('Pcb', pcb); + configBuilder.withExtension(preConnectionBlob(pcb)); } if (kdc_proxy_url !== '') { - configBuilder.withExtension('KdcProxyUrl', kdc_proxy_url); + configBuilder.withExtension(kdcProxyUrl(kdc_proxy_url)); } + const config = configBuilder.build(); - uiService.connect(config).then(() => { - uiService.setVisibility(true); + userInteraction.connect(config).then(() => { + userInteraction.setVisibility(true); window.onresize = onWindowResize; }); } @@ -66,7 +68,7 @@ function onWindowResize() { const innerWidth = window.innerWidth; const innerHeight = window.innerHeight; - uiService.resize(innerWidth, innerHeight); + userInteraction.resize(innerWidth, innerHeight); } function onUnicodeModeChange(e: MouseEvent) { @@ -80,14 +82,14 @@ return; } - uiService.setKeyboardUnicodeMode(element.checked); + userInteraction.setKeyboardUnicodeMode(element.checked); } function toggleCursorKind() { if (cursorOverrideActive) { - uiService.setCursorStyleOverride(null); + userInteraction.setCursorStyleOverride(null); } else { - uiService.setCursorStyleOverride('url("crosshair.png") 7 7, default'); + userInteraction.setCursorStyleOverride('url("crosshair.png") 7 7, default'); } cursorOverrideActive = !cursorOverrideActive; @@ -125,8 +127,8 @@
- - + - +