From b98bff68747fa9fc7c6d65913e958fbf9b22c78e Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Mon, 22 Jan 2024 21:01:07 +0100 Subject: [PATCH 01/11] Support changing the mouse pointer --- lib/srv/desktop/rdp/rdpclient/src/client.rs | 2 +- .../TdpClientCanvas/TdpClientCanvas.tsx | 23 +++++++++++ web/packages/teleport/src/ironrdp/src/lib.rs | 41 +++++++++++++++---- web/packages/teleport/src/lib/tdp/client.ts | 4 ++ 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/lib/srv/desktop/rdp/rdpclient/src/client.rs b/lib/srv/desktop/rdp/rdpclient/src/client.rs index 5739268901685..0e42622c237a0 100644 --- a/lib/srv/desktop/rdp/rdpclient/src/client.rs +++ b/lib/srv/desktop/rdp/rdpclient/src/client.rs @@ -1101,7 +1101,7 @@ fn create_config(width: u16, height: u16, pin: String) -> Config { // https://github.com/FreeRDP/FreeRDP/blob/4e24b966c86fdf494a782f0dfcfc43a057a2ea60/libfreerdp/core/settings.c#LL49C34-L49C70 client_dir: "C:\\Windows\\System32\\mstscax.dll".to_string(), platform: MajorPlatformType::UNSPECIFIED, - no_server_pointer: true, + no_server_pointer: false, autologon: true, pointer_software_rendering: false, } diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index 6c0d01d6385bc..eb2a518f6d2fd 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -97,6 +97,29 @@ function TdpClientCanvas(props: Props) { } }, [client, clientOnPngFrame]); + useEffect(() => { + if (client) { + const canvas = canvasRef.current; + const updatePointer = (pointer: {data: ImageData, hotspot_x: number, hotspot_y: number}) => { + if (pointer.data === undefined) { + canvas.style.cursor = "none"; + return; + } + const cursor = canvas.ownerDocument.createElement("canvas"); + cursor.width = pointer.data.width; + cursor.height = pointer.data.height; + cursor.getContext('2d').putImageData(pointer.data, 0, 0); + canvas.style.cursor = "url(" + cursor.toDataURL() + ") " + pointer.hotspot_x + " " + pointer.hotspot_y + ", auto"; + }; + + client.on(TdpClientEvent.POINTER, updatePointer); + + return () => { + client.removeListener(TdpClientEvent.POINTER, updatePointer); + }; + } + }, [client]); + useEffect(() => { if (client && clientOnBmpFrame) { const canvas = canvasRef.current; diff --git a/web/packages/teleport/src/ironrdp/src/lib.rs b/web/packages/teleport/src/ironrdp/src/lib.rs index 80ddd9a7c669f..851a8d9e4c450 100644 --- a/web/packages/teleport/src/ironrdp/src/lib.rs +++ b/web/packages/teleport/src/ironrdp/src/lib.rs @@ -162,7 +162,7 @@ impl FastPathProcessor { user_channel_id, // These should be set to the same values as they're set to in the // `Config` object in lib/srv/desktop/rdp/rdpclient/src/client.rs. - no_server_pointer: true, + no_server_pointer: false, pointer_software_rendering: false, } .build(), @@ -184,6 +184,7 @@ impl FastPathProcessor { cb_context: &JsValue, draw_cb: &js_sys::Function, respond_cb: &js_sys::Function, + update_pointer_cb: &js_sys::Function, ) -> Result<(), JsValue> { self.check_remote_fx(tdp_fast_path_frame)?; @@ -211,12 +212,17 @@ impl FastPathProcessor { UpdateKind::Region(region) => { outputs.push(ActiveStageOutput::GraphicsUpdate(region)); } - UpdateKind::PointerDefault - | UpdateKind::PointerHidden - | UpdateKind::PointerPosition { .. } - | UpdateKind::PointerBitmap(_) => { - warn!("Pointer updates are not supported"); - continue; + UpdateKind::PointerDefault => { + outputs.push(ActiveStageOutput::PointerDefault); + } + UpdateKind::PointerHidden => { + outputs.push(ActiveStageOutput::PointerHidden); + } + UpdateKind::PointerPosition { x, y } => { + outputs.push(ActiveStageOutput::PointerPosition { x: x, y: y }); + } + UpdateKind::PointerBitmap(pointer) => { + outputs.push(ActiveStageOutput::PointerBitmap(pointer)) } } } @@ -240,6 +246,27 @@ impl FastPathProcessor { ActiveStageOutput::Terminate => { return Err(JsValue::from_str("Terminate should never be returned")); } + ActiveStageOutput::PointerBitmap(pointer) => { + let data = &pointer.bitmap_data; + let image_data = create_image_data_from_image_and_region( + data, + InclusiveRectangle { + left: 0, + top: 0, + right: pointer.width - 1, + bottom: pointer.height - 1, + }, + )?; + update_pointer_cb.call3( + cb_context, + &JsValue::from(image_data), + &JsValue::from(pointer.hotspot_x), + &JsValue::from(pointer.hotspot_y), + )?; + } + ActiveStageOutput::PointerHidden => { + update_pointer_cb.call0(cb_context)?; + } _ => { debug!("Unhandled ActiveStageOutput: {:?}", output); } diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index 64b66dde7dffd..96abafa60528a 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -74,6 +74,7 @@ export enum TdpClientEvent { WS_OPEN = 'ws open', WS_CLOSE = 'ws close', RESET = 'reset', + POINTER = 'pointer', } export enum LogType { @@ -348,6 +349,9 @@ export default class Client extends EventEmitterWebAuthnSender { }, (responseFrame: ArrayBuffer) => { this.sendRDPResponsePDU(responseFrame); + }, + (data: ImageData, hotspot_x: number, hotspot_y: number) => { + this.emit(TdpClientEvent.POINTER, {data, hotspot_x, hotspot_y}); } ); } catch (e) { From 74fa4b361aad0a38d86d079e61f79c677586df63 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Sat, 10 Feb 2024 01:00:13 +0100 Subject: [PATCH 02/11] Update web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx Co-authored-by: Isaiah Becker-Mayer --- .../teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index eb2a518f6d2fd..98909f4363f55 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -105,7 +105,7 @@ function TdpClientCanvas(props: Props) { canvas.style.cursor = "none"; return; } - const cursor = canvas.ownerDocument.createElement("canvas"); + const cursor = document.createElement('canvas'); cursor.width = pointer.data.width; cursor.height = pointer.data.height; cursor.getContext('2d').putImageData(pointer.data, 0, 0); From dff30ce269f830ed6b440381db6ef91005776016 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Sat, 10 Feb 2024 01:00:25 +0100 Subject: [PATCH 03/11] Update web/packages/teleport/src/lib/tdp/client.ts Co-authored-by: Isaiah Becker-Mayer --- web/packages/teleport/src/lib/tdp/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index 96abafa60528a..850d8eb2fedc2 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -350,7 +350,7 @@ export default class Client extends EventEmitterWebAuthnSender { (responseFrame: ArrayBuffer) => { this.sendRDPResponsePDU(responseFrame); }, - (data: ImageData, hotspot_x: number, hotspot_y: number) => { + (data?: ImageData, hotspot_x?: number, hotspot_y?: number) => { this.emit(TdpClientEvent.POINTER, {data, hotspot_x, hotspot_y}); } ); From 260227cffaf1516a652df8b7f1036b63ee1fc5f5 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Sat, 10 Feb 2024 01:00:39 +0100 Subject: [PATCH 04/11] Update web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx Co-authored-by: Isaiah Becker-Mayer --- .../teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index 98909f4363f55..02759606ad37a 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -100,7 +100,7 @@ function TdpClientCanvas(props: Props) { useEffect(() => { if (client) { const canvas = canvasRef.current; - const updatePointer = (pointer: {data: ImageData, hotspot_x: number, hotspot_y: number}) => { + const updatePointer = (pointer: {data?: ImageData, hotspot_x?: number, hotspot_y?: number}) => { if (pointer.data === undefined) { canvas.style.cursor = "none"; return; From 067cad8ae904dc2ec31a45b83ddec5f98d55de48 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Tue, 20 Feb 2024 01:20:45 +0100 Subject: [PATCH 05/11] Review comments --- .../components/TdpClientCanvas/TdpClientCanvas.tsx | 13 ++++++++++--- web/packages/teleport/src/ironrdp/src/lib.rs | 10 +++++++--- web/packages/teleport/src/lib/tdp/client.ts | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index 02759606ad37a..c7a24b7cc79de 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -97,12 +97,19 @@ function TdpClientCanvas(props: Props) { } }, [client, clientOnPngFrame]); + let previousCursor = "auto"; + useEffect(() => { if (client) { const canvas = canvasRef.current; - const updatePointer = (pointer: {data?: ImageData, hotspot_x?: number, hotspot_y?: number}) => { - if (pointer.data === undefined) { - canvas.style.cursor = "none"; + const updatePointer = (pointer: {data: ImageData | boolean, hotspot_x?: number, hotspot_y?: number}) => { + if (typeof pointer.data === "boolean") { + if(pointer.data) { + canvas.style.cursor = previousCursor; + } else { + previousCursor = canvas.style.cursor; + canvas.style.cursor = "none"; + } return; } const cursor = document.createElement('canvas'); diff --git a/web/packages/teleport/src/ironrdp/src/lib.rs b/web/packages/teleport/src/ironrdp/src/lib.rs index 851a8d9e4c450..c2f793c0308f2 100644 --- a/web/packages/teleport/src/ironrdp/src/lib.rs +++ b/web/packages/teleport/src/ironrdp/src/lib.rs @@ -218,8 +218,9 @@ impl FastPathProcessor { UpdateKind::PointerHidden => { outputs.push(ActiveStageOutput::PointerHidden); } - UpdateKind::PointerPosition { x, y } => { - outputs.push(ActiveStageOutput::PointerPosition { x: x, y: y }); + UpdateKind::PointerPosition { .. } => { + warn!("Pointer position updates are not supported"); + continue; } UpdateKind::PointerBitmap(pointer) => { outputs.push(ActiveStageOutput::PointerBitmap(pointer)) @@ -264,8 +265,11 @@ impl FastPathProcessor { &JsValue::from(pointer.hotspot_y), )?; } + ActiveStageOutput::PointerDefault => { + update_pointer_cb.call1(cb_context, &JsValue::from(true))?; + } ActiveStageOutput::PointerHidden => { - update_pointer_cb.call0(cb_context)?; + update_pointer_cb.call1(cb_context, &JsValue::from(false))?; } _ => { debug!("Unhandled ActiveStageOutput: {:?}", output); diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index 850d8eb2fedc2..e6dd27e8174b0 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -350,7 +350,7 @@ export default class Client extends EventEmitterWebAuthnSender { (responseFrame: ArrayBuffer) => { this.sendRDPResponsePDU(responseFrame); }, - (data?: ImageData, hotspot_x?: number, hotspot_y?: number) => { + (data: ImageData|boolean, hotspot_x?: number, hotspot_y?: number) => { this.emit(TdpClientEvent.POINTER, {data, hotspot_x, hotspot_y}); } ); From 66f88dacaf43ebb8047512cf3c40cebfd73d1937 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Tue, 20 Feb 2024 01:40:16 +0100 Subject: [PATCH 06/11] prettier --- .../TdpClientCanvas/TdpClientCanvas.tsx | 23 ++++++++++++++----- web/packages/teleport/src/lib/tdp/client.ts | 4 ++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index c7a24b7cc79de..abd64a0341850 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -97,18 +97,22 @@ function TdpClientCanvas(props: Props) { } }, [client, clientOnPngFrame]); - let previousCursor = "auto"; + let previousCursor = 'auto'; useEffect(() => { if (client) { const canvas = canvasRef.current; - const updatePointer = (pointer: {data: ImageData | boolean, hotspot_x?: number, hotspot_y?: number}) => { - if (typeof pointer.data === "boolean") { - if(pointer.data) { + const updatePointer = (pointer: { + data: ImageData | boolean; + hotspot_x?: number; + hotspot_y?: number; + }) => { + if (typeof pointer.data === 'boolean') { + if (pointer.data) { canvas.style.cursor = previousCursor; } else { previousCursor = canvas.style.cursor; - canvas.style.cursor = "none"; + canvas.style.cursor = 'none'; } return; } @@ -116,7 +120,14 @@ function TdpClientCanvas(props: Props) { cursor.width = pointer.data.width; cursor.height = pointer.data.height; cursor.getContext('2d').putImageData(pointer.data, 0, 0); - canvas.style.cursor = "url(" + cursor.toDataURL() + ") " + pointer.hotspot_x + " " + pointer.hotspot_y + ", auto"; + canvas.style.cursor = + 'url(' + + cursor.toDataURL() + + ') ' + + pointer.hotspot_x + + ' ' + + pointer.hotspot_y + + ', auto'; }; client.on(TdpClientEvent.POINTER, updatePointer); diff --git a/web/packages/teleport/src/lib/tdp/client.ts b/web/packages/teleport/src/lib/tdp/client.ts index e6dd27e8174b0..78815dfb012a4 100644 --- a/web/packages/teleport/src/lib/tdp/client.ts +++ b/web/packages/teleport/src/lib/tdp/client.ts @@ -350,8 +350,8 @@ export default class Client extends EventEmitterWebAuthnSender { (responseFrame: ArrayBuffer) => { this.sendRDPResponsePDU(responseFrame); }, - (data: ImageData|boolean, hotspot_x?: number, hotspot_y?: number) => { - this.emit(TdpClientEvent.POINTER, {data, hotspot_x, hotspot_y}); + (data: ImageData | boolean, hotspot_x?: number, hotspot_y?: number) => { + this.emit(TdpClientEvent.POINTER, { data, hotspot_x, hotspot_y }); } ); } catch (e) { From 51e7627776be4e316079dde70b7ce9e45a602c8a Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Wed, 21 Feb 2024 20:46:11 +0100 Subject: [PATCH 07/11] use template string --- .../components/TdpClientCanvas/TdpClientCanvas.tsx | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index abd64a0341850..79ad016846862 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -120,14 +120,9 @@ function TdpClientCanvas(props: Props) { cursor.width = pointer.data.width; cursor.height = pointer.data.height; cursor.getContext('2d').putImageData(pointer.data, 0, 0); - canvas.style.cursor = - 'url(' + - cursor.toDataURL() + - ') ' + - pointer.hotspot_x + - ' ' + - pointer.hotspot_y + - ', auto'; + canvas.style.cursor = `url(${cursor.toDataURL()}) ${ + pointer.hotspot_x + } ${pointer.hotspot_y}, auto`; }; client.on(TdpClientEvent.POINTER, updatePointer); From 4f759ab12ec3b16b20bd26aee106bc8859e83618 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Wed, 21 Feb 2024 20:54:33 +0100 Subject: [PATCH 08/11] useRef --- .../src/components/TdpClientCanvas/TdpClientCanvas.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index 79ad016846862..5f950a518f4f9 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -97,7 +97,7 @@ function TdpClientCanvas(props: Props) { } }, [client, clientOnPngFrame]); - let previousCursor = 'auto'; + const previousCursor = useRef('auto'); useEffect(() => { if (client) { @@ -109,9 +109,9 @@ function TdpClientCanvas(props: Props) { }) => { if (typeof pointer.data === 'boolean') { if (pointer.data) { - canvas.style.cursor = previousCursor; + canvas.style.cursor = previousCursor.current; } else { - previousCursor = canvas.style.cursor; + previousCursor.current = canvas.style.cursor; canvas.style.cursor = 'none'; } return; From 81fa12c78e8714af47db8baa7ab1c96f8165286d Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Wed, 21 Feb 2024 21:48:25 +0100 Subject: [PATCH 09/11] don't update pointer during replay --- web/packages/teleport/src/DesktopSession/DesktopSession.tsx | 1 + web/packages/teleport/src/Player/DesktopPlayer.tsx | 1 + .../src/components/TdpClientCanvas/TdpClientCanvas.tsx | 4 +++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/web/packages/teleport/src/DesktopSession/DesktopSession.tsx b/web/packages/teleport/src/DesktopSession/DesktopSession.tsx index 76d0d05d892ff..119b601b59eb4 100644 --- a/web/packages/teleport/src/DesktopSession/DesktopSession.tsx +++ b/web/packages/teleport/src/DesktopSession/DesktopSession.tsx @@ -339,6 +339,7 @@ function Session({ canvasOnMouseUp={canvasOnMouseUp} canvasOnMouseWheelScroll={canvasOnMouseWheelScroll} canvasOnContextMenu={canvasOnContextMenu} + updatePointer={true} /> ); diff --git a/web/packages/teleport/src/Player/DesktopPlayer.tsx b/web/packages/teleport/src/Player/DesktopPlayer.tsx index 8262835db060c..ea928de1af3ac 100644 --- a/web/packages/teleport/src/Player/DesktopPlayer.tsx +++ b/web/packages/teleport/src/Player/DesktopPlayer.tsx @@ -107,6 +107,7 @@ export const DesktopPlayer = ({ ...canvasStyle, ...canvasAndProgressBarDisplayStyle, }} + updatePointer={false} /> (null); @@ -100,7 +101,7 @@ function TdpClientCanvas(props: Props) { const previousCursor = useRef('auto'); useEffect(() => { - if (client) { + if (client && updatePointer) { const canvas = canvasRef.current; const updatePointer = (pointer: { data: ImageData | boolean; @@ -425,6 +426,7 @@ export type Props = { canvasOnMouseWheelScroll?: (cli: TdpClient, e: WheelEvent) => void; canvasOnContextMenu?: () => boolean; style?: CSSProperties; + updatePointer: boolean; }; export default memo(TdpClientCanvas); From 49ca3edd5770266bbf2fad675045e8b9aa8e1ada Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Wed, 21 Feb 2024 23:59:07 +0100 Subject: [PATCH 10/11] don't update pointer during replay --- .../teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx index 18dad34ee6511..a1dab2defff7f 100644 --- a/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx +++ b/web/packages/teleport/src/components/TdpClientCanvas/TdpClientCanvas.tsx @@ -132,7 +132,7 @@ function TdpClientCanvas(props: Props) { client.removeListener(TdpClientEvent.POINTER, updatePointer); }; } - }, [client]); + }, [client, updatePointer]); useEffect(() => { if (client && clientOnBmpFrame) { From 29f4d19f744332bf5bbeccdbe1b0181a8f2b3ec2 Mon Sep 17 00:00:00 2001 From: Przemko Robakowski Date: Mon, 26 Feb 2024 00:36:03 +0100 Subject: [PATCH 11/11] Review comments --- web/packages/teleport/src/Player/DesktopPlayer.tsx | 1 - .../src/components/TdpClientCanvas/TdpClientCanvas.tsx | 8 +++++--- web/packages/teleport/src/ironrdp/src/lib.rs | 4 ++++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/web/packages/teleport/src/Player/DesktopPlayer.tsx b/web/packages/teleport/src/Player/DesktopPlayer.tsx index ea928de1af3ac..8262835db060c 100644 --- a/web/packages/teleport/src/Player/DesktopPlayer.tsx +++ b/web/packages/teleport/src/Player/DesktopPlayer.tsx @@ -107,7 +107,6 @@ export const DesktopPlayer = ({ ...canvasStyle, ...canvasAndProgressBarDisplayStyle, }} - updatePointer={false} /> { client.removeListener(TdpClientEvent.POINTER, updatePointer); @@ -426,7 +428,7 @@ export type Props = { canvasOnMouseWheelScroll?: (cli: TdpClient, e: WheelEvent) => void; canvasOnContextMenu?: () => boolean; style?: CSSProperties; - updatePointer: boolean; + updatePointer?: boolean; }; export default memo(TdpClientCanvas); diff --git a/web/packages/teleport/src/ironrdp/src/lib.rs b/web/packages/teleport/src/ironrdp/src/lib.rs index c2f793c0308f2..f448392f59a8e 100644 --- a/web/packages/teleport/src/ironrdp/src/lib.rs +++ b/web/packages/teleport/src/ironrdp/src/lib.rs @@ -178,6 +178,10 @@ impl FastPathProcessor { /// `draw_cb: (bitmapFrame: BitmapFrame) => void` /// /// `respond_cb: (responseFrame: ArrayBuffer) => void` + /// + /// `update_pointer_cb: (data: ImageData | boolean, hotspot_x: number, hotspot_y: number) => void` + /// if data is `false` we hide cursor but remember its value, if data is `true` we restore last + /// cursor value, otherwise we set cursor to bitmapt from `ImageData` pub fn process( &mut self, tdp_fast_path_frame: &[u8],