feat(web): implement clipboard enable/disable (#676)

- Add setEnableClipboard function in user interface 
- Updated demo with clipboard control
This commit is contained in:
irvingouj@Devolutions 2025-02-19 10:12:49 -05:00 committed by GitHub
parent 58e1cb9034
commit e76dc12485
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 138 additions and 82 deletions

View file

@ -34,4 +34,6 @@ export interface UserInteraction {
onSessionEvent(callback: (event: SessionEvent) => void): void;
resize(width: number, height: number, scale?: number): void;
setEnableClipboard(enable: boolean): void;
}

View file

@ -67,24 +67,25 @@
/* Firefox-specific END */
/* Clipboard initialization BEGIN */
// Detect if browser supports async Clipboard API
if (!isFirefox && navigator.clipboard != undefined) {
if (navigator.clipboard.read != undefined && navigator.clipboard.write != undefined) {
isClipboardApiSupported = true;
function initClipboard() {
// Detect if browser supports async Clipboard API
if (!isFirefox && navigator.clipboard != undefined) {
if (navigator.clipboard.read != undefined && navigator.clipboard.write != undefined) {
isClipboardApiSupported = true;
}
}
}
if (isFirefox) {
wasmService.setOnRemoteClipboardChanged(ffOnRemoteClipboardChanged);
wasmService.setOnRemoteReceivedFormatList(ffOnRemoteReceivedFormatList);
wasmService.setOnForceClipboardUpdate(onForceClipboardUpdate);
} else if (isClipboardApiSupported) {
wasmService.setOnRemoteClipboardChanged(onRemoteClipboardChanged);
wasmService.setOnForceClipboardUpdate(onForceClipboardUpdate);
if (isFirefox) {
wasmService.setOnRemoteClipboardChanged(ffOnRemoteClipboardChanged);
wasmService.setOnRemoteReceivedFormatList(ffOnRemoteReceivedFormatList);
wasmService.setOnForceClipboardUpdate(onForceClipboardUpdate);
} else if (isClipboardApiSupported) {
wasmService.setOnRemoteClipboardChanged(onRemoteClipboardChanged);
wasmService.setOnForceClipboardUpdate(onForceClipboardUpdate);
// Start the clipboard monitoring loop
setTimeout(onMonitorClipboard, CLIPBOARD_MONITORING_INTERVAL);
// Start the clipboard monitoring loop
setTimeout(onMonitorClipboard, CLIPBOARD_MONITORING_INTERVAL);
}
}
/* Clipboard initialization END */
@ -659,6 +660,7 @@
loggingService.verbose = verbose === 'true';
loggingService.info('Dom ready');
await initcanvas();
initClipboard();
});
</script>

View file

@ -75,6 +75,10 @@ export class PublicAPI {
this.wasmService.resizeDynamic(width, height, scale);
}
private setEnableClipboard(enable: boolean) {
this.wasmService.setEnableClipboard(enable);
}
getExposedFunctions(): UserInteraction {
return {
setVisibility: this.setVisibility.bind(this),
@ -89,6 +93,7 @@ export class PublicAPI {
setKeyboardUnicodeMode: this.setKeyboardUnicodeMode.bind(this),
setCursorStyleOverride: this.setCursorStyleOverride.bind(this),
resize: this.resize.bind(this),
setEnableClipboard: this.setEnableClipboard.bind(this),
};
}
}

View file

@ -48,6 +48,7 @@ export class WasmBridgeService {
private onForceClipboardUpdate?: OnForceClipboardUpdate;
private cursorHasOverride: boolean = false;
private lastCursorStyle: string = 'default';
private enableClipboard: boolean = true;
resize: Observable<ResizeEvent>;
session?: Session;
@ -74,6 +75,11 @@ export class WasmBridgeService {
ironrdp_init(LogType[debug]);
}
// 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;
}
/// Callback to set the local clipboard content to data received from the remote.
setOnRemoteClipboardChanged(callback: OnRemoteClipboardChanged) {
this.onRemoteClipboardChanged = callback;
@ -154,13 +160,13 @@ export class WasmBridgeService {
if (preConnectionBlob != null) {
sessionBuilder.pcb(preConnectionBlob);
}
if (this.onRemoteClipboardChanged != null) {
if (this.onRemoteClipboardChanged != null && this.enableClipboard) {
sessionBuilder.remote_clipboard_changed_callback(this.onRemoteClipboardChanged);
}
if (this.onRemoteReceivedFormatList != null) {
if (this.onRemoteReceivedFormatList != null && this.enableClipboard) {
sessionBuilder.remote_received_format_list_callback(this.onRemoteReceivedFormatList);
}
if (this.onForceClipboardUpdate != null) {
if (this.onForceClipboardUpdate != null && this.enableClipboard) {
sessionBuilder.force_clipboard_update_callback(this.onForceClipboardUpdate);
}

View file

@ -2,3 +2,31 @@
display: flex;
justify-content: center;
}
.login-container {
height: 100vh;
overflow-y: auto;
padding: 1rem;
}
.login-content {
min-height: min-content;
padding-bottom: 2rem;
}
.field.label.border {
margin-bottom: 1rem;
}
.checkbox-container {
display: flex;
justify-content: start;
gap: 30rem;
padding: 0.5rem;
}
.checkbox-wrapper {
display: flex;
align-items: center;
font-size: 1.5em;
}

View file

@ -21,6 +21,7 @@
};
let pcb: string;
let pop_up = false;
let enable_clipboard = true;
let userInteraction: UserInteraction;
@ -66,6 +67,7 @@
desktopSize,
pcb,
kdc_proxy_url,
enable_clipboard,
});
const base64Data = btoa(data);
window.open(
@ -76,6 +78,7 @@
return;
}
userInteraction.setEnableClipboard(enable_clipboard);
from(
userInteraction.connect(
username,
@ -124,73 +127,83 @@
};
</script>
<main class="responsive">
<div class="large-space" />
<div class="grid">
<div class="s2" />
<div class="s8">
<article class="primary-container">
<h5>Login</h5>
<div class="medium-space" />
<div>
<div class="field label border">
<input id="hostname" type="text" bind:value={hostname} />
<label for="hostname">Hostname</label>
</div>
<div class="field label border">
<input id="domain" type="text" bind:value={domain} />
<label for="domain">Domain</label>
</div>
<div class="field label border">
<input id="username" type="text" bind:value={username} />
<label for="username">Username</label>
</div>
<div class="field label border">
<input id="password" type="password" bind:value={password} />
<label for="password">Password</label>
</div>
<div class="field label border">
<input id="gatewayAddress" type="text" bind:value={gatewayAddress} />
<label for="gatewayAddress">Gateway Address</label>
</div>
<div class="field label border">
<input id="authtoken" type="text" bind:value={authtoken} />
<label for="authtoken">AuthToken</label>
</div>
<div class="field label border">
<input id="pcb" type="text" bind:value={pcb} />
<label for="pcb">Pre Connection Blob</label>
</div>
<div class="field label border">
<input id="desktopSizeW" type="text" bind:value={desktopSize.width} />
<label for="desktopSizeW">Desktop Width</label>
</div>
<div class="field label border">
<input id="desktopSizeH" type="text" bind:value={desktopSize.height} />
<label for="desktopSizeH">Desktop Height</label>
</div>
<div class="field label border">
<input id="kdc_proxy_url" type="text" bind:value={kdc_proxy_url} />
<label for="kdc_proxy_url">KDC Proxy URL</label>
</div>
<div class="field label border">
<div style="display: flex; height: 100%; align-items: center; font-size: 1.5em;">
<input
id="use_pop_up"
type="checkbox"
bind:value={pop_up}
style="width: 1.5em; height: 1.5em; margin-right: 0.5em;"
/>
<label for="use_pop_up">Use Pop Up</label>
<main class="responsive login-container">
<div class="login-content">
<div class="grid">
<div class="s2" />
<div class="s8">
<article class="primary-container">
<h5>Login</h5>
<div class="medium-space" />
<div>
<div class="field label border">
<input id="hostname" type="text" bind:value={hostname} />
<label for="hostname">Hostname</label>
</div>
<div class="field label border">
<input id="domain" type="text" bind:value={domain} />
<label for="domain">Domain</label>
</div>
<div class="field label border">
<input id="username" type="text" bind:value={username} />
<label for="username">Username</label>
</div>
<div class="field label border">
<input id="password" type="password" bind:value={password} />
<label for="password">Password</label>
</div>
<div class="field label border">
<input id="gatewayAddress" type="text" bind:value={gatewayAddress} />
<label for="gatewayAddress">Gateway Address</label>
</div>
<div class="field label border">
<input id="authtoken" type="text" bind:value={authtoken} />
<label for="authtoken">AuthToken</label>
</div>
<div class="field label border">
<input id="pcb" type="text" bind:value={pcb} />
<label for="pcb">Pre Connection Blob</label>
</div>
<div class="field label border">
<input id="desktopSizeW" type="text" bind:value={desktopSize.width} />
<label for="desktopSizeW">Desktop Width</label>
</div>
<div class="field label border">
<input id="desktopSizeH" type="text" bind:value={desktopSize.height} />
<label for="desktopSizeH">Desktop Height</label>
</div>
<div class="field label border">
<input id="kdc_proxy_url" type="text" bind:value={kdc_proxy_url} />
<label for="kdc_proxy_url">KDC Proxy URL</label>
</div>
<div class="field label border checkbox-container">
<div class="checkbox-wrapper">
<input
id="use_pop_up"
type="checkbox"
bind:checked={pop_up}
style="width: 1.5em; height: 1.5em; margin-right: 0.5em;"
/>
<label for="use_pop_up">Use Pop Up</label>
</div>
<div class="checkbox-wrapper">
<input
id="enable_clipboard"
type="checkbox"
bind:checked={enable_clipboard}
style="width: 1.5em; height: 1.5em; margin-right: 0.5em;"
/>
<label for="enable_clipboard">Enable Clipboard</label>
</div>
</div>
</div>
</div>
<nav class="center-align">
<button on:click={StartSession}>Login</button>
</nav>
</article>
<nav class="center-align">
<button on:click={StartSession}>Login</button>
</nav>
</article>
</div>
<div class="s2" />
</div>
<div class="s2" />
</div>
</main>