refactor(web): consolidate WASM object constructors as create (#776)

This commit is contained in:
Benoît Cortier 2025-04-23 19:56:11 +02:00 committed by GitHub
parent aef4b924aa
commit 24e64d7589
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 174 additions and 192 deletions

View file

@ -14,18 +14,11 @@ export async function init(log_level: string) {
}
export const Backend = {
createDesktopSize: DesktopSize.init,
createMouseButtonPressed: DeviceEvent.mouse_button_pressed,
createMouseButtonReleased: DeviceEvent.mouse_button_released,
createMouseMove: DeviceEvent.mouse_move,
createWheelRotations: DeviceEvent.wheel_rotations,
createKeyPressed: DeviceEvent.key_pressed,
createKeyReleased: DeviceEvent.key_released,
createUnicodePressed: DeviceEvent.unicode_pressed,
createUnicodeReleased: DeviceEvent.unicode_released,
createInputTransaction: InputTransaction.init,
createSessionBuilder: SessionBuilder.init,
createClipboardData: ClipboardData.init,
DesktopSize: DesktopSize,
InputTransaction: InputTransaction,
SessionBuilder: SessionBuilder,
ClipboardData: ClipboardData,
DeviceEvent: DeviceEvent,
};
export function preConnectionBlob(pcb: string): Extension {

View file

@ -1,8 +1,8 @@
import type { ClipboardItem } from './ClipboardItem';
export interface ClipboardData {
add_text(mime_type: string, text: string): void;
add_binary(mime_type: string, binary: Uint8Array): void;
addText(mimeType: string, text: string): void;
addBinary(mimeType: string, binary: Uint8Array): void;
items(): ClipboardItem[];
is_empty(): boolean;
isEmpty(): boolean;
}

View file

@ -1,4 +1,4 @@
export interface ClipboardItem {
mime_type(): string;
mimeType(): string;
value(): string | Uint8Array;
}

View file

@ -1,6 +1,5 @@
import type { DeviceEvent } from './DeviceEvent';
export interface InputTransaction {
init(): InputTransaction;
add_event(event: DeviceEvent): void;
addEvent(event: DeviceEvent): void;
}

View file

@ -1,7 +1,7 @@
import type { DesktopSize } from './DesktopSize';
export interface NewSessionInfo {
session_id: number;
websocket_port: number;
initial_desktop_size: DesktopSize;
sessionId: number;
websocketPort: number;
initialDesktopSize: DesktopSize;
}

View file

@ -5,16 +5,18 @@ import type { SessionBuilder } from './SessionBuilder';
import type { ClipboardData } from './ClipboardData';
export interface RemoteDesktopModule {
createDesktopSize(width: number, height: number): DesktopSize;
createMouseButtonPressed(button: number): DeviceEvent;
createMouseButtonReleased(button: number): DeviceEvent;
createMouseMove(x: number, y: number): DeviceEvent;
createWheelRotations(vertical: boolean, rotation_units: number): DeviceEvent;
createKeyPressed(scancode: number): DeviceEvent;
createKeyReleased(scancode: number): DeviceEvent;
createUnicodePressed(unicode: string): DeviceEvent;
createUnicodeReleased(unicode: string): DeviceEvent;
createInputTransaction(): InputTransaction;
createSessionBuilder(): SessionBuilder;
createClipboardData(): ClipboardData;
DesktopSize: { new (width: number, height: number): DesktopSize };
InputTransaction: { new (): InputTransaction };
SessionBuilder: { new (): SessionBuilder };
ClipboardData: { new (): ClipboardData };
DeviceEvent: {
mouseButtonPressed(button: number): DeviceEvent;
mouseButtonReleased(button: number): DeviceEvent;
mouseMove(x: number, y: number): DeviceEvent;
wheelRotations(vertical: boolean, rotationUnits: number): DeviceEvent;
keyPressed(scancode: number): DeviceEvent;
keyReleased(scancode: number): DeviceEvent;
unicodePressed(unicode: string): DeviceEvent;
unicodeReleased(unicode: string): DeviceEvent;
};
}

View file

@ -1,6 +1,6 @@
import type { DesktopSize } from './DesktopSize';
export interface ResizeEvent {
session_id: number;
desktop_size: DesktopSize;
sessionId: number;
desktopSize: DesktopSize;
}

View file

@ -1,8 +0,0 @@
export interface ServerRect {
bottom: number;
left: number;
right: number;
top: number;
clone_buffer(): Uint8Array;
}

View file

@ -5,19 +5,19 @@ import type { ClipboardData } from './ClipboardData';
export interface Session {
run(): Promise<SessionTerminationInfo>;
desktop_size(): DesktopSize;
apply_inputs(transaction: InputTransaction): void;
release_all_inputs(): void;
synchronize_lock_keys(scroll_lock: boolean, num_lock: boolean, caps_lock: boolean, kana_lock: boolean): void;
extension_call(value: unknown): unknown;
desktopSize(): DesktopSize;
applyInputs(transaction: InputTransaction): void;
releaseAllInputs(): void;
synchronizeLockKeys(scrollLock: boolean, numLock: boolean, capsLock: boolean, kanaLock: boolean): void;
extensionCall(value: unknown): unknown;
shutdown(): void;
on_clipboard_paste(data: ClipboardData): Promise<void>;
onClipboardPaste(data: ClipboardData): Promise<void>;
resize(
width: number,
height: number,
scale_factor?: number | null,
physical_width?: number | null,
physical_height?: number | null,
scaleFactor?: number | null,
physicalWidth?: number | null,
physicalHeight?: number | null,
): void;
supports_unicode_keyboard_shortcuts(): boolean;
supportsUnicodeKeyboardShortcuts(): boolean;
}

View file

@ -14,7 +14,7 @@ export interface SessionBuilder {
/**
* Optional
*/
server_domain(server_domain: string): SessionBuilder;
serverDomain(serverDomain: string): SessionBuilder;
/**
* Required
*/
@ -22,19 +22,19 @@ export interface SessionBuilder {
/**
* Required
*/
proxy_address(address: string): SessionBuilder;
proxyAddress(address: string): SessionBuilder;
/**
* Required
*/
auth_token(token: string): SessionBuilder;
authToken(token: string): SessionBuilder;
/**
* Optional
*/
desktop_size(desktop_size: DesktopSize): SessionBuilder;
desktopSize(desktopSize: DesktopSize): SessionBuilder;
/**
* Optional
*/
render_canvas(canvas: HTMLCanvasElement): SessionBuilder;
renderCanvas(canvas: HTMLCanvasElement): SessionBuilder;
/**
* Required.
*
@ -44,33 +44,33 @@ export interface SessionBuilder {
* - `url` (custom cursor data URL); `cursor_data` contains the data URL with Base64-encoded
* cursor bitmap; `hotspot_x` and `hotspot_y` are set to the cursor hotspot coordinates.
*/
set_cursor_style_callback(callback: SetCursorStyleCallback): SessionBuilder;
setCursorStyleCallback(callback: SetCursorStyleCallback): SessionBuilder;
/**
* Required.
*/
set_cursor_style_callback_context(context: unknown): SessionBuilder;
setCursorStyleCallbackContext(context: unknown): SessionBuilder;
/**
* Optional
*/
remote_clipboard_changed_callback(callback: RemoteClipboardChangedCallback): SessionBuilder;
remoteClipboardChangedCallback(callback: RemoteClipboardChangedCallback): SessionBuilder;
/**
* Optional
*/
remote_received_format_list_callback(callback: RemoteReceiveForwardListCallback): SessionBuilder;
remoteReceivedFormatListCallback(callback: RemoteReceiveForwardListCallback): SessionBuilder;
/**
* Optional
*/
force_clipboard_update_callback(callback: ForceClipboardUpdateCallback): SessionBuilder;
forceClipboardUpdateCallback(callback: ForceClipboardUpdateCallback): SessionBuilder;
extension(value: unknown): SessionBuilder;
connect(): Promise<Session>;
}
interface SetCursorStyleCallback {
(
cursor_kind: string,
cursor_data: string | undefined,
hotspot_x: number | undefined,
hotspot_y: number | undefined,
cursorKind: string,
cursorData: string | undefined,
hotspotX: number | undefined,
hotspotY: number | undefined,
): void;
}

View file

@ -13,7 +13,7 @@ export interface UserInteraction {
connect(config: Config): Promise<NewSessionInfo>;
setKeyboardUnicodeMode(use_unicode: boolean): void;
setKeyboardUnicodeMode(useUnicode: boolean): void;
ctrlAltDel(): void;

View file

@ -134,7 +134,7 @@
let result = {} as Record<string, Blob>;
for (const item of data.items()) {
let mime = item.mime_type();
let mime = item.mimeType();
let value = new Blob([item.value()], { type: mime });
result[mime] = value;
@ -233,7 +233,7 @@
if (!sameValue) {
lastClientClipboardItems = values;
let data = remoteDesktopService.createClipboardData();
let clipboardData = new module.ClipboardData();
// Iterate over `Record` type
values.forEach((value: string | Uint8Array, key: string) => {
@ -243,15 +243,15 @@
}
if (key.startsWith('text/') && typeof value === 'string') {
data.add_text(key, value);
clipboardData.addText(key, value);
} else if (key.startsWith('image/') && value instanceof Uint8Array) {
data.add_binary(key, value);
clipboardData.addBinary(key, value);
}
});
if (!data.is_empty()) {
lastClientClipboardData = data;
remoteDesktopService.onClipboardChanged(data);
if (!clipboardData.isEmpty()) {
lastClientClipboardData = clipboardData;
remoteDesktopService.onClipboardChanged(clipboardData);
}
}
} catch (err) {
@ -294,7 +294,7 @@
ffRemoteClipboardData = null;
for (const item of clipboard_data.items()) {
// Firefox only supports text/plain mime type for clipboard writes :(
if (item.mime_type() === 'text/plain') {
if (item.mimeType() === 'text/plain') {
const value = item.value();
if (typeof value === 'string') {
@ -338,7 +338,7 @@
}
try {
let clipboard_data = remoteDesktopService.createClipboardData();
let clipboardData = new module.ClipboardData();
if (evt.clipboardData == null) {
return;
@ -349,10 +349,10 @@
if (mime.startsWith('text/')) {
clipItem.getAsString((str: string) => {
clipboard_data.add_text(mime, str);
clipboardData.addText(mime, str);
if (!clipboard_data.is_empty()) {
remoteDesktopService.onClipboardChanged(clipboard_data);
if (!clipboardData.isEmpty()) {
remoteDesktopService.onClipboardChanged(clipboardData);
}
});
break;
@ -367,10 +367,10 @@
file.arrayBuffer().then((buffer: ArrayBuffer) => {
const strict_buffer = new Uint8Array(buffer);
clipboard_data.add_binary(mime, strict_buffer);
clipboardData.addBinary(mime, strict_buffer);
if (!clipboard_data.is_empty()) {
remoteDesktopService.onClipboardChanged(clipboard_data);
if (!clipboardData.isEmpty()) {
remoteDesktopService.onClipboardChanged(clipboardData);
}
});
break;
@ -457,9 +457,9 @@
function serverBridgeListeners() {
remoteDesktopService.resize.subscribe((evt: ResizeEvent) => {
loggingService.info(`Resize canvas to: ${evt.desktop_size.width}x${evt.desktop_size.height}`);
canvas.width = evt.desktop_size.width;
canvas.height = evt.desktop_size.height;
loggingService.info(`Resize canvas to: ${evt.desktopSize.width}x${evt.desktopSize.height}`);
canvas.width = evt.desktopSize.width;
canvas.height = evt.desktopSize.height;
scaleSession(scale);
});
}

View file

@ -1,7 +1,6 @@
export * as default from './iron-remote-desktop.svelte';
export type { ResizeEvent } from './interfaces/ResizeEvent';
export type { NewSessionInfo } from './interfaces/NewSessionInfo';
export type { ServerRect } from './interfaces/ServerRect';
export type { SessionEvent, IronError, IronErrorKind } from './interfaces/session-event';
export type { SessionEventType } from './enums/SessionEventType';
export type { SessionTerminationInfo } from './interfaces/SessionTerminationInfo';

View file

@ -63,10 +63,6 @@ export class RemoteDesktopService {
loggingService.info('Web bridge initialized.');
}
createClipboardData(): ClipboardData {
return this.module.createClipboardData();
}
// If set to false, the clipboard will not be enabled and the callbacks will not be registered to the Rust side
setEnableClipboard(enable: boolean) {
this.enableClipboard = enable;
@ -108,12 +104,14 @@ export class RemoteDesktopService {
if (preventDefault) {
event.preventDefault(); // prevent default behavior (context menu, etc)
}
const mouseFnc = isDown ? this.module.createMouseButtonPressed : this.module.createMouseButtonReleased;
const mouseFnc = isDown
? this.module.DeviceEvent.mouseButtonPressed
: this.module.DeviceEvent.mouseButtonReleased;
this.doTransactionFromDeviceEvents([mouseFnc(event.button)]);
}
updateMousePosition(position: MousePosition) {
this.doTransactionFromDeviceEvents([this.module.createMouseMove(position.x, position.y)]);
this.doTransactionFromDeviceEvents([this.module.DeviceEvent.mouseMove(position.x, position.y)]);
this.mousePosition.next(position);
}
@ -122,35 +120,35 @@ export class RemoteDesktopService {
}
connect(config: Config): Observable<NewSessionInfo> {
const sessionBuilder = this.module.createSessionBuilder();
const sessionBuilder = new this.module.SessionBuilder();
sessionBuilder.proxy_address(config.proxyAddress);
sessionBuilder.proxyAddress(config.proxyAddress);
sessionBuilder.destination(config.destination);
sessionBuilder.server_domain(config.serverDomain);
sessionBuilder.serverDomain(config.serverDomain);
sessionBuilder.password(config.password);
sessionBuilder.auth_token(config.authToken);
sessionBuilder.authToken(config.authToken);
sessionBuilder.username(config.username);
sessionBuilder.render_canvas(this.canvas!);
sessionBuilder.set_cursor_style_callback_context(this);
sessionBuilder.set_cursor_style_callback(this.setCursorStyleCallback);
sessionBuilder.renderCanvas(this.canvas!);
sessionBuilder.setCursorStyleCallbackContext(this);
sessionBuilder.setCursorStyleCallback(this.setCursorStyleCallback);
config.extensions.forEach((extension) => {
sessionBuilder.extension(extension);
});
if (this.onRemoteClipboardChanged != null && this.enableClipboard) {
sessionBuilder.remote_clipboard_changed_callback(this.onRemoteClipboardChanged);
sessionBuilder.remoteClipboardChangedCallback(this.onRemoteClipboardChanged);
}
if (this.onRemoteReceivedFormatList != null && this.enableClipboard) {
sessionBuilder.remote_received_format_list_callback(this.onRemoteReceivedFormatList);
sessionBuilder.remoteReceivedFormatListCallback(this.onRemoteReceivedFormatList);
}
if (this.onForceClipboardUpdate != null && this.enableClipboard) {
sessionBuilder.force_clipboard_update_callback(this.onForceClipboardUpdate);
sessionBuilder.forceClipboardUpdateCallback(this.onForceClipboardUpdate);
}
if (config.desktopSize != null) {
sessionBuilder.desktop_size(
this.module.createDesktopSize(config.desktopSize.width, config.desktopSize.height),
sessionBuilder.desktopSize(
new this.module.DesktopSize(config.desktopSize.width, config.desktopSize.height),
);
}
@ -202,17 +200,17 @@ export class RemoteDesktopService {
loggingService.info('Session started.');
this.session = session;
this._resize.next({
desktop_size: session.desktop_size(),
session_id: 0,
desktopSize: session.desktopSize(),
sessionId: 0,
});
this.raiseSessionEvent({
type: SessionEventType.STARTED,
data: 'Session started',
});
return {
session_id: 0,
initial_desktop_size: session.desktop_size(),
websocket_port: 0,
sessionId: 0,
initialDesktopSize: session.desktopSize(),
websocketPort: 0,
};
}),
);
@ -232,7 +230,7 @@ export class RemoteDesktopService {
mouseWheel(event: WheelEvent) {
const vertical = event.deltaY !== 0;
const rotation = vertical ? event.deltaY : event.deltaX;
this.doTransactionFromDeviceEvents([this.module.createWheelRotations(vertical, -rotation)]);
this.doTransactionFromDeviceEvents([this.module.DeviceEvent.wheelRotations(vertical, -rotation)]);
}
setVisibility(state: boolean) {
@ -256,14 +254,14 @@ export class RemoteDesktopService {
/// cache the content and send it to the server when it is requested.
onClipboardChanged(data: ClipboardData): Promise<void> {
const onClipboardChangedPromise = async () => {
await this.session?.on_clipboard_paste(data);
await this.session?.onClipboardPaste(data);
};
return onClipboardChangedPromise();
}
onClipboardChangedEmpty(): Promise<void> {
const onClipboardChangedPromise = async () => {
await this.session?.on_clipboard_paste(this.module.createClipboardData());
await this.session?.onClipboardPaste(new this.module.ClipboardData());
};
return onClipboardChangedPromise();
}
@ -283,7 +281,7 @@ export class RemoteDesktopService {
}
private releaseAllInputs() {
this.session?.release_all_inputs();
this.session?.releaseAllInputs();
}
private supportsUnicodeKeyboardShortcuts(): boolean {
@ -292,8 +290,8 @@ export class RemoteDesktopService {
return this.backendSupportsUnicodeKeyboardShortcuts;
}
if (this.session?.supports_unicode_keyboard_shortcuts) {
this.backendSupportsUnicodeKeyboardShortcuts = this.session?.supports_unicode_keyboard_shortcuts();
if (this.session?.supportsUnicodeKeyboardShortcuts) {
this.backendSupportsUnicodeKeyboardShortcuts = this.session?.supportsUnicodeKeyboardShortcuts();
return this.backendSupportsUnicodeKeyboardShortcuts;
}
@ -308,11 +306,11 @@ export class RemoteDesktopService {
let unicodeEvent;
if (evt.type === 'keydown') {
keyEvent = this.module.createKeyPressed;
unicodeEvent = this.module.createUnicodePressed;
keyEvent = this.module.DeviceEvent.keyPressed;
unicodeEvent = this.module.DeviceEvent.unicodePressed;
} else if (evt.type === 'keyup') {
keyEvent = this.module.createKeyReleased;
unicodeEvent = this.module.createUnicodeReleased;
keyEvent = this.module.DeviceEvent.keyReleased;
unicodeEvent = this.module.DeviceEvent.unicodeReleased;
}
let sendAsUnicode = true;
@ -369,8 +367,8 @@ export class RemoteDesktopService {
private setCursorStyleCallback(
style: string,
data: string | undefined,
hotspot_x: number | undefined,
hotspot_y: number | undefined,
hotspotX: number | undefined,
hotspotY: number | undefined,
) {
let cssStyle;
@ -384,7 +382,7 @@ export class RemoteDesktopService {
break;
}
case 'url': {
if (data == undefined || hotspot_x == undefined || hotspot_y == undefined) {
if (data == undefined || hotspotX == undefined || hotspotY == undefined) {
console.error('Invalid custom cursor parameters.');
return;
}
@ -394,8 +392,8 @@ export class RemoteDesktopService {
const image = new Image();
image.src = data;
const rounded_hotspot_x = Math.round(hotspot_x);
const rounded_hotspot_y = Math.round(hotspot_y);
const rounded_hotspot_x = Math.round(hotspotX);
const rounded_hotspot_y = Math.round(hotspotY);
cssStyle = `url(${data}) ${rounded_hotspot_x} ${rounded_hotspot_y}, default`;
@ -420,7 +418,7 @@ export class RemoteDesktopService {
const syncScrollLockActive = evt.getModifierState(LockKey.SCROLL_LOCK);
const syncKanaModeActive = evt.getModifierState(LockKey.KANA_MODE);
this.session?.synchronize_lock_keys(
this.session?.synchronizeLockKeys(
syncScrollLockActive,
syncNumsLockActive,
syncCapsLockActive,
@ -443,9 +441,9 @@ export class RemoteDesktopService {
}
private doTransactionFromDeviceEvents(deviceEvents: DeviceEvent[]) {
const transaction = this.module.createInputTransaction();
deviceEvents.forEach((event) => transaction.add_event(event));
this.session?.apply_inputs(transaction);
const transaction = new this.module.InputTransaction();
deviceEvents.forEach((event) => transaction.addEvent(event));
this.session?.applyInputs(transaction);
}
private ctrlAltDel() {
@ -454,18 +452,21 @@ export class RemoteDesktopService {
const suppr = parseInt('0xE053', 16);
this.doTransactionFromDeviceEvents([
this.module.createKeyPressed(ctrl),
this.module.createKeyPressed(alt),
this.module.createKeyPressed(suppr),
this.module.createKeyReleased(ctrl),
this.module.createKeyReleased(alt),
this.module.createKeyReleased(suppr),
this.module.DeviceEvent.keyPressed(ctrl),
this.module.DeviceEvent.keyPressed(alt),
this.module.DeviceEvent.keyPressed(suppr),
this.module.DeviceEvent.keyReleased(ctrl),
this.module.DeviceEvent.keyReleased(alt),
this.module.DeviceEvent.keyReleased(suppr),
]);
}
private sendMeta() {
const meta = parseInt('0xE05B', 16);
this.doTransactionFromDeviceEvents([this.module.createKeyPressed(meta), this.module.createKeyReleased(meta)]);
this.doTransactionFromDeviceEvents([
this.module.DeviceEvent.keyPressed(meta),
this.module.DeviceEvent.keyReleased(meta),
]);
}
}

View file

@ -149,16 +149,16 @@
}),
filter((result) => result !== null && result !== undefined), // Explicitly checking for null/undefined
)
.subscribe((start_info: NewSessionInfo | null) => {
if (start_info != null && start_info.initial_desktop_size !== null) {
.subscribe((info: NewSessionInfo | null) => {
if (info != null && info.initialDesktopSize !== null) {
toast.set({
type: 'info',
message: 'Success',
});
currentSession.update((session) =>
Object.assign(session, {
sessionId: start_info.session_id,
desktopSize: start_info.initial_desktop_size,
sessionId: info.sessionId,
desktopSize: info.initialDesktopSize,
active: true,
}),
);

View file

@ -1,38 +0,0 @@
import type { Observable } from 'rxjs';
export interface ServerRect {
free(): void;
clone_buffer(): Uint8Array;
bottom: number;
left: number;
right: number;
top: number;
}
export interface NewSessionInfo {
session_id: number;
websocket_port: number;
initial_desktop_size: DesktopSize;
}
export interface DesktopSize {
width: number;
height: number;
}
export interface ResizeEvent {
session_id: number;
desktop_size: DesktopSize;
}
export abstract class ServerBridgeService {
abstract init(): void;
abstract connect(username: string, password: string, address: string): Observable<NewSessionInfo>;
abstract resize: Observable<ResizeEvent>;
abstract updateMouse(mouse_x: number, mouse_y: number, click_state: number): void;
}