From e83861e7cc76bcf6d96d4201b96f6af4a45b1514 Mon Sep 17 00:00:00 2001 From: irving ou Date: Tue, 16 Apr 2024 18:00:44 -0400 Subject: [PATCH 01/21] images showing up --- .../App.axaml | 10 ++ .../App.axaml.cs | 25 +++ ...Devolutions.IronRdp.AvaloniaExample.csproj | 21 +++ .../Devolutions.IronRdp.AvaloniaExample.sln | 25 +++ .../MainWindow.axaml | 10 ++ .../MainWindow.axaml.cs | 148 ++++++++++++++++++ .../Program.cs | 21 +++ .../app.manifest | 18 +++ .../Devolutions.IronRdp.ConnectExample.sln | 25 +++ .../Generated/DiplomatRuntime.cs | 6 +- 10 files changed, 306 insertions(+), 3 deletions(-) create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.sln create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/app.manifest create mode 100644 ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.sln diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml new file mode 100644 index 000000000..f807f50e9 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml @@ -0,0 +1,10 @@ + + + + + + + \ No newline at end of file diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs new file mode 100644 index 000000000..1396a66aa --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs @@ -0,0 +1,25 @@ +using System; +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace Devolutions.IronRdp.AvaloniaExample; + +public partial class App : Application +{ + public override void Initialize() + { + Console.WriteLine("App Initialize"); + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj new file mode 100644 index 000000000..d358f1e4c --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.csproj @@ -0,0 +1,21 @@ + + + WinExe + net8.0 + enable + true + app.manifest + true + true + + + + + + + + + + + + diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.sln b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.sln new file mode 100644 index 000000000..dc1a353ba --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Devolutions.IronRdp.AvaloniaExample.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.IronRdp.AvaloniaExample", "Devolutions.IronRdp.AvaloniaExample.csproj", "{B374556F-70F4-4B70-90AE-6DF00C532240}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B374556F-70F4-4B70-90AE-6DF00C532240}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B374556F-70F4-4B70-90AE-6DF00C532240}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B374556F-70F4-4B70-90AE-6DF00C532240}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B374556F-70F4-4B70-90AE-6DF00C532240}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1DD5DE59-5AB4-4F5F-A2AC-CA4D7012F56E} + EndGlobalSection +EndGlobal diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml new file mode 100644 index 000000000..605aa1af2 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml @@ -0,0 +1,10 @@ + + + + diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs new file mode 100644 index 000000000..6cc6e65b5 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -0,0 +1,148 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Media.Imaging; +using Avalonia.Platform; +using Avalonia.Threading; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Net.Security; +using System.Threading.Tasks; + +namespace Devolutions.IronRdp.AvaloniaExample; + +public partial class MainWindow : Window +{ + + WriteableBitmap bitmap; + Image image; + public MainWindow() + { + InitializeComponent(); + this.Opened += OnOpened; + + } + + private void OnOpened(object sender, EventArgs e) + { + Console.WriteLine("OnOpened"); + + var username = "Administrator"; + var password = "DevoLabs123!"; + var domain = "ad.it-help.ninja"; + var server = "IT-HELP-DC.ad.it-help.ninja"; + var width = 1280; + var height = 980; + + var config = buildConfig(server, username, password, domain, width, height); + + var task = Connection.Connect(config, server); + this.bitmap = new WriteableBitmap(new PixelSize(width, height), new Vector(96, 96), Avalonia.Platform.PixelFormat.Rgba8888, AlphaFormat.Opaque); + var canvas = this.FindControl("MainCanvas")!; + this.image = new Image { Width = width, Height = height, Source = this.bitmap }; + canvas.Children.Add(image); + + task.ContinueWith(async t => + { + if (t.IsFaulted) + { + return; + } + var (res, stream) = t.Result; + var decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), res.GetDesktopSize().GetHeight()); + var activeState = ActiveStage.New(res); + + await ProcessActiveState(activeState, decodedImage, stream); + }); + } + + private void WriteDecodedImageToCanvas(DecodedImage decodedImage) + { + Dispatcher.UIThread.InvokeAsync(() => + { + var data = decodedImage.GetData(); + var buffer_size = (int)data.GetSize(); + var buffer = new byte[buffer_size]; + data.Fill(buffer); + + using (var bitmap = this.bitmap.Lock()) + { + unsafe + { + fixed (byte* p = buffer) + { + var src = (uint*)p; + var dst = (uint*)bitmap.Address; + for (var i = 0; i < buffer_size / 4; i++) + { + dst[i] = src[i]; + } + } + } + } + + // Assuming `image` is the Image control that needs to be updated. + image.InvalidateVisual(); // Force redraw of image + }); + } + + + private async Task ProcessActiveState(ActiveStage activeState, DecodedImage decodedImage, Framed framed) + { + var keepLooping = true; + while (keepLooping) + { + var readPduTask = framed.ReadPdu(); + Action? action; + byte[]? payload; + if (readPduTask == await Task.WhenAny(readPduTask, Task.Delay(10000))) + { + var pduReadTask = await readPduTask; + action = pduReadTask.Item1; + payload = pduReadTask.Item2; + Console.WriteLine($"Action: {action}"); + } + else + { + Console.WriteLine("Timeout"); + break; + } + var outputIterator = activeState.Process(decodedImage, action, payload); + + while (!outputIterator.IsEmpty()) + { + var output = outputIterator.Next()!; // outputIterator.Next() is not null since outputIterator.IsEmpty() is false + Console.WriteLine($"Output type: {output.GetType()}"); + if (output.GetType() == ActiveStageOutputType.Terminate) + { + Console.WriteLine("Connection terminated."); + keepLooping = false; + } + + if (output.GetType() == ActiveStageOutputType.ResponseFrame) + { + // render the decoded image to canvas + WriteDecodedImageToCanvas(decodedImage); + // Send the response frame to the server + var responseFrame = output.GetResponseFrame()!; + byte[] responseFrameBytes = new byte[responseFrame.GetSize()]; + responseFrame.Fill(responseFrameBytes); + await framed.Write(responseFrameBytes); + } + } + } + } + + private static Config buildConfig(string servername, string username, string password, string domain, int width, int height) + { + ConfigBuilder configBuilder = ConfigBuilder.New(); + + configBuilder.WithUsernameAndPassword(username, password); + configBuilder.SetDomain(domain); + configBuilder.SetDesktopSize((ushort)height, (ushort)width); + configBuilder.SetClientName("IronRdp"); + configBuilder.SetClientDir("C:\\"); + configBuilder.SetPerformanceFlags(PerformanceFlags.NewDefault()); + + return configBuilder.Build(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs new file mode 100644 index 000000000..79c6f23da --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs @@ -0,0 +1,21 @@ +using Avalonia; +using System; + +namespace Devolutions.IronRdp.AvaloniaExample; + +class Program +{ + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) => BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .WithInterFont() + .LogToTrace(); +} diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/app.manifest b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/app.manifest new file mode 100644 index 000000000..62829436e --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/app.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.sln b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.sln new file mode 100644 index 000000000..3f6a119b0 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Devolutions.IronRdp.ConnectExample.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.002.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Devolutions.IronRdp.ConnectExample", "Devolutions.IronRdp.ConnectExample.csproj", "{EB812E03-EDBE-4576-A84A-A26982D46BE1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EB812E03-EDBE-4576-A84A-A26982D46BE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EB812E03-EDBE-4576-A84A-A26982D46BE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EB812E03-EDBE-4576-A84A-A26982D46BE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EB812E03-EDBE-4576-A84A-A26982D46BE1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C47D6FCB-C82B-4C07-AA17-839B0B0CF0D2} + EndGlobalSection +EndGlobal diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/DiplomatRuntime.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/DiplomatRuntime.cs index 158688dd6..18223e37b 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/DiplomatRuntime.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/DiplomatRuntime.cs @@ -46,11 +46,11 @@ public DiplomatWriteable() IntPtr flushFuncPtr = Marshal.GetFunctionPointerForDelegate(flushFunc); IntPtr growFuncPtr = Marshal.GetFunctionPointerForDelegate(growFunc); - + // flushFunc and growFunc are managed objects and might be disposed of by the garbage collector. // To prevent this, we make the context hold the references and protect the context itself // for automatic disposal by moving it behind a GCHandle. - DiplomatWriteableContext ctx = new DiplomatWriteableContext(); + DiplomatWriteableContext ctx = new DiplomatWriteableContext(); ctx.flushFunc = flushFunc; ctx.growFunc = growFunc; GCHandle ctxHandle = GCHandle.Alloc(ctx); @@ -81,7 +81,7 @@ public string ToUnicode() { throw new IndexOutOfRangeException("DiplomatWriteable buffer is too big"); } - return Marshal.PtrToStringUTF8(buf, (int)len); + return Marshal.PtrToStringUTF8(buf, (int) len); #else byte[] utf8 = ToUtf8Bytes(); return DiplomatUtils.Utf8ToString(utf8); From 91d256357000bfa4672a8601d06e2eb92e03d8fc Mon Sep 17 00:00:00 2001 From: irving ou Date: Wed, 17 Apr 2024 16:00:34 -0400 Subject: [PATCH 02/21] WIP:Need to fix keyboard --- crates/ironrdp-client/src/gui.rs | 1 - .../MainWindow.axaml | 22 +- .../MainWindow.axaml.cs | 243 ++++++++++++++---- .../Program.cs | 4 +- .../Generated/ActiveStage.cs | 34 +++ .../Generated/ActiveStageOutput.cs | 20 +- .../Generated/ClientConnectorState.cs | 8 +- .../Generated/InputDatabase.cs | 22 ++ .../Generated/RawActiveStage.cs | 3 + .../Generated/RawActiveStageOutput.cs | 4 +- .../Generated/RawClientConnectorState.cs | 4 +- .../Generated/RawInputDatabase.cs | 3 + .../Devolutions.IronRdp/src/Connection.cs | 2 +- ffi/dotnet/Devolutions.IronRdp/src/Framed.cs | 25 +- ffi/src/connector/state.rs | 2 +- ffi/src/input.rs | 120 +++++++++ ffi/src/pdu.rs | 14 + ffi/src/session/mod.rs | 15 +- ffi/src/utils/mod.rs | 1 + 19 files changed, 452 insertions(+), 95 deletions(-) diff --git a/crates/ironrdp-client/src/gui.rs b/crates/ironrdp-client/src/gui.rs index f8e0f5f51..c9d227295 100644 --- a/crates/ironrdp-client/src/gui.rs +++ b/crates/ironrdp-client/src/gui.rs @@ -141,7 +141,6 @@ impl GuiContext { x: (position.x / sf) as u16, y: (position.y / sf) as u16, }); - let input_events = input_database.apply(std::iter::once(operation)); send_fast_path_events(&input_event_sender, input_events); diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml index 605aa1af2..71a325e2c 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml @@ -1,10 +1,14 @@ - - - + xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" + xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + mc:Ignorable="d" d:DesignWidth="1980" d:DesignHeight="1080" + x:Class="Devolutions.IronRdp.AvaloniaExample.MainWindow" + Title="Devolutions.IronRdp.AvaloniaExample"> + + + \ No newline at end of file diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index 6cc6e65b5..be5c52ee0 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -1,9 +1,9 @@ using Avalonia; using Avalonia.Controls; +using Avalonia.Input; using Avalonia.Media.Imaging; using Avalonia.Platform; using Avalonia.Threading; -using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Net.Security; using System.Threading.Tasks; @@ -13,8 +13,13 @@ namespace Devolutions.IronRdp.AvaloniaExample; public partial class MainWindow : Window { - WriteableBitmap bitmap; - Image image; + WriteableBitmap? bitmap; + Canvas? canvas; + Image? image; + InputDatabase? inputDatabase = InputDatabase.New(); + ActiveStage? activeStage; + DecodedImage? decodedImage; + Framed? framed; public MainWindow() { InitializeComponent(); @@ -22,7 +27,7 @@ public MainWindow() } - private void OnOpened(object sender, EventArgs e) + private void OnOpened(object? sender, EventArgs e) { Console.WriteLine("OnOpened"); @@ -31,40 +36,44 @@ private void OnOpened(object sender, EventArgs e) var domain = "ad.it-help.ninja"; var server = "IT-HELP-DC.ad.it-help.ninja"; var width = 1280; - var height = 980; + var height = 800; - var config = buildConfig(server, username, password, domain, width, height); + var config = buildConfig(username, password, domain, width, height); var task = Connection.Connect(config, server); - this.bitmap = new WriteableBitmap(new PixelSize(width, height), new Vector(96, 96), Avalonia.Platform.PixelFormat.Rgba8888, AlphaFormat.Opaque); - var canvas = this.FindControl("MainCanvas")!; - this.image = new Image { Width = width, Height = height, Source = this.bitmap }; + bitmap = new WriteableBitmap(new PixelSize(width, height), new Vector(96, 96), Avalonia.Platform.PixelFormat.Rgba8888, AlphaFormat.Opaque); + canvas = this.FindControl("MainCanvas")!; + canvas.Focusable = true; + image = new Image { Width = width, Height = height, Source = this.bitmap }; canvas.Children.Add(image); - task.ContinueWith(async t => + canvas.KeyDown += Canvas_KeyDown; + canvas.KeyUp += Canvas_KeyUp; + + task.ContinueWith(t => { if (t.IsFaulted) { return; } - var (res, stream) = t.Result; - var decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), res.GetDesktopSize().GetHeight()); - var activeState = ActiveStage.New(res); - - await ProcessActiveState(activeState, decodedImage, stream); + var (res, framed) = t.Result; + this.decodedImage = DecodedImage.New(PixelFormat.RgbA32, res.GetDesktopSize().GetWidth(), res.GetDesktopSize().GetHeight()); + this.activeStage = ActiveStage.New(res); + this.framed = framed; + ReadPduAndProcessActiveStage(); }); } - private void WriteDecodedImageToCanvas(DecodedImage decodedImage) + private void WriteDecodedImageToCanvas() { Dispatcher.UIThread.InvokeAsync(() => { - var data = decodedImage.GetData(); + var data = decodedImage!.GetData(); var buffer_size = (int)data.GetSize(); var buffer = new byte[buffer_size]; data.Fill(buffer); - using (var bitmap = this.bitmap.Lock()) + using (var bitmap = this.bitmap!.Lock()) { unsafe { @@ -81,68 +90,188 @@ private void WriteDecodedImageToCanvas(DecodedImage decodedImage) } // Assuming `image` is the Image control that needs to be updated. - image.InvalidateVisual(); // Force redraw of image + image!.InvalidateVisual(); // Force redraw of image }); } - private async Task ProcessActiveState(ActiveStage activeState, DecodedImage decodedImage, Framed framed) + private void ReadPduAndProcessActiveStage() { - var keepLooping = true; - while (keepLooping) + Task.Run(async () => { - var readPduTask = framed.ReadPdu(); - Action? action; - byte[]? payload; - if (readPduTask == await Task.WhenAny(readPduTask, Task.Delay(10000))) + var keepLooping = true; + while (keepLooping) { - var pduReadTask = await readPduTask; - action = pduReadTask.Item1; - payload = pduReadTask.Item2; - Console.WriteLine($"Action: {action}"); + Console.WriteLine("Reading PDU , updateCounter = " + updateCounter); + var readPduTask = await framed!.ReadPdu(); + Action action = readPduTask.Item1; + byte[] payload = readPduTask.Item2; + var outputIterator = activeStage!.Process(decodedImage!, action, payload); + keepLooping = await HandleActiveStageOutput(outputIterator); } - else - { - Console.WriteLine("Timeout"); - break; - } - var outputIterator = activeState.Process(decodedImage, action, payload); + }); + } + + private static Config buildConfig(string username, string password, string domain, int width, int height) + { + ConfigBuilder configBuilder = ConfigBuilder.New(); + + configBuilder.WithUsernameAndPassword(username, password); + configBuilder.SetDomain(domain); + configBuilder.SetDesktopSize((ushort)height, (ushort)width); + configBuilder.SetClientName("IronRdp"); + configBuilder.SetClientDir("C:\\"); + configBuilder.SetPerformanceFlags(PerformanceFlags.NewDefault()); + + return configBuilder.Build(); + } + + private void Canvas_OnPointerPressed(object sender, Avalonia.Input.PointerPressedEventArgs e) + { + Console.WriteLine("Mouse pressed"); + PointerUpdateKind mouseButton = e.GetCurrentPoint((Visual?)sender).Properties.PointerUpdateKind; + + MouseButtonType buttonType = mouseButton switch + { + PointerUpdateKind.LeftButtonPressed => MouseButtonType.Left, + PointerUpdateKind.RightButtonPressed => MouseButtonType.Right, + PointerUpdateKind.MiddleButtonPressed => MouseButtonType.Middle, + PointerUpdateKind.XButton1Pressed => MouseButtonType.X1, + PointerUpdateKind.XButton2Pressed => MouseButtonType.X2, + PointerUpdateKind.LeftButtonReleased => MouseButtonType.Left, + PointerUpdateKind.MiddleButtonReleased => MouseButtonType.Middle, + PointerUpdateKind.RightButtonReleased => MouseButtonType.Right, + PointerUpdateKind.XButton1Released => MouseButtonType.X1, + PointerUpdateKind.XButton2Released => MouseButtonType.X2, + PointerUpdateKind.Other => throw new NotImplementedException(), + _ => throw new NotImplementedException(), + }; + + var buttonOperation = MouseButton.New(buttonType).AsOperationMouseButtonPressed(); + var fastpath = inputDatabase!.Apply(buttonOperation); + var output = activeStage!.ProcessFastpathInput(decodedImage!, fastpath); + var _ = HandleActiveStageOutput(output); + } + + private void Canvas_PointerMoved(object sender, PointerEventArgs e) + { + if (this.activeStage == null || this.decodedImage == null) + { + return; + } + var position = e.GetPosition((Visual?)sender); + var x = (ushort)position.X; + var y = (ushort)position.Y; + var mouseMovedEvent = MousePosition.New(x, y).AsOperation(); + var fastpath = inputDatabase!.Apply(mouseMovedEvent); + var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); + Console.WriteLine($"Pointer moved to X: {x}, Y: {y}"); + var _ = HandleActiveStageOutput(output); + } + + private void Canvas_PointerReleased(object sender, PointerReleasedEventArgs e) + { + Console.WriteLine("Mouse released"); + PointerUpdateKind mouseButton = e.GetCurrentPoint((Visual?)sender).Properties.PointerUpdateKind; + + MouseButtonType buttonType = mouseButton switch + { + PointerUpdateKind.LeftButtonPressed => MouseButtonType.Left, + PointerUpdateKind.RightButtonPressed => MouseButtonType.Right, + PointerUpdateKind.MiddleButtonPressed => MouseButtonType.Middle, + PointerUpdateKind.XButton1Pressed => MouseButtonType.X1, + PointerUpdateKind.XButton2Pressed => MouseButtonType.X2, + PointerUpdateKind.LeftButtonReleased => MouseButtonType.Left, + PointerUpdateKind.MiddleButtonReleased => MouseButtonType.Middle, + PointerUpdateKind.RightButtonReleased => MouseButtonType.Right, + PointerUpdateKind.XButton1Released => MouseButtonType.X1, + PointerUpdateKind.XButton2Released => MouseButtonType.X2, + PointerUpdateKind.Other => throw new NotImplementedException(), + _ => throw new NotImplementedException(), + }; + + var buttonOperation = MouseButton.New(buttonType).AsOperationMouseButtonReleased(); + var fastpath = inputDatabase!.Apply(buttonOperation); + var output = activeStage!.ProcessFastpathInput(decodedImage!, fastpath); + var _ = HandleActiveStageOutput(output); + } + + private void Canvas_KeyDown(object? sender, KeyEventArgs? e) + { + if (activeStage == null || decodedImage == null) + { + return; + } + Console.WriteLine($"Key pressed: {e!.Key}"); + Key key = e.Key; + var keyOperation = Scancode.FromU16((ushort)e.Key).AsOperationKeyPressed(); + var fastpath = inputDatabase!.Apply(keyOperation); + var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); + var _ = HandleActiveStageOutput(output); + } + + private void Canvas_KeyUp(object? sender, KeyEventArgs? e) + { + if (this.activeStage == null || this.decodedImage == null) + { + return; + } + Console.WriteLine($"Key released: {e!.Key}"); + Key key = e.Key; + var keyOperation = Scancode.FromU16((ushort)key).AsOperationKeyReleased(); + var fastpath = inputDatabase!.Apply(keyOperation); + var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); + var _ = HandleActiveStageOutput(output); + } + + private ulong updateCounter = 0; + private async Task HandleActiveStageOutput(ActiveStageOutputIterator outputIterator) + { + try + { while (!outputIterator.IsEmpty()) { var output = outputIterator.Next()!; // outputIterator.Next() is not null since outputIterator.IsEmpty() is false - Console.WriteLine($"Output type: {output.GetType()}"); - if (output.GetType() == ActiveStageOutputType.Terminate) + Console.WriteLine("Handling output = " + updateCounter++, "output type " + output.GetEnumType()); + if (output.GetEnumType() == ActiveStageOutputType.Terminate) { - Console.WriteLine("Connection terminated."); - keepLooping = false; + return false; } - - if (output.GetType() == ActiveStageOutputType.ResponseFrame) + else if (output.GetEnumType() == ActiveStageOutputType.ResponseFrame) { // render the decoded image to canvas - WriteDecodedImageToCanvas(decodedImage); + WriteDecodedImageToCanvas(); // Send the response frame to the server var responseFrame = output.GetResponseFrame()!; byte[] responseFrameBytes = new byte[responseFrame.GetSize()]; responseFrame.Fill(responseFrameBytes); - await framed.Write(responseFrameBytes); + await framed!.Write(responseFrameBytes); + } + else if (output.GetEnumType() == ActiveStageOutputType.GraphicsUpdate) + { + WriteDecodedImageToCanvas(); + } + else if (output.GetEnumType() == ActiveStageOutputType.PointerPosition) + { + WriteDecodedImageToCanvas(); + } + else if (output.GetEnumType() == ActiveStageOutputType.PointerBitmap) + { + WriteDecodedImageToCanvas(); + } + else + { + Console.WriteLine("Unhandled output type " + output.GetEnumType()); } } + return true; + } + catch (Exception e) + { + Console.WriteLine("Error handling output: " + e.Message); + return false; } } - private static Config buildConfig(string servername, string username, string password, string domain, int width, int height) - { - ConfigBuilder configBuilder = ConfigBuilder.New(); - - configBuilder.WithUsernameAndPassword(username, password); - configBuilder.SetDomain(domain); - configBuilder.SetDesktopSize((ushort)height, (ushort)width); - configBuilder.SetClientName("IronRdp"); - configBuilder.SetClientDir("C:\\"); - configBuilder.SetPerformanceFlags(PerformanceFlags.NewDefault()); - - return configBuilder.Build(); - } } diff --git a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs index 3be56f89c..2d4d8c817 100644 --- a/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs +++ b/ffi/dotnet/Devolutions.IronRdp.ConnectExample/Program.cs @@ -47,13 +47,13 @@ static async Task Main(string[] args) { var output = outputIterator.Next()!; // outputIterator.Next() is not null since outputIterator.IsEmpty() is false Console.WriteLine($"Output type: {output.GetType()}"); - if (output.GetType() == ActiveStageOutputType.Terminate) + if (output.GetEnumType() == ActiveStageOutputType.Terminate) { Console.WriteLine("Connection terminated."); keepLooping = false; } - if (output.GetType() == ActiveStageOutputType.ResponseFrame) + if (output.GetEnumType() == ActiveStageOutputType.ResponseFrame) { var responseFrame = output.GetResponseFrame()!; byte[] responseFrameBytes = new byte[responseFrame.GetSize()]; diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStage.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStage.cs index ed32efa1c..105e06c11 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStage.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStage.cs @@ -91,6 +91,40 @@ public ActiveStageOutputIterator Process(DecodedImage image, Action action, byte } } + /// + /// + /// A ActiveStageOutputIterator allocated on Rust side. + /// + public ActiveStageOutputIterator ProcessFastpathInput(DecodedImage image, FastPathInputEventIterator fastpathInput) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("ActiveStage"); + } + Raw.DecodedImage* imageRaw; + imageRaw = image.AsFFI(); + if (imageRaw == null) + { + throw new ObjectDisposedException("DecodedImage"); + } + Raw.FastPathInputEventIterator* fastpathInputRaw; + fastpathInputRaw = fastpathInput.AsFFI(); + if (fastpathInputRaw == null) + { + throw new ObjectDisposedException("FastPathInputEventIterator"); + } + Raw.SessionFfiResultBoxActiveStageOutputIteratorBoxIronRdpError result = Raw.ActiveStage.ProcessFastpathInput(_inner, imageRaw, fastpathInputRaw); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.ActiveStageOutputIterator* retVal = result.Ok; + return new ActiveStageOutputIterator(retVal); + } + } + /// /// Returns the underlying raw handle. /// diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStageOutput.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStageOutput.cs index 4bc7022b3..44e407576 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStageOutput.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ActiveStageOutput.cs @@ -23,6 +23,14 @@ public ConnectionActivationSequence DeactivateAll } } + public ActiveStageOutputType EnumType + { + get + { + return GetEnumType(); + } + } + public InclusiveRectangle GraphicsUpdate { get @@ -63,14 +71,6 @@ public GracefulDisconnectReason Terminate } } - public ActiveStageOutputType Type - { - get - { - return GetType(); - } - } - /// /// Creates a managed ActiveStageOutput from a raw handle. /// @@ -88,7 +88,7 @@ public unsafe ActiveStageOutput(Raw.ActiveStageOutput* handle) /// /// A ActiveStageOutputType allocated on C# side. /// - public ActiveStageOutputType GetType() + public ActiveStageOutputType GetEnumType() { unsafe { @@ -96,7 +96,7 @@ public ActiveStageOutputType GetType() { throw new ObjectDisposedException("ActiveStageOutput"); } - Raw.ActiveStageOutputType retVal = Raw.ActiveStageOutput.GetType(_inner); + Raw.ActiveStageOutputType retVal = Raw.ActiveStageOutput.GetEnumType(_inner); return (ActiveStageOutputType)retVal; } } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs index 1b4b692df..efabee588 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs @@ -63,11 +63,11 @@ public SecurityProtocol EnhancedSecurityUpgradeSelectedProtocol } } - public ClientConnectorStateType Type + public ClientConnectorStateType EnumType { get { - return GetType(); + return GetEnumType(); } } @@ -89,7 +89,7 @@ public unsafe ClientConnectorState(Raw.ClientConnectorState* handle) /// /// A ClientConnectorStateType allocated on C# side. /// - public ClientConnectorStateType GetType() + public ClientConnectorStateType GetEnumType() { unsafe { @@ -97,7 +97,7 @@ public ClientConnectorStateType GetType() { throw new ObjectDisposedException("ClientConnectorState"); } - Raw.ConnectorStateFfiResultClientConnectorStateTypeBoxIronRdpError result = Raw.ClientConnectorState.GetType(_inner); + Raw.ConnectorStateFfiResultClientConnectorStateTypeBoxIronRdpError result = Raw.ClientConnectorState.GetEnumType(_inner); if (!result.isOk) { throw new IronRdpException(new IronRdpError(result.Err)); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/InputDatabase.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/InputDatabase.cs index fc59c3da7..d754dd559 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/InputDatabase.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/InputDatabase.cs @@ -41,6 +41,28 @@ public static InputDatabase New() } } + /// + /// A FastPathInputEventIterator allocated on Rust side. + /// + public FastPathInputEventIterator Apply(Operation operation) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("InputDatabase"); + } + Raw.Operation* operationRaw; + operationRaw = operation.AsFFI(); + if (operationRaw == null) + { + throw new ObjectDisposedException("Operation"); + } + Raw.FastPathInputEventIterator* retVal = Raw.InputDatabase.Apply(_inner, operationRaw); + return new FastPathInputEventIterator(retVal); + } + } + /// /// Returns the underlying raw handle. /// diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStage.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStage.cs index a0f3d27ee..ddda9e5e1 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStage.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStage.cs @@ -22,6 +22,9 @@ public partial struct ActiveStage [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ActiveStage_process", ExactSpelling = true)] public static unsafe extern SessionFfiResultBoxActiveStageOutputIteratorBoxIronRdpError Process(ActiveStage* self, DecodedImage* image, Action* action, byte* payload, nuint payloadSz); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ActiveStage_process_fastpath_input", ExactSpelling = true)] + public static unsafe extern SessionFfiResultBoxActiveStageOutputIteratorBoxIronRdpError ProcessFastpathInput(ActiveStage* self, DecodedImage* image, FastPathInputEventIterator* fastpathInput); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ActiveStage_destroy", ExactSpelling = true)] public static unsafe extern void Destroy(ActiveStage* self); } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStageOutput.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStageOutput.cs index 5aa25a44a..a39a90147 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStageOutput.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawActiveStageOutput.cs @@ -16,8 +16,8 @@ public partial struct ActiveStageOutput { private const string NativeLib = "DevolutionsIronRdp"; - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ActiveStageOutput_get_type", ExactSpelling = true)] - public static unsafe extern ActiveStageOutputType GetType(ActiveStageOutput* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ActiveStageOutput_get_enum_type", ExactSpelling = true)] + public static unsafe extern ActiveStageOutputType GetEnumType(ActiveStageOutput* self); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ActiveStageOutput_get_response_frame", ExactSpelling = true)] public static unsafe extern SessionFfiResultBoxBytesSliceBoxIronRdpError GetResponseFrame(ActiveStageOutput* self); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs index 06e1dd417..54b7f5a5f 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs @@ -16,8 +16,8 @@ public partial struct ClientConnectorState { private const string NativeLib = "DevolutionsIronRdp"; - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ClientConnectorState_get_type", ExactSpelling = true)] - public static unsafe extern ConnectorStateFfiResultClientConnectorStateTypeBoxIronRdpError GetType(ClientConnectorState* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ClientConnectorState_get_enum_type", ExactSpelling = true)] + public static unsafe extern ConnectorStateFfiResultClientConnectorStateTypeBoxIronRdpError GetEnumType(ClientConnectorState* self); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ClientConnectorState_get_connection_initiation_wait_confirm_requested_protocol", ExactSpelling = true)] public static unsafe extern ConnectorStateFfiResultBoxSecurityProtocolBoxIronRdpError GetConnectionInitiationWaitConfirmRequestedProtocol(ClientConnectorState* self); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputDatabase.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputDatabase.cs index b1699ddec..712591cd0 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputDatabase.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputDatabase.cs @@ -19,6 +19,9 @@ public partial struct InputDatabase [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "InputDatabase_new", ExactSpelling = true)] public static unsafe extern InputDatabase* New(); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "InputDatabase_apply", ExactSpelling = true)] + public static unsafe extern FastPathInputEventIterator* Apply(InputDatabase* self, Operation* operation); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "InputDatabase_destroy", ExactSpelling = true)] public static unsafe extern void Destroy(InputDatabase* self); } diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs index 38d2f8f71..3581e84d4 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Connection.cs @@ -73,7 +73,7 @@ private static async Task ConnectFinalize(string servername, C ClientConnectorState state = connector.ConsumeAndCastToClientConnectorState(); - if (state.GetType() == ClientConnectorStateType.Connected) + if (state.GetEnumType() == ClientConnectorStateType.Connected) { return state.GetConnectedResult(); } diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs index 56a336adb..029a99dc1 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs @@ -6,6 +6,7 @@ public class Framed where S : Stream { private S stream; private List buffer; + private readonly Mutex writeLock = new Mutex(); public Framed(S stream) { @@ -20,12 +21,13 @@ public Framed(S stream) public async Task<(Devolutions.IronRdp.Action, byte[])> ReadPdu() { - while (true) { + Console.WriteLine("ReadPdu, buffer size: " + this.buffer.Count); var pduInfo = IronRdpPdu.New().FindSize(this.buffer.ToArray()); if (null != pduInfo) { + Console.WriteLine("ReadPdu: ReadExact, pduInfo.GetLength()=" + pduInfo.GetLength()); var frame = await this.ReadExact(pduInfo.GetLength()); var action = pduInfo.GetAction(); return (action, frame); @@ -33,6 +35,7 @@ public Framed(S stream) else { var len = await this.Read(); + Console.WriteLine("ReadPdu: Read, len=" + len); if (len == 0) { @@ -63,12 +66,14 @@ public async Task ReadExact(nuint size) { if (buffer.Count >= (int)size) { + Console.WriteLine("ReadExact: Take, size=" + size); var res = this.buffer.Take((int)size).ToArray(); this.buffer = this.buffer.Skip((int)size).ToList(); return res; } var len = await this.Read(); + Console.WriteLine("ReadExact: Read, len = " + len); if (len == 0) { throw new Exception("EOF"); @@ -78,7 +83,7 @@ public async Task ReadExact(nuint size) async Task Read() { - var buffer = new byte[1024]; + var buffer = new byte[8096]; Memory memory = buffer; var size = await this.stream.ReadAsync(memory); this.buffer.AddRange(buffer.Take(size)); @@ -87,8 +92,20 @@ async Task Read() public async Task Write(byte[] data) { - ReadOnlyMemory memory = data; - await this.stream.WriteAsync(memory); + writeLock.WaitOne(); + try + { + ReadOnlyMemory memory = data; + await this.stream.WriteAsync(memory); + } + catch (Exception e) + { + Console.WriteLine("Write error: " + e.Message); + } + finally + { + writeLock.ReleaseMutex(); + } } diff --git a/ffi/src/connector/state.rs b/ffi/src/connector/state.rs index 2a9519ae2..63b11e40a 100644 --- a/ffi/src/connector/state.rs +++ b/ffi/src/connector/state.rs @@ -27,7 +27,7 @@ pub mod ffi { } impl ClientConnectorState { - pub fn get_type(&self) -> Result> { + pub fn get_enum_type(&self) -> Result> { let res = match &self .0 .as_ref() diff --git a/ffi/src/input.rs b/ffi/src/input.rs index ca7eba7aa..b8b3d7f45 100644 --- a/ffi/src/input.rs +++ b/ffi/src/input.rs @@ -1,5 +1,7 @@ #[diplomat::bridge] pub mod ffi { + use crate::pdu::ffi::FastPathInputEventIterator; + #[diplomat::opaque] pub struct InputDatabase(pub ironrdp::input::Database); @@ -7,5 +9,123 @@ pub mod ffi { pub fn new() -> Box { Box::new(InputDatabase(ironrdp::input::Database::new())) } + + pub fn apply(&mut self, operation: &Operation) -> Box { + let res = self.0.apply(std::iter::once(operation.0.clone())); + Box::new(res.to_vec().into()) + } + } + + #[diplomat::opaque] + pub struct Operation(pub ironrdp::input::Operation); + + pub enum OperationType { + MouseButtonPressed, + MouseButtonReleased, + MouseMove, + WheelRotations, + KeyPressed, + KeyReleased, + UnicodeKeyPressed, + UnicodeKeyReleased, + } + + #[diplomat::opaque] + pub struct MousePosition(pub ironrdp::input::MousePosition); + + impl MousePosition { + pub fn new(x: u16, y: u16) -> Box { + Box::new(MousePosition(ironrdp::input::MousePosition { x, y })) + } + + pub fn as_operation(&self) -> Box { + Box::new(Operation(ironrdp::input::Operation::MouseMove(self.0))) + } + } + + #[diplomat::opaque] + pub struct MouseButton(pub ironrdp::input::MouseButton); + + #[diplomat::enum_convert(ironrdp::input::MouseButton)] + pub enum MouseButtonType { + Left = 0, + Middle = 1, + Right = 2, + X1 = 3, + X2 = 4, + } + + impl MouseButton { + pub fn new(button: MouseButtonType) -> Box { + Box::new(MouseButton(button.into())) + } + + pub fn as_operation_mouse_button_pressed(&self) -> Box { + let operation = ironrdp::input::Operation::MouseButtonPressed(self.0); + Box::new(Operation(operation)) + } + + pub fn as_operation_mouse_button_released(&self) -> Box { + let operation = ironrdp::input::Operation::MouseButtonReleased(self.0); + Box::new(Operation(operation)) + } + } + + #[diplomat::opaque] + pub struct WheelRotations(pub ironrdp::input::WheelRotations); + + impl WheelRotations { + pub fn new(is_vertical: bool, rotation_units: i16) -> Box { + Box::new(WheelRotations(ironrdp::input::WheelRotations { + is_vertical, + rotation_units, + })) + } + + pub fn as_operation(&self) -> Box { + Box::new(Operation(ironrdp::input::Operation::WheelRotations(self.0))) + } + } + + #[diplomat::opaque] + pub struct Scancode(pub ironrdp::input::Scancode); + + impl Scancode { + pub fn from_u8(extended: bool, code: u8) -> Box { + Box::new(Scancode(ironrdp::input::Scancode::from_u8(extended, code))) + } + + pub fn from_u16(code: u16) -> Box { + Box::new(Scancode(ironrdp::input::Scancode::from_u16(code))) + } + + pub fn as_operation_key_pressed(&self) -> Box { + let operation = ironrdp::input::Operation::KeyPressed(self.0); + Box::new(Operation(operation)) + } + + pub fn as_operation_key_released(&self) -> Box { + let operation = ironrdp::input::Operation::KeyReleased(self.0); + Box::new(Operation(operation)) + } + } + + #[diplomat::opaque] + pub struct Char(pub char); + + impl Char { + pub fn new(c: char) -> Box { + Box::new(Char(c)) + } + + pub fn as_operation_unicode_key_pressed(&self) -> Box { + let operation = ironrdp::input::Operation::UnicodeKeyPressed(self.0); + Box::new(Operation(operation)) + } + + pub fn as_operation_unicode_key_released(&self) -> Box { + let operation = ironrdp::input::Operation::UnicodeKeyReleased(self.0); + Box::new(Operation(operation)) + } } } diff --git a/ffi/src/pdu.rs b/ffi/src/pdu.rs index 57bb11555..f672b7f6e 100644 --- a/ffi/src/pdu.rs +++ b/ffi/src/pdu.rs @@ -58,4 +58,18 @@ pub mod ffi { Ok(ironrdp::pdu::find_size(bytes)?.map(PduInfo).map(Box::new)) } } + + #[diplomat::opaque] + pub struct FastPathInputEvent(pub ironrdp::pdu::input::fast_path::FastPathInputEvent); + + #[diplomat::opaque] + pub struct FastPathInputEventIterator(pub Vec); + + } + +impl From> for ffi::FastPathInputEventIterator{ + fn from(value: Vec) -> Self { + ffi::FastPathInputEventIterator(value) + } +} \ No newline at end of file diff --git a/ffi/src/session/mod.rs b/ffi/src/session/mod.rs index 1ba65faae..f4aca56a2 100644 --- a/ffi/src/session/mod.rs +++ b/ffi/src/session/mod.rs @@ -7,7 +7,7 @@ pub mod ffi { connector::{ffi::ConnectionActivationSequence, result::ffi::ConnectionResult}, error::{ffi::IronRdpError, IncorrectEnumTypeError, ValueConsumedError}, graphics::ffi::DecodedPointer, - pdu::ffi::{Action, InclusiveRectangle}, + pdu::ffi::{Action, FastPathInputEventIterator, InclusiveRectangle}, utils::ffi::{BytesSlice, Position}, }; @@ -55,6 +55,17 @@ pub mod ffi { let outputs = self.0.process(&mut image.0, action.0, payload)?; Ok(Box::new(ActiveStageOutputIterator(outputs))) } + + pub fn process_fastpath_input( + &mut self, + image: &mut DecodedImage, + fastpath_input: &FastPathInputEventIterator, + ) -> Result, Box> { + Ok(self + .0 + .process_fastpath_input(&mut image.0, &fastpath_input.0) + .map(|outputs| Box::new(ActiveStageOutputIterator(outputs)))?) + } } pub enum ActiveStageOutputType { @@ -69,7 +80,7 @@ pub mod ffi { } impl ActiveStageOutput { - pub fn get_type(&self) -> ActiveStageOutputType { + pub fn get_enum_type(&self) -> ActiveStageOutputType { match &self.0 { ironrdp::session::ActiveStageOutput::ResponseFrame { .. } => ActiveStageOutputType::ResponseFrame, ironrdp::session::ActiveStageOutput::GraphicsUpdate { .. } => ActiveStageOutputType::GraphicsUpdate, diff --git a/ffi/src/utils/mod.rs b/ffi/src/utils/mod.rs index 672c5653e..bcdb1321e 100644 --- a/ffi/src/utils/mod.rs +++ b/ffi/src/utils/mod.rs @@ -79,4 +79,5 @@ pub mod ffi { self.0.ok_or_else(|| "value is None".into()) } } + } From 26aa6629fe3d9f255a358301324b0556fe22770d Mon Sep 17 00:00:00 2001 From: irving ou Date: Wed, 17 Apr 2024 16:00:46 -0400 Subject: [PATCH 03/21] WIP:Need to fix keyboard --- .../Devolutions.IronRdp/Generated/Char.cs | 107 ++++++++++++++++ .../Generated/FastPathInputEvent.cs | 63 ++++++++++ .../Generated/FastPathInputEventIterator.cs | 63 ++++++++++ .../Generated/MouseButton.cs | 109 ++++++++++++++++ .../Generated/MouseButtonType.cs | 21 ++++ .../Generated/MousePosition.cs | 91 ++++++++++++++ .../Generated/Operation.cs | 63 ++++++++++ .../Generated/OperationType.cs | 24 ++++ .../Devolutions.IronRdp/Generated/RawChar.cs | 30 +++++ .../Generated/RawFastPathInputEvent.cs | 21 ++++ .../RawFastPathInputEventIterator.cs | 21 ++++ .../Generated/RawMouseButton.cs | 30 +++++ .../Generated/RawMouseButtonType.cs | 21 ++++ .../Generated/RawMousePosition.cs | 27 ++++ .../Generated/RawOperation.cs | 21 ++++ .../Generated/RawOperationType.cs | 24 ++++ .../Generated/RawScancode.cs | 33 +++++ .../Generated/RawWheelRotations.cs | 27 ++++ .../Devolutions.IronRdp/Generated/Scancode.cs | 119 ++++++++++++++++++ .../Generated/WheelRotations.cs | 91 ++++++++++++++ 20 files changed, 1006 insertions(+) create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEvent.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEventIterator.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/MouseButton.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/MouseButtonType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/Operation.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/OperationType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEvent.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEventIterator.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButton.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButtonType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawOperation.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawOperationType.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawScancode.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawWheelRotations.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/Scancode.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/WheelRotations.cs diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs new file mode 100644 index 000000000..af00c2c40 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs @@ -0,0 +1,107 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class Char: IDisposable +{ + private unsafe Raw.Char* _inner; + + /// + /// Creates a managed Char from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe Char(Raw.Char* handle) + { + _inner = handle; + } + + /// + /// A Char allocated on Rust side. + /// + public static Char New(uint c) + { + unsafe + { + Raw.Char* retVal = Raw.Char.New(c); + return new Char(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperationUnicodeKeyPressed() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("Char"); + } + Raw.Operation* retVal = Raw.Char.AsOperationUnicodeKeyPressed(_inner); + return new Operation(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperationUnicodeKeyReleased() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("Char"); + } + Raw.Operation* retVal = Raw.Char.AsOperationUnicodeKeyReleased(_inner); + return new Operation(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.Char* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.Char.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~Char() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEvent.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEvent.cs new file mode 100644 index 000000000..2323938f4 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEvent.cs @@ -0,0 +1,63 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class FastPathInputEvent: IDisposable +{ + private unsafe Raw.FastPathInputEvent* _inner; + + /// + /// Creates a managed FastPathInputEvent from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe FastPathInputEvent(Raw.FastPathInputEvent* handle) + { + _inner = handle; + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.FastPathInputEvent* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.FastPathInputEvent.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~FastPathInputEvent() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEventIterator.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEventIterator.cs new file mode 100644 index 000000000..e68281465 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/FastPathInputEventIterator.cs @@ -0,0 +1,63 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class FastPathInputEventIterator: IDisposable +{ + private unsafe Raw.FastPathInputEventIterator* _inner; + + /// + /// Creates a managed FastPathInputEventIterator from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe FastPathInputEventIterator(Raw.FastPathInputEventIterator* handle) + { + _inner = handle; + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.FastPathInputEventIterator* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.FastPathInputEventIterator.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~FastPathInputEventIterator() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/MouseButton.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/MouseButton.cs new file mode 100644 index 000000000..70dee4bdb --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/MouseButton.cs @@ -0,0 +1,109 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class MouseButton: IDisposable +{ + private unsafe Raw.MouseButton* _inner; + + /// + /// Creates a managed MouseButton from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe MouseButton(Raw.MouseButton* handle) + { + _inner = handle; + } + + /// + /// A MouseButton allocated on Rust side. + /// + public static MouseButton New(MouseButtonType button) + { + unsafe + { + Raw.MouseButtonType buttonRaw; + buttonRaw = (Raw.MouseButtonType)button; + Raw.MouseButton* retVal = Raw.MouseButton.New(buttonRaw); + return new MouseButton(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperationMouseButtonPressed() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("MouseButton"); + } + Raw.Operation* retVal = Raw.MouseButton.AsOperationMouseButtonPressed(_inner); + return new Operation(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperationMouseButtonReleased() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("MouseButton"); + } + Raw.Operation* retVal = Raw.MouseButton.AsOperationMouseButtonReleased(_inner); + return new Operation(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.MouseButton* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.MouseButton.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~MouseButton() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/MouseButtonType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/MouseButtonType.cs new file mode 100644 index 000000000..e84e07518 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/MouseButtonType.cs @@ -0,0 +1,21 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public enum MouseButtonType +{ + Left = 0, + Middle = 1, + Right = 2, + X1 = 3, + X2 = 4, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs new file mode 100644 index 000000000..6e80279af --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs @@ -0,0 +1,91 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class MousePosition: IDisposable +{ + private unsafe Raw.MousePosition* _inner; + + /// + /// Creates a managed MousePosition from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe MousePosition(Raw.MousePosition* handle) + { + _inner = handle; + } + + /// + /// A MousePosition allocated on Rust side. + /// + public static MousePosition New(ushort x, ushort y) + { + unsafe + { + Raw.MousePosition* retVal = Raw.MousePosition.New(x, y); + return new MousePosition(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperation() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("MousePosition"); + } + Raw.Operation* retVal = Raw.MousePosition.AsOperation(_inner); + return new Operation(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.MousePosition* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.MousePosition.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~MousePosition() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/Operation.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/Operation.cs new file mode 100644 index 000000000..a7a3dcd21 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/Operation.cs @@ -0,0 +1,63 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class Operation: IDisposable +{ + private unsafe Raw.Operation* _inner; + + /// + /// Creates a managed Operation from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe Operation(Raw.Operation* handle) + { + _inner = handle; + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.Operation* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.Operation.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~Operation() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/OperationType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/OperationType.cs new file mode 100644 index 000000000..553542410 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/OperationType.cs @@ -0,0 +1,24 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public enum OperationType +{ + MouseButtonPressed = 0, + MouseButtonReleased = 1, + MouseMove = 2, + WheelRotations = 3, + KeyPressed = 4, + KeyReleased = 5, + UnicodeKeyPressed = 6, + UnicodeKeyReleased = 7, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs new file mode 100644 index 000000000..8bef300cc --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs @@ -0,0 +1,30 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct Char +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_new", ExactSpelling = true)] + public static unsafe extern Char* New(uint c); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_as_operation_unicode_key_pressed", ExactSpelling = true)] + public static unsafe extern Operation* AsOperationUnicodeKeyPressed(Char* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_as_operation_unicode_key_released", ExactSpelling = true)] + public static unsafe extern Operation* AsOperationUnicodeKeyReleased(Char* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(Char* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEvent.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEvent.cs new file mode 100644 index 000000000..b64183050 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEvent.cs @@ -0,0 +1,21 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct FastPathInputEvent +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "FastPathInputEvent_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(FastPathInputEvent* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEventIterator.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEventIterator.cs new file mode 100644 index 000000000..df9c1617e --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawFastPathInputEventIterator.cs @@ -0,0 +1,21 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct FastPathInputEventIterator +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "FastPathInputEventIterator_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(FastPathInputEventIterator* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButton.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButton.cs new file mode 100644 index 000000000..bc7dbb033 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButton.cs @@ -0,0 +1,30 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct MouseButton +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MouseButton_new", ExactSpelling = true)] + public static unsafe extern MouseButton* New(MouseButtonType button); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MouseButton_as_operation_mouse_button_pressed", ExactSpelling = true)] + public static unsafe extern Operation* AsOperationMouseButtonPressed(MouseButton* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MouseButton_as_operation_mouse_button_released", ExactSpelling = true)] + public static unsafe extern Operation* AsOperationMouseButtonReleased(MouseButton* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MouseButton_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(MouseButton* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButtonType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButtonType.cs new file mode 100644 index 000000000..ae7994ebe --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMouseButtonType.cs @@ -0,0 +1,21 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +public enum MouseButtonType +{ + Left = 0, + Middle = 1, + Right = 2, + X1 = 3, + X2 = 4, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs new file mode 100644 index 000000000..cc3e74ef9 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs @@ -0,0 +1,27 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct MousePosition +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MousePosition_new", ExactSpelling = true)] + public static unsafe extern MousePosition* New(ushort x, ushort y); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MousePosition_as_operation", ExactSpelling = true)] + public static unsafe extern Operation* AsOperation(MousePosition* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MousePosition_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(MousePosition* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawOperation.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawOperation.cs new file mode 100644 index 000000000..5bd5f7621 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawOperation.cs @@ -0,0 +1,21 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct Operation +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Operation_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(Operation* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawOperationType.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawOperationType.cs new file mode 100644 index 000000000..01ab36490 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawOperationType.cs @@ -0,0 +1,24 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +public enum OperationType +{ + MouseButtonPressed = 0, + MouseButtonReleased = 1, + MouseMove = 2, + WheelRotations = 3, + KeyPressed = 4, + KeyReleased = 5, + UnicodeKeyPressed = 6, + UnicodeKeyReleased = 7, +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawScancode.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawScancode.cs new file mode 100644 index 000000000..36b28edd7 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawScancode.cs @@ -0,0 +1,33 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct Scancode +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Scancode_from_u8", ExactSpelling = true)] + public static unsafe extern Scancode* FromU8([MarshalAs(UnmanagedType.U1)] bool extended, byte code); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Scancode_from_u16", ExactSpelling = true)] + public static unsafe extern Scancode* FromU16(ushort code); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Scancode_as_operation_key_pressed", ExactSpelling = true)] + public static unsafe extern Operation* AsOperationKeyPressed(Scancode* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Scancode_as_operation_key_released", ExactSpelling = true)] + public static unsafe extern Operation* AsOperationKeyReleased(Scancode* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Scancode_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(Scancode* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawWheelRotations.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawWheelRotations.cs new file mode 100644 index 000000000..95185c5f8 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawWheelRotations.cs @@ -0,0 +1,27 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct WheelRotations +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "WheelRotations_new", ExactSpelling = true)] + public static unsafe extern WheelRotations* New([MarshalAs(UnmanagedType.U1)] bool isVertical, short rotationUnits); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "WheelRotations_as_operation", ExactSpelling = true)] + public static unsafe extern Operation* AsOperation(WheelRotations* self); + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "WheelRotations_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(WheelRotations* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/Scancode.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/Scancode.cs new file mode 100644 index 000000000..dbc7504f9 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/Scancode.cs @@ -0,0 +1,119 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class Scancode: IDisposable +{ + private unsafe Raw.Scancode* _inner; + + /// + /// Creates a managed Scancode from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe Scancode(Raw.Scancode* handle) + { + _inner = handle; + } + + /// + /// A Scancode allocated on Rust side. + /// + public static Scancode FromU8(bool extended, byte code) + { + unsafe + { + Raw.Scancode* retVal = Raw.Scancode.FromU8(extended, code); + return new Scancode(retVal); + } + } + + /// + /// A Scancode allocated on Rust side. + /// + public static Scancode FromU16(ushort code) + { + unsafe + { + Raw.Scancode* retVal = Raw.Scancode.FromU16(code); + return new Scancode(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperationKeyPressed() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("Scancode"); + } + Raw.Operation* retVal = Raw.Scancode.AsOperationKeyPressed(_inner); + return new Operation(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperationKeyReleased() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("Scancode"); + } + Raw.Operation* retVal = Raw.Scancode.AsOperationKeyReleased(_inner); + return new Operation(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.Scancode* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.Scancode.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~Scancode() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/WheelRotations.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/WheelRotations.cs new file mode 100644 index 000000000..97077dd59 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/WheelRotations.cs @@ -0,0 +1,91 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class WheelRotations: IDisposable +{ + private unsafe Raw.WheelRotations* _inner; + + /// + /// Creates a managed WheelRotations from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe WheelRotations(Raw.WheelRotations* handle) + { + _inner = handle; + } + + /// + /// A WheelRotations allocated on Rust side. + /// + public static WheelRotations New(bool isVertical, short rotationUnits) + { + unsafe + { + Raw.WheelRotations* retVal = Raw.WheelRotations.New(isVertical, rotationUnits); + return new WheelRotations(retVal); + } + } + + /// + /// A Operation allocated on Rust side. + /// + public Operation AsOperation() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("WheelRotations"); + } + Raw.Operation* retVal = Raw.WheelRotations.AsOperation(_inner); + return new Operation(retVal); + } + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.WheelRotations* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.WheelRotations.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~WheelRotations() + { + Dispose(); + } +} From 458de5b732d77e1b4e68129dfa3446646c5729cd Mon Sep 17 00:00:00 2001 From: irving ou Date: Wed, 17 Apr 2024 16:16:27 -0400 Subject: [PATCH 04/21] WIP:Need to fix keyboard 2 --- .../Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index be5c52ee0..be87a59d1 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -203,8 +203,8 @@ private void Canvas_KeyDown(object? sender, KeyEventArgs? e) return; } Console.WriteLine($"Key pressed: {e!.Key}"); - Key key = e.Key; - var keyOperation = Scancode.FromU16((ushort)e.Key).AsOperationKeyPressed(); + PhysicalKey physicalKey = e.PhysicalKey; + var keyOperation = Scancode.FromU16(KeyCodeMapper.GetScancode(physicalKey)).AsOperationKeyPressed(); var fastpath = inputDatabase!.Apply(keyOperation); var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); var _ = HandleActiveStageOutput(output); From 30fd6185aed570f7ed0a1d11ff8e773b37912b68 Mon Sep 17 00:00:00 2001 From: irving ou Date: Wed, 17 Apr 2024 16:16:36 -0400 Subject: [PATCH 05/21] WIP:Need to fix keyboard 3 --- .../KeyCodeMapper.cs | 146 ++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs new file mode 100644 index 000000000..92bd989ce --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs @@ -0,0 +1,146 @@ +using Avalonia.Input; +using System.Collections.Generic; + +public static class KeyCodeMapper +{ + private static readonly Dictionary KeyToScancodeMap = new Dictionary + { + { PhysicalKey.None, 0x00 }, + { PhysicalKey.A, 0x04 }, + { PhysicalKey.B, 0x05 }, + { PhysicalKey.C, 0x06 }, + { PhysicalKey.D, 0x07 }, + { PhysicalKey.E, 0x08 }, + { PhysicalKey.F, 0x09 }, + { PhysicalKey.G, 0x0A }, + { PhysicalKey.H, 0x0B }, + { PhysicalKey.I, 0x0C }, + { PhysicalKey.J, 0x0D }, + { PhysicalKey.K, 0x0E }, + { PhysicalKey.L, 0x0F }, + { PhysicalKey.M, 0x10 }, + { PhysicalKey.N, 0x11 }, + { PhysicalKey.O, 0x12 }, + { PhysicalKey.P, 0x13 }, + { PhysicalKey.Q, 0x14 }, + { PhysicalKey.R, 0x15 }, + { PhysicalKey.S, 0x16 }, + { PhysicalKey.T, 0x17 }, + { PhysicalKey.U, 0x18 }, + { PhysicalKey.V, 0x19 }, + { PhysicalKey.W, 0x1A }, + { PhysicalKey.X, 0x1B }, + { PhysicalKey.Y, 0x1C }, + { PhysicalKey.Z, 0x1D }, + { PhysicalKey.Digit1, 0x1E }, + { PhysicalKey.Digit2, 0x1F }, + { PhysicalKey.Digit3, 0x20 }, + { PhysicalKey.Digit4, 0x21 }, + { PhysicalKey.Digit5, 0x22 }, + { PhysicalKey.Digit6, 0x23 }, + { PhysicalKey.Digit7, 0x24 }, + { PhysicalKey.Digit8, 0x25 }, + { PhysicalKey.Digit9, 0x26 }, + { PhysicalKey.Digit0, 0x27 }, + { PhysicalKey.Enter, 0x28 }, + { PhysicalKey.Escape, 0x29 }, + { PhysicalKey.Backspace, 0x2A }, + { PhysicalKey.Tab, 0x2B }, + { PhysicalKey.Space, 0x2C }, + { PhysicalKey.Minus, 0x2D }, + { PhysicalKey.Equal, 0x2E }, + { PhysicalKey.BracketLeft, 0x2F }, + { PhysicalKey.BracketRight, 0x30 }, + { PhysicalKey.Backslash, 0x31 }, + { PhysicalKey.Semicolon, 0x33 }, + { PhysicalKey.Quote, 0x34 }, + { PhysicalKey.Backquote, 0x35 }, + { PhysicalKey.Comma, 0x36 }, + { PhysicalKey.Period, 0x37 }, + { PhysicalKey.Slash, 0x38 }, + { PhysicalKey.CapsLock, 0x39 }, + { PhysicalKey.F1, 0x3A }, + { PhysicalKey.F2, 0x3B }, + { PhysicalKey.F3, 0x3C }, + { PhysicalKey.F4, 0x3D }, + { PhysicalKey.F5, 0x3E }, + { PhysicalKey.F6, 0x3F }, + { PhysicalKey.F7, 0x40 }, + { PhysicalKey.F8, 0x41 }, + { PhysicalKey.F9, 0x42 }, + { PhysicalKey.F10, 0x43 }, + { PhysicalKey.F11, 0x44 }, + { PhysicalKey.F12, 0x45 }, + { PhysicalKey.PrintScreen, 0x46 }, + { PhysicalKey.ScrollLock, 0x47 }, + { PhysicalKey.Pause, 0x48 }, + { PhysicalKey.Insert, 0x49 }, + { PhysicalKey.Home, 0x4A }, + { PhysicalKey.PageUp, 0x4B }, + { PhysicalKey.Delete, 0x4C }, + { PhysicalKey.End, 0x4D }, + { PhysicalKey.PageDown, 0x4E }, + { PhysicalKey.ArrowRight, 0x4F }, + { PhysicalKey.ArrowLeft, 0x50 }, + { PhysicalKey.ArrowDown, 0x51 }, + { PhysicalKey.ArrowUp, 0x52 }, + { PhysicalKey.NumLock, 0x53 }, + { PhysicalKey.NumPadDivide, 0x54 }, + { PhysicalKey.NumPadMultiply, 0x55 }, + { PhysicalKey.NumPadSubtract, 0x56 }, + { PhysicalKey.NumPadAdd, 0x57 }, + { PhysicalKey.NumPadEnter, 0x58 }, + { PhysicalKey.NumPad1, 0x59 }, + { PhysicalKey.NumPad2, 0x5A }, + { PhysicalKey.NumPad3, 0x5B }, + { PhysicalKey.NumPad4, 0x5C }, + { PhysicalKey.NumPad5, 0x5D }, + { PhysicalKey.NumPad6, 0x5E }, + { PhysicalKey.NumPad7, 0x5F }, + { PhysicalKey.NumPad8, 0x60 }, + { PhysicalKey.NumPad9, 0x61 }, + { PhysicalKey.NumPad0, 0x62 }, + { PhysicalKey.NumPadDecimal, 0x63 }, + { PhysicalKey.IntlBackslash, 0x64 }, + { PhysicalKey.ContextMenu, 0x65 }, + { PhysicalKey.Power, 0x66 }, + { PhysicalKey.NumPadEqual, 0x67 }, + { PhysicalKey.F13, 0x68 }, + { PhysicalKey.F14, 0x69 }, + { PhysicalKey.F15, 0x6A }, + { PhysicalKey.F16, 0x6B }, + { PhysicalKey.F17, 0x6C }, + { PhysicalKey.F18, 0x6D }, + { PhysicalKey.F19, 0x6E }, + { PhysicalKey.F20, 0x6F }, + { PhysicalKey.F21, 0x70 }, + { PhysicalKey.F22, 0x71 }, + { PhysicalKey.F23, 0x72 }, + { PhysicalKey.F24, 0x73 }, + { PhysicalKey.Open, 0x74 }, + { PhysicalKey.Help, 0x75 }, + { PhysicalKey.Props, 0x76 }, + { PhysicalKey.Again, 0x79 }, + { PhysicalKey.Undo, 0x7A }, + { PhysicalKey.Cut, 0x7B }, + { PhysicalKey.Copy, 0x7C }, + { PhysicalKey.Paste, 0x7D }, + { PhysicalKey.Find, 0x7E }, + { PhysicalKey.NumPadComma, 0x85 }, + { PhysicalKey.Lang1, 0x90 }, + { PhysicalKey.Lang2, 0x91 }, + { PhysicalKey.Lang3, 0x92 }, + { PhysicalKey.Lang4, 0x93 }, + { PhysicalKey.Lang5, 0x94 }, + // Additional keys can be mapped here + }; + + public static ushort GetScancode(PhysicalKey key) + { + if (KeyToScancodeMap.TryGetValue(key, out ushort scancode)) + { + return scancode; + } + throw new KeyNotFoundException($"Key {key} not found in the map"); + } +} From c114dc77478441da04c24d550027d4a42bfc0869 Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 10:44:40 -0400 Subject: [PATCH 06/21] WIP:fix keyboard --- .../KeyCodeMapper.cs | 213 +++++++----------- 1 file changed, 85 insertions(+), 128 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs index 92bd989ce..2fe906880 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs @@ -5,136 +5,93 @@ public static class KeyCodeMapper { private static readonly Dictionary KeyToScancodeMap = new Dictionary { - { PhysicalKey.None, 0x00 }, - { PhysicalKey.A, 0x04 }, - { PhysicalKey.B, 0x05 }, - { PhysicalKey.C, 0x06 }, - { PhysicalKey.D, 0x07 }, - { PhysicalKey.E, 0x08 }, - { PhysicalKey.F, 0x09 }, - { PhysicalKey.G, 0x0A }, - { PhysicalKey.H, 0x0B }, - { PhysicalKey.I, 0x0C }, - { PhysicalKey.J, 0x0D }, - { PhysicalKey.K, 0x0E }, - { PhysicalKey.L, 0x0F }, - { PhysicalKey.M, 0x10 }, - { PhysicalKey.N, 0x11 }, - { PhysicalKey.O, 0x12 }, - { PhysicalKey.P, 0x13 }, - { PhysicalKey.Q, 0x14 }, - { PhysicalKey.R, 0x15 }, - { PhysicalKey.S, 0x16 }, - { PhysicalKey.T, 0x17 }, - { PhysicalKey.U, 0x18 }, - { PhysicalKey.V, 0x19 }, - { PhysicalKey.W, 0x1A }, - { PhysicalKey.X, 0x1B }, - { PhysicalKey.Y, 0x1C }, - { PhysicalKey.Z, 0x1D }, - { PhysicalKey.Digit1, 0x1E }, - { PhysicalKey.Digit2, 0x1F }, - { PhysicalKey.Digit3, 0x20 }, - { PhysicalKey.Digit4, 0x21 }, - { PhysicalKey.Digit5, 0x22 }, - { PhysicalKey.Digit6, 0x23 }, - { PhysicalKey.Digit7, 0x24 }, - { PhysicalKey.Digit8, 0x25 }, - { PhysicalKey.Digit9, 0x26 }, - { PhysicalKey.Digit0, 0x27 }, - { PhysicalKey.Enter, 0x28 }, - { PhysicalKey.Escape, 0x29 }, - { PhysicalKey.Backspace, 0x2A }, - { PhysicalKey.Tab, 0x2B }, - { PhysicalKey.Space, 0x2C }, - { PhysicalKey.Minus, 0x2D }, - { PhysicalKey.Equal, 0x2E }, - { PhysicalKey.BracketLeft, 0x2F }, - { PhysicalKey.BracketRight, 0x30 }, - { PhysicalKey.Backslash, 0x31 }, - { PhysicalKey.Semicolon, 0x33 }, - { PhysicalKey.Quote, 0x34 }, - { PhysicalKey.Backquote, 0x35 }, - { PhysicalKey.Comma, 0x36 }, - { PhysicalKey.Period, 0x37 }, - { PhysicalKey.Slash, 0x38 }, - { PhysicalKey.CapsLock, 0x39 }, - { PhysicalKey.F1, 0x3A }, - { PhysicalKey.F2, 0x3B }, - { PhysicalKey.F3, 0x3C }, - { PhysicalKey.F4, 0x3D }, - { PhysicalKey.F5, 0x3E }, - { PhysicalKey.F6, 0x3F }, - { PhysicalKey.F7, 0x40 }, - { PhysicalKey.F8, 0x41 }, - { PhysicalKey.F9, 0x42 }, - { PhysicalKey.F10, 0x43 }, - { PhysicalKey.F11, 0x44 }, - { PhysicalKey.F12, 0x45 }, - { PhysicalKey.PrintScreen, 0x46 }, - { PhysicalKey.ScrollLock, 0x47 }, - { PhysicalKey.Pause, 0x48 }, - { PhysicalKey.Insert, 0x49 }, - { PhysicalKey.Home, 0x4A }, - { PhysicalKey.PageUp, 0x4B }, - { PhysicalKey.Delete, 0x4C }, - { PhysicalKey.End, 0x4D }, - { PhysicalKey.PageDown, 0x4E }, - { PhysicalKey.ArrowRight, 0x4F }, - { PhysicalKey.ArrowLeft, 0x50 }, - { PhysicalKey.ArrowDown, 0x51 }, - { PhysicalKey.ArrowUp, 0x52 }, - { PhysicalKey.NumLock, 0x53 }, - { PhysicalKey.NumPadDivide, 0x54 }, - { PhysicalKey.NumPadMultiply, 0x55 }, - { PhysicalKey.NumPadSubtract, 0x56 }, - { PhysicalKey.NumPadAdd, 0x57 }, - { PhysicalKey.NumPadEnter, 0x58 }, - { PhysicalKey.NumPad1, 0x59 }, - { PhysicalKey.NumPad2, 0x5A }, - { PhysicalKey.NumPad3, 0x5B }, - { PhysicalKey.NumPad4, 0x5C }, - { PhysicalKey.NumPad5, 0x5D }, - { PhysicalKey.NumPad6, 0x5E }, - { PhysicalKey.NumPad7, 0x5F }, - { PhysicalKey.NumPad8, 0x60 }, - { PhysicalKey.NumPad9, 0x61 }, - { PhysicalKey.NumPad0, 0x62 }, - { PhysicalKey.NumPadDecimal, 0x63 }, - { PhysicalKey.IntlBackslash, 0x64 }, - { PhysicalKey.ContextMenu, 0x65 }, - { PhysicalKey.Power, 0x66 }, - { PhysicalKey.NumPadEqual, 0x67 }, - { PhysicalKey.F13, 0x68 }, - { PhysicalKey.F14, 0x69 }, - { PhysicalKey.F15, 0x6A }, - { PhysicalKey.F16, 0x6B }, - { PhysicalKey.F17, 0x6C }, - { PhysicalKey.F18, 0x6D }, - { PhysicalKey.F19, 0x6E }, - { PhysicalKey.F20, 0x6F }, - { PhysicalKey.F21, 0x70 }, - { PhysicalKey.F22, 0x71 }, - { PhysicalKey.F23, 0x72 }, - { PhysicalKey.F24, 0x73 }, - { PhysicalKey.Open, 0x74 }, - { PhysicalKey.Help, 0x75 }, - { PhysicalKey.Props, 0x76 }, - { PhysicalKey.Again, 0x79 }, - { PhysicalKey.Undo, 0x7A }, - { PhysicalKey.Cut, 0x7B }, - { PhysicalKey.Copy, 0x7C }, - { PhysicalKey.Paste, 0x7D }, - { PhysicalKey.Find, 0x7E }, - { PhysicalKey.NumPadComma, 0x85 }, - { PhysicalKey.Lang1, 0x90 }, - { PhysicalKey.Lang2, 0x91 }, - { PhysicalKey.Lang3, 0x92 }, - { PhysicalKey.Lang4, 0x93 }, - { PhysicalKey.Lang5, 0x94 }, - // Additional keys can be mapped here + {PhysicalKey.Escape, 0x01}, + {PhysicalKey.Digit1, 0x02}, + {PhysicalKey.Digit2, 0x03}, + {PhysicalKey.Digit3, 0x04}, + {PhysicalKey.Digit4, 0x05}, + {PhysicalKey.Digit5, 0x06}, + {PhysicalKey.Digit6, 0x07}, + {PhysicalKey.Digit7, 0x08}, + {PhysicalKey.Digit8, 0x09}, + {PhysicalKey.Digit9, 0x0A}, + {PhysicalKey.Digit0, 0x0B}, + {PhysicalKey.Minus, 0x0C}, + {PhysicalKey.Equal, 0x0D}, + {PhysicalKey.Backspace, 0x0E}, + {PhysicalKey.Tab, 0x0F}, + {PhysicalKey.Q, 0x10}, + {PhysicalKey.W, 0x11}, + {PhysicalKey.E, 0x12}, + {PhysicalKey.R, 0x13}, + {PhysicalKey.T, 0x14}, + {PhysicalKey.Y, 0x15}, + {PhysicalKey.U, 0x16}, + {PhysicalKey.I, 0x17}, + {PhysicalKey.O, 0x18}, + {PhysicalKey.P, 0x19}, + {PhysicalKey.BracketLeft, 0x1A}, + {PhysicalKey.BracketRight, 0x1B}, + {PhysicalKey.Enter, 0x1C}, + {PhysicalKey.ControlLeft, 0x1D}, + {PhysicalKey.A, 0x1E}, + {PhysicalKey.S, 0x1F}, + {PhysicalKey.D, 0x20}, + {PhysicalKey.F, 0x21}, + {PhysicalKey.G, 0x22}, + {PhysicalKey.H, 0x23}, + {PhysicalKey.J, 0x24}, + {PhysicalKey.K, 0x25}, + {PhysicalKey.L, 0x26}, + {PhysicalKey.Semicolon, 0x27}, + {PhysicalKey.Quote, 0x28}, + {PhysicalKey.ShiftLeft, 0x2A}, + {PhysicalKey.Backslash, 0x2B}, + {PhysicalKey.Z, 0x2C}, + {PhysicalKey.X, 0x2D}, + {PhysicalKey.C, 0x2E}, + {PhysicalKey.V, 0x2F}, + {PhysicalKey.B, 0x30}, + {PhysicalKey.N, 0x31}, + {PhysicalKey.M, 0x32}, + {PhysicalKey.Comma, 0x33}, + {PhysicalKey.Period, 0x34}, + {PhysicalKey.Slash, 0x35}, + {PhysicalKey.ShiftRight, 0x36}, + {PhysicalKey.PrintScreen, 0x37}, + {PhysicalKey.AltLeft, 0x38}, + {PhysicalKey.Space, 0x39}, + {PhysicalKey.CapsLock, 0x3A}, + {PhysicalKey.F1, 0x3B}, + {PhysicalKey.F2, 0x3C}, + {PhysicalKey.F3, 0x3D}, + {PhysicalKey.F4, 0x3E}, + {PhysicalKey.F5, 0x3F}, + {PhysicalKey.F6, 0x40}, + {PhysicalKey.F7, 0x41}, + {PhysicalKey.F8, 0x42}, + {PhysicalKey.F9, 0x43}, + {PhysicalKey.F10, 0x44}, + {PhysicalKey.NumLock, 0x45}, + {PhysicalKey.ScrollLock, 0x46}, + {PhysicalKey.Home, 0x47}, + {PhysicalKey.ArrowUp, 0x48}, + {PhysicalKey.PageUp, 0x49}, + {PhysicalKey.NumPadSubtract, 0x4A}, + {PhysicalKey.ArrowLeft, 0x4B}, + {PhysicalKey.NumPad5, 0x4C}, + {PhysicalKey.ArrowRight, 0x4D}, + {PhysicalKey.NumPadAdd, 0x4E}, + {PhysicalKey.End, 0x4F}, + {PhysicalKey.ArrowDown, 0x50}, + {PhysicalKey.PageDown, 0x51}, + {PhysicalKey.Insert, 0x52}, + {PhysicalKey.Delete, 0x53}, + {PhysicalKey.F11, 0x57}, + {PhysicalKey.F12, 0x58} }; + public static ushort GetScancode(PhysicalKey key) { if (KeyToScancodeMap.TryGetValue(key, out ushort scancode)) From 2438af5a5f405625a2dac074e02653d126b6c0a4 Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 10:48:17 -0400 Subject: [PATCH 07/21] CI:fmt --- ffi/src/pdu.rs | 6 ++---- ffi/src/utils/mod.rs | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ffi/src/pdu.rs b/ffi/src/pdu.rs index f672b7f6e..ac53df778 100644 --- a/ffi/src/pdu.rs +++ b/ffi/src/pdu.rs @@ -64,12 +64,10 @@ pub mod ffi { #[diplomat::opaque] pub struct FastPathInputEventIterator(pub Vec); - - } -impl From> for ffi::FastPathInputEventIterator{ +impl From> for ffi::FastPathInputEventIterator { fn from(value: Vec) -> Self { ffi::FastPathInputEventIterator(value) } -} \ No newline at end of file +} diff --git a/ffi/src/utils/mod.rs b/ffi/src/utils/mod.rs index bcdb1321e..672c5653e 100644 --- a/ffi/src/utils/mod.rs +++ b/ffi/src/utils/mod.rs @@ -79,5 +79,4 @@ pub mod ffi { self.0.ok_or_else(|| "value is None".into()) } } - } From 17e624b93556caf11c92e7e6b753a4779c341495 Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 11:04:33 -0400 Subject: [PATCH 08/21] House keeping --- crates/ironrdp-client/src/gui.rs | 1 + .../.gitignore | 16 ++++++++ .../KeyCodeMapper.cs | 5 ++- .../MainWindow.axaml.cs | 40 +++++++------------ 4 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/.gitignore diff --git a/crates/ironrdp-client/src/gui.rs b/crates/ironrdp-client/src/gui.rs index c9d227295..f8e0f5f51 100644 --- a/crates/ironrdp-client/src/gui.rs +++ b/crates/ironrdp-client/src/gui.rs @@ -141,6 +141,7 @@ impl GuiContext { x: (position.x / sf) as u16, y: (position.y / sf) as u16, }); + let input_events = input_database.apply(std::iter::once(operation)); send_fast_path_events(&input_event_sender, input_events); diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/.gitignore b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/.gitignore new file mode 100644 index 000000000..a374dd6be --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/.gitignore @@ -0,0 +1,16 @@ +# Ignore directories +bin/ +obj/ + +# Ignore files +*.user +*.userosscache +*.suo +*.userprefs +*.dll +*.exe +*.pdb +*.cache +*.vsp +*.vspx +*.sap diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs index 2fe906880..b94824579 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/KeyCodeMapper.cs @@ -1,4 +1,5 @@ using Avalonia.Input; +using System; using System.Collections.Generic; public static class KeyCodeMapper @@ -92,12 +93,12 @@ public static class KeyCodeMapper }; - public static ushort GetScancode(PhysicalKey key) + public static ushort? GetScancode(PhysicalKey key) { if (KeyToScancodeMap.TryGetValue(key, out ushort scancode)) { return scancode; } - throw new KeyNotFoundException($"Key {key} not found in the map"); + return null; } } diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index be87a59d1..92c5787b9 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -29,8 +29,6 @@ public MainWindow() private void OnOpened(object? sender, EventArgs e) { - Console.WriteLine("OnOpened"); - var username = "Administrator"; var password = "DevoLabs123!"; var domain = "ad.it-help.ninja"; @@ -64,37 +62,33 @@ private void OnOpened(object? sender, EventArgs e) }); } - private void WriteDecodedImageToCanvas() + private async void WriteDecodedImageToCanvas() { - Dispatcher.UIThread.InvokeAsync(() => + await Dispatcher.UIThread.InvokeAsync(() => { var data = decodedImage!.GetData(); - var buffer_size = (int)data.GetSize(); - var buffer = new byte[buffer_size]; + var bufferSize = (int)data.GetSize(); + + var buffer = new byte[bufferSize]; data.Fill(buffer); using (var bitmap = this.bitmap!.Lock()) { unsafe { - fixed (byte* p = buffer) - { - var src = (uint*)p; - var dst = (uint*)bitmap.Address; - for (var i = 0; i < buffer_size / 4; i++) - { - dst[i] = src[i]; - } - } + var bitmapSpan = new Span((void*)bitmap.Address, bufferSize); + var bufferSpan = new Span(buffer); + bufferSpan.CopyTo(bitmapSpan); } } - // Assuming `image` is the Image control that needs to be updated. - image!.InvalidateVisual(); // Force redraw of image + image!.InvalidateVisual(); }); } + + private void ReadPduAndProcessActiveStage() { Task.Run(async () => @@ -102,7 +96,6 @@ private void ReadPduAndProcessActiveStage() var keepLooping = true; while (keepLooping) { - Console.WriteLine("Reading PDU , updateCounter = " + updateCounter); var readPduTask = await framed!.ReadPdu(); Action action = readPduTask.Item1; byte[] payload = readPduTask.Item2; @@ -202,9 +195,9 @@ private void Canvas_KeyDown(object? sender, KeyEventArgs? e) { return; } - Console.WriteLine($"Key pressed: {e!.Key}"); - PhysicalKey physicalKey = e.PhysicalKey; - var keyOperation = Scancode.FromU16(KeyCodeMapper.GetScancode(physicalKey)).AsOperationKeyPressed(); + PhysicalKey physicalKey = e!.PhysicalKey; + + var keyOperation = Scancode.FromU16((ushort)KeyCodeMapper.GetScancode(physicalKey)!).AsOperationKeyPressed(); var fastpath = inputDatabase!.Apply(keyOperation); var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); var _ = HandleActiveStageOutput(output); @@ -216,15 +209,13 @@ private void Canvas_KeyUp(object? sender, KeyEventArgs? e) { return; } - Console.WriteLine($"Key released: {e!.Key}"); - Key key = e.Key; + Key key = e!.Key; var keyOperation = Scancode.FromU16((ushort)key).AsOperationKeyReleased(); var fastpath = inputDatabase!.Apply(keyOperation); var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); var _ = HandleActiveStageOutput(output); } - private ulong updateCounter = 0; private async Task HandleActiveStageOutput(ActiveStageOutputIterator outputIterator) { try @@ -233,7 +224,6 @@ private async Task HandleActiveStageOutput(ActiveStageOutputIterator outpu while (!outputIterator.IsEmpty()) { var output = outputIterator.Next()!; // outputIterator.Next() is not null since outputIterator.IsEmpty() is false - Console.WriteLine("Handling output = " + updateCounter++, "output type " + output.GetEnumType()); if (output.GetEnumType() == ActiveStageOutputType.Terminate) { return false; From 825d9dc68f792edb4532e38c252d8ec81d438c15 Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 11:22:11 -0400 Subject: [PATCH 09/21] House keeping 2 --- .../Generated/ClientConnectorState.cs | 30 +++++++++++++++++++ .../Generated/ConfigBuilder.cs | 26 ++++++++++++++++ .../Generated/RawClientConnectorState.cs | 3 ++ .../Generated/RawConfigBuilder.cs | 3 ++ ffi/src/connector/config.rs | 14 +++++---- ffi/src/connector/state.rs | 20 +++++++++++-- 6 files changed, 88 insertions(+), 8 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs index efabee588..ba2bb0301 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ClientConnectorState.cs @@ -39,6 +39,14 @@ public ConnectionResult ConnectedResult } } + public ConnectionActivationSequence ConnectionFinalizationResult + { + get + { + return GetConnectionFinalizationResult(); + } + } + public SecurityProtocol ConnectionInitiationWaitConfirmRequestedProtocol { get @@ -239,6 +247,28 @@ public ConnectionResult GetConnectedResult() } } + /// + /// + /// A ConnectionActivationSequence allocated on Rust side. + /// + public ConnectionActivationSequence GetConnectionFinalizationResult() + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("ClientConnectorState"); + } + Raw.ConnectorStateFfiResultBoxConnectionActivationSequenceBoxIronRdpError result = Raw.ClientConnectorState.GetConnectionFinalizationResult(_inner); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.ConnectionActivationSequence* retVal = result.Ok; + return new ConnectionActivationSequence(retVal); + } + } + /// /// Returns the underlying raw handle. /// diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs index f821ebfb3..be986f02a 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs @@ -23,6 +23,14 @@ public bool Autologon } } + public BitmapConfig Bitmap + { + set + { + SetBitmap(value); + } + } + public uint ClientBuild { set @@ -326,6 +334,24 @@ public void SetPerformanceFlags(PerformanceFlags performanceFlags) } } + public void SetBitmap(BitmapConfig bitmap) + { + unsafe + { + if (_inner == null) + { + throw new ObjectDisposedException("ConfigBuilder"); + } + Raw.BitmapConfig* bitmapRaw; + bitmapRaw = bitmap.AsFFI(); + if (bitmapRaw == null) + { + throw new ObjectDisposedException("BitmapConfig"); + } + Raw.ConfigBuilder.SetBitmap(_inner, bitmapRaw); + } + } + public void SetClientBuild(uint clientBuild) { unsafe diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs index 54b7f5a5f..57c4abe0d 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawClientConnectorState.cs @@ -37,6 +37,9 @@ public partial struct ClientConnectorState [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ClientConnectorState_get_connected_result", ExactSpelling = true)] public static unsafe extern ConnectorStateFfiResultBoxConnectionResultBoxIronRdpError GetConnectedResult(ClientConnectorState* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ClientConnectorState_get_connection_finalization_result", ExactSpelling = true)] + public static unsafe extern ConnectorStateFfiResultBoxConnectionActivationSequenceBoxIronRdpError GetConnectionFinalizationResult(ClientConnectorState* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ClientConnectorState_destroy", ExactSpelling = true)] public static unsafe extern void Destroy(ClientConnectorState* self); } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs index 08a19c4ef..12c7cbfc5 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs @@ -52,6 +52,9 @@ public partial struct ConfigBuilder [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConfigBuilder_set_performance_flags", ExactSpelling = true)] public static unsafe extern void SetPerformanceFlags(ConfigBuilder* self, PerformanceFlags* performanceFlags); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConfigBuilder_set_bitmap", ExactSpelling = true)] + public static unsafe extern void SetBitmap(ConfigBuilder* self, BitmapConfig* bitmap); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConfigBuilder_set_client_build", ExactSpelling = true)] public static unsafe extern void SetClientBuild(ConfigBuilder* self, uint clientBuild); diff --git a/ffi/src/connector/config.rs b/ffi/src/connector/config.rs index d150cdfea..b91841682 100644 --- a/ffi/src/connector/config.rs +++ b/ffi/src/connector/config.rs @@ -4,10 +4,7 @@ use self::ffi::PerformanceFlagsType; #[diplomat::bridge] pub mod ffi { - use ironrdp::{ - connector::{BitmapConfig, Credentials}, - pdu::rdp::capability_sets::MajorPlatformType, - }; + use ironrdp::{connector::Credentials, pdu::rdp::capability_sets::MajorPlatformType}; use crate::error::ffi::IronRdpError; @@ -33,7 +30,7 @@ pub mod ffi { pub ime_file_name: Option, pub dig_product_id: Option, pub desktop_size: Option, - pub bitmap: Option, + pub bitmap: Option, pub client_build: Option, pub client_name: Option, pub client_dir: Option, @@ -120,7 +117,9 @@ pub mod ffi { self.performance_flags = Some(performance_flags.0); } - // TODO: set bitmap + pub fn set_bitmap(&mut self, bitmap: &BitmapConfig) { + self.bitmap = Some(bitmap.0.clone()); + } pub fn set_client_build(&mut self, client_build: u32) { self.client_build = Some(client_build); @@ -227,6 +226,9 @@ pub mod ffi { self.0.insert(flag.into()); } } + + #[diplomat::opaque] + pub struct BitmapConfig(pub ironrdp::connector::BitmapConfig); } impl From for PerformanceFlags { diff --git a/ffi/src/connector/state.rs b/ffi/src/connector/state.rs index 63b11e40a..4cb44cdbb 100644 --- a/ffi/src/connector/state.rs +++ b/ffi/src/connector/state.rs @@ -166,8 +166,6 @@ pub mod ffi { .map(Box::new) } - // TODO: Add more getters for other states - pub fn get_connected_result( &mut self, ) -> Result, Box> { @@ -184,5 +182,23 @@ pub mod ffi { .into()), } } + + pub fn get_connection_finalization_result( + &mut self, + ) -> Result, Box> { + match self + .0 + .take() + .ok_or_else(|| ValueConsumedError::for_item("ClientConnectorState"))? + { + ironrdp::connector::ClientConnectorState::ConnectionFinalization { connection_activation } => Ok( + crate::connector::ffi::ConnectionActivationSequence(Box::new(connection_activation.clone())), + ), + _ => Err(IncorrectEnumTypeError::on_variant("ConnectionFinalization") + .of_enum("ClientConnectorState") + .into()), + } + .map(Box::new) + } } } From 25ccfef49831b016a8f3a5a9b0b43c86cae28c8b Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 11:59:33 -0400 Subject: [PATCH 10/21] Add a few more functions --- .../Generated/BitmapConfig.cs | 63 +++++++++++++++++++ .../Generated/RawBitmapConfig.cs | 21 +++++++ ...ectionActivationSequenceBoxIronRdpError.cs | 46 ++++++++++++++ 3 files changed, 130 insertions(+) create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/BitmapConfig.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawBitmapConfig.cs create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawConnectorStateFfiResultBoxConnectionActivationSequenceBoxIronRdpError.cs diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/BitmapConfig.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/BitmapConfig.cs new file mode 100644 index 000000000..e3d932525 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/BitmapConfig.cs @@ -0,0 +1,63 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp; + +#nullable enable + +public partial class BitmapConfig: IDisposable +{ + private unsafe Raw.BitmapConfig* _inner; + + /// + /// Creates a managed BitmapConfig from a raw handle. + /// + /// + /// Safety: you should not build two managed objects using the same raw handle (may causes use-after-free and double-free). + ///
+ /// This constructor assumes the raw struct is allocated on Rust side. + /// If implemented, the custom Drop implementation on Rust side WILL run on destruction. + ///
+ public unsafe BitmapConfig(Raw.BitmapConfig* handle) + { + _inner = handle; + } + + /// + /// Returns the underlying raw handle. + /// + public unsafe Raw.BitmapConfig* AsFFI() + { + return _inner; + } + + /// + /// Destroys the underlying object immediately. + /// + public void Dispose() + { + unsafe + { + if (_inner == null) + { + return; + } + + Raw.BitmapConfig.Destroy(_inner); + _inner = null; + + GC.SuppressFinalize(this); + } + } + + ~BitmapConfig() + { + Dispose(); + } +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawBitmapConfig.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawBitmapConfig.cs new file mode 100644 index 000000000..0cfede60a --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawBitmapConfig.cs @@ -0,0 +1,21 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct BitmapConfig +{ + private const string NativeLib = "DevolutionsIronRdp"; + + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "BitmapConfig_destroy", ExactSpelling = true)] + public static unsafe extern void Destroy(BitmapConfig* self); +} diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawConnectorStateFfiResultBoxConnectionActivationSequenceBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawConnectorStateFfiResultBoxConnectionActivationSequenceBoxIronRdpError.cs new file mode 100644 index 000000000..0523d083f --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawConnectorStateFfiResultBoxConnectionActivationSequenceBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct ConnectorStateFfiResultBoxConnectionActivationSequenceBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal ConnectionActivationSequence* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe ConnectionActivationSequence* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} From 905a69ab9c97a9fe786681f38c3deb5aa6892b55 Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 12:12:51 -0400 Subject: [PATCH 11/21] house keeping 3 --- .../MainWindow.axaml.cs | 19 +++++++++++++++---- .../Program.cs | 15 ++++++++++++++- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index 92c5787b9..c6e8c80c9 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -5,6 +5,7 @@ using Avalonia.Platform; using Avalonia.Threading; using System; +using System.Diagnostics; using System.Net.Security; using System.Threading.Tasks; @@ -29,10 +30,20 @@ public MainWindow() private void OnOpened(object? sender, EventArgs e) { - var username = "Administrator"; - var password = "DevoLabs123!"; - var domain = "ad.it-help.ninja"; - var server = "IT-HELP-DC.ad.it-help.ninja"; + + + var username = Environment.GetEnvironmentVariable("IRONRDP_USERNAME"); + var password = Environment.GetEnvironmentVariable("IRONRDP_PASSWORD"); + var domain = Environment.GetEnvironmentVariable("IRONRDP_DOMAIN"); + var server = Environment.GetEnvironmentVariable("IRONRDP_SERVER"); + + if (username == null || password == null || domain == null || server == null) + { + Trace.TraceError("Please set the IRONRDP_USERNAME, IRONRDP_PASSWORD, IRONRDP_DOMAIN, and IRONRDP_SERVER environment variables"); + Close(); + return; + } + var width = 1280; var height = 800; diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs index 79c6f23da..c2c121ea0 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs @@ -1,5 +1,6 @@ using Avalonia; using System; +using System.Diagnostics; namespace Devolutions.IronRdp.AvaloniaExample; @@ -9,8 +10,12 @@ class Program // SynchronizationContext-reliant code before AppMain is called: things aren't initialized // yet and stuff might break. [STAThread] - public static void Main(string[] args) => BuildAvaloniaApp() + public static void Main(string[] args) + { + InitializeLogging(); + BuildAvaloniaApp() .StartWithClassicDesktopLifetime(args); + } // Avalonia configuration, don't remove; also used by visual designer. public static AppBuilder BuildAvaloniaApp() @@ -18,4 +23,12 @@ public static AppBuilder BuildAvaloniaApp() .UsePlatformDetect() .WithInterFont() .LogToTrace(); + + public static void InitializeLogging() + { + Trace.AutoFlush = true; + TextWriterTraceListener myListener = new TextWriterTraceListener(System.IO.File.CreateText("AvaloniaExample.log")); + Trace.Listeners.Add(myListener); + } + } From 9d7e8bc684df2a546d179066edb37dac728a1368 Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 12:14:56 -0400 Subject: [PATCH 12/21] CI:lints --- ffi/src/connector/config.rs | 2 +- ffi/src/connector/state.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ffi/src/connector/config.rs b/ffi/src/connector/config.rs index b91841682..7d9219ef3 100644 --- a/ffi/src/connector/config.rs +++ b/ffi/src/connector/config.rs @@ -118,7 +118,7 @@ pub mod ffi { } pub fn set_bitmap(&mut self, bitmap: &BitmapConfig) { - self.bitmap = Some(bitmap.0.clone()); + self.bitmap = Some(bitmap.0); } pub fn set_client_build(&mut self, client_build: u32) { diff --git a/ffi/src/connector/state.rs b/ffi/src/connector/state.rs index 4cb44cdbb..78f59b094 100644 --- a/ffi/src/connector/state.rs +++ b/ffi/src/connector/state.rs @@ -192,7 +192,7 @@ pub mod ffi { .ok_or_else(|| ValueConsumedError::for_item("ClientConnectorState"))? { ironrdp::connector::ClientConnectorState::ConnectionFinalization { connection_activation } => Ok( - crate::connector::ffi::ConnectionActivationSequence(Box::new(connection_activation.clone())), + crate::connector::ffi::ConnectionActivationSequence(Box::new(connection_activation)), ), _ => Err(IncorrectEnumTypeError::on_variant("ConnectionFinalization") .of_enum("ClientConnectorState") From ddfc09a92faf1eb5c3e8166336cc7c21ec6470a5 Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 12:18:55 -0400 Subject: [PATCH 13/21] catch global error --- ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs index c2c121ea0..1d0ce255d 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/Program.cs @@ -13,8 +13,12 @@ class Program public static void Main(string[] args) { InitializeLogging(); - BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + try{ + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + }catch(Exception e){ + Trace.TraceError(e.Message); + } } // Avalonia configuration, don't remove; also used by visual designer. From 3bacce0ed8bb0b20040b99eb895f6e4a7fb8617e Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 12:27:36 -0400 Subject: [PATCH 14/21] catch more error --- .../Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index c6e8c80c9..cf53d1436 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -63,6 +63,9 @@ private void OnOpened(object? sender, EventArgs e) { if (t.IsFaulted) { + Exception e = t.Exception!; + Trace.TraceError("Error connecting to server: " + e.Message); + Close(); return; } var (res, framed) = t.Result; From 32ab511847311d8e809398779e7f381f3f331bcf Mon Sep 17 00:00:00 2001 From: irving ou Date: Mon, 22 Apr 2024 12:33:56 -0400 Subject: [PATCH 15/21] full screen on boot --- .../Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index cf53d1436..068e31ac4 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -30,7 +30,7 @@ public MainWindow() private void OnOpened(object? sender, EventArgs e) { - + WindowState = WindowState.Maximized; var username = Environment.GetEnvironmentVariable("IRONRDP_USERNAME"); var password = Environment.GetEnvironmentVariable("IRONRDP_PASSWORD"); From bdc64ed2a920049b4bff1eadf3923aabeecbc64f Mon Sep 17 00:00:00 2001 From: "irvingouj @ Devolutions" <139169536+irvingoujAtDevolution@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:54:32 -0400 Subject: [PATCH 16/21] Update ffi/src/connector/config.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benoît Cortier --- ffi/src/connector/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/src/connector/config.rs b/ffi/src/connector/config.rs index 7d9219ef3..c3804b279 100644 --- a/ffi/src/connector/config.rs +++ b/ffi/src/connector/config.rs @@ -117,7 +117,7 @@ pub mod ffi { self.performance_flags = Some(performance_flags.0); } - pub fn set_bitmap(&mut self, bitmap: &BitmapConfig) { + pub fn set_bitmap_config(&mut self, bitmap: &BitmapConfig) { self.bitmap = Some(bitmap.0); } From 7e10d5100bcf7cf2227eae858c24430dfdfc0bb3 Mon Sep 17 00:00:00 2001 From: "irvingouj @ Devolutions" <139169536+irvingoujAtDevolution@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:54:39 -0400 Subject: [PATCH 17/21] Update ffi/src/input.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Benoît Cortier --- ffi/src/input.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ffi/src/input.rs b/ffi/src/input.rs index b8b3d7f45..a7ac6c75a 100644 --- a/ffi/src/input.rs +++ b/ffi/src/input.rs @@ -38,7 +38,7 @@ pub mod ffi { Box::new(MousePosition(ironrdp::input::MousePosition { x, y })) } - pub fn as_operation(&self) -> Box { + pub fn as_move_operation(&self) -> Box { Box::new(Operation(ironrdp::input::Operation::MouseMove(self.0))) } } From ba33677e33b3c8aed55df61bb3e3439f581b1658 Mon Sep 17 00:00:00 2001 From: irving ou Date: Tue, 23 Apr 2024 11:09:10 -0400 Subject: [PATCH 18/21] review fix --- ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs | 8 ++++---- ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs | 4 ++-- .../Devolutions.IronRdp/Generated/RawConfigBuilder.cs | 4 ++-- .../Devolutions.IronRdp/Generated/RawMousePosition.cs | 4 ++-- ffi/dotnet/Devolutions.IronRdp/src/Framed.cs | 8 +------- 5 files changed, 11 insertions(+), 17 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs index be986f02a..92a20977e 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/ConfigBuilder.cs @@ -23,11 +23,11 @@ public bool Autologon } } - public BitmapConfig Bitmap + public BitmapConfig BitmapConfig { set { - SetBitmap(value); + SetBitmapConfig(value); } } @@ -334,7 +334,7 @@ public void SetPerformanceFlags(PerformanceFlags performanceFlags) } } - public void SetBitmap(BitmapConfig bitmap) + public void SetBitmapConfig(BitmapConfig bitmap) { unsafe { @@ -348,7 +348,7 @@ public void SetBitmap(BitmapConfig bitmap) { throw new ObjectDisposedException("BitmapConfig"); } - Raw.ConfigBuilder.SetBitmap(_inner, bitmapRaw); + Raw.ConfigBuilder.SetBitmapConfig(_inner, bitmapRaw); } } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs index 6e80279af..daf15e364 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/MousePosition.cs @@ -44,7 +44,7 @@ public static MousePosition New(ushort x, ushort y) /// /// A Operation allocated on Rust side. /// - public Operation AsOperation() + public Operation AsMoveOperation() { unsafe { @@ -52,7 +52,7 @@ public Operation AsOperation() { throw new ObjectDisposedException("MousePosition"); } - Raw.Operation* retVal = Raw.MousePosition.AsOperation(_inner); + Raw.Operation* retVal = Raw.MousePosition.AsMoveOperation(_inner); return new Operation(retVal); } } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs index 12c7cbfc5..e369260dd 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawConfigBuilder.cs @@ -52,8 +52,8 @@ public partial struct ConfigBuilder [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConfigBuilder_set_performance_flags", ExactSpelling = true)] public static unsafe extern void SetPerformanceFlags(ConfigBuilder* self, PerformanceFlags* performanceFlags); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConfigBuilder_set_bitmap", ExactSpelling = true)] - public static unsafe extern void SetBitmap(ConfigBuilder* self, BitmapConfig* bitmap); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConfigBuilder_set_bitmap_config", ExactSpelling = true)] + public static unsafe extern void SetBitmapConfig(ConfigBuilder* self, BitmapConfig* bitmap); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConfigBuilder_set_client_build", ExactSpelling = true)] public static unsafe extern void SetClientBuild(ConfigBuilder* self, uint clientBuild); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs index cc3e74ef9..dcd70e332 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawMousePosition.cs @@ -19,8 +19,8 @@ public partial struct MousePosition [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MousePosition_new", ExactSpelling = true)] public static unsafe extern MousePosition* New(ushort x, ushort y); - [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MousePosition_as_operation", ExactSpelling = true)] - public static unsafe extern Operation* AsOperation(MousePosition* self); + [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MousePosition_as_move_operation", ExactSpelling = true)] + public static unsafe extern Operation* AsMoveOperation(MousePosition* self); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "MousePosition_destroy", ExactSpelling = true)] public static unsafe extern void Destroy(MousePosition* self); diff --git a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs index 029a99dc1..d284790d8 100644 --- a/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs +++ b/ffi/dotnet/Devolutions.IronRdp/src/Framed.cs @@ -23,11 +23,9 @@ public Framed(S stream) { while (true) { - Console.WriteLine("ReadPdu, buffer size: " + this.buffer.Count); var pduInfo = IronRdpPdu.New().FindSize(this.buffer.ToArray()); if (null != pduInfo) { - Console.WriteLine("ReadPdu: ReadExact, pduInfo.GetLength()=" + pduInfo.GetLength()); var frame = await this.ReadExact(pduInfo.GetLength()); var action = pduInfo.GetAction(); return (action, frame); @@ -35,8 +33,6 @@ public Framed(S stream) else { var len = await this.Read(); - Console.WriteLine("ReadPdu: Read, len=" + len); - if (len == 0) { throw new IronRdpLibException(IronRdpLibExceptionType.EndOfFile, "EOF on ReadPdu"); @@ -66,14 +62,12 @@ public async Task ReadExact(nuint size) { if (buffer.Count >= (int)size) { - Console.WriteLine("ReadExact: Take, size=" + size); var res = this.buffer.Take((int)size).ToArray(); this.buffer = this.buffer.Skip((int)size).ToList(); return res; } var len = await this.Read(); - Console.WriteLine("ReadExact: Read, len = " + len); if (len == 0) { throw new Exception("EOF"); @@ -100,7 +94,7 @@ public async Task Write(byte[] data) } catch (Exception e) { - Console.WriteLine("Write error: " + e.Message); + throw e; } finally { From fc65f3d444234fd4270cab90da91fc5421996856 Mon Sep 17 00:00:00 2001 From: irving ou Date: Tue, 23 Apr 2024 11:11:19 -0400 Subject: [PATCH 19/21] review fix 2 --- .../Devolutions.IronRdp.AvaloniaExample/App.axaml.cs | 1 - .../MainWindow.axaml.cs | 8 -------- 2 files changed, 9 deletions(-) diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs index 1396a66aa..23f0f0dc1 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/App.axaml.cs @@ -9,7 +9,6 @@ public partial class App : Application { public override void Initialize() { - Console.WriteLine("App Initialize"); AvaloniaXamlLoader.Load(this); } diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index 068e31ac4..4c18ea02c 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -135,7 +135,6 @@ private static Config buildConfig(string username, string password, string domai private void Canvas_OnPointerPressed(object sender, Avalonia.Input.PointerPressedEventArgs e) { - Console.WriteLine("Mouse pressed"); PointerUpdateKind mouseButton = e.GetCurrentPoint((Visual?)sender).Properties.PointerUpdateKind; MouseButtonType buttonType = mouseButton switch @@ -172,13 +171,11 @@ private void Canvas_PointerMoved(object sender, PointerEventArgs e) var mouseMovedEvent = MousePosition.New(x, y).AsOperation(); var fastpath = inputDatabase!.Apply(mouseMovedEvent); var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); - Console.WriteLine($"Pointer moved to X: {x}, Y: {y}"); var _ = HandleActiveStageOutput(output); } private void Canvas_PointerReleased(object sender, PointerReleasedEventArgs e) { - Console.WriteLine("Mouse released"); PointerUpdateKind mouseButton = e.GetCurrentPoint((Visual?)sender).Properties.PointerUpdateKind; MouseButtonType buttonType = mouseButton switch @@ -264,16 +261,11 @@ private async Task HandleActiveStageOutput(ActiveStageOutputIterator outpu { WriteDecodedImageToCanvas(); } - else - { - Console.WriteLine("Unhandled output type " + output.GetEnumType()); - } } return true; } catch (Exception e) { - Console.WriteLine("Error handling output: " + e.Message); return false; } } From a0e652fcdc3c50b61edbfa50db53e0a3e045bf5d Mon Sep 17 00:00:00 2001 From: irving ou Date: Tue, 23 Apr 2024 11:17:42 -0400 Subject: [PATCH 20/21] review fix 3 --- .../Devolutions.IronRdp/Generated/Char.cs | 8 +++- .../Devolutions.IronRdp/Generated/RawChar.cs | 2 +- ...RawInputFfiResultBoxCharBoxIronRdpError.cs | 46 +++++++++++++++++++ ffi/src/input.rs | 8 ++-- 4 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs index af00c2c40..41b58453a 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs @@ -29,6 +29,7 @@ public unsafe Char(Raw.Char* handle) _inner = handle; } + /// /// /// A Char allocated on Rust side. /// @@ -36,7 +37,12 @@ public static Char New(uint c) { unsafe { - Raw.Char* retVal = Raw.Char.New(c); + Raw.InputFfiResultBoxCharBoxIronRdpError result = Raw.Char.New(c); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.Char* retVal = result.Ok; return new Char(retVal); } } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs index 8bef300cc..6e12c8b00 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs @@ -17,7 +17,7 @@ public partial struct Char private const string NativeLib = "DevolutionsIronRdp"; [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_new", ExactSpelling = true)] - public static unsafe extern Char* New(uint c); + public static unsafe extern InputFfiResultBoxCharBoxIronRdpError New(uint c); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_as_operation_unicode_key_pressed", ExactSpelling = true)] public static unsafe extern Operation* AsOperationUnicodeKeyPressed(Char* self); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs new file mode 100644 index 000000000..1e721b374 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct InputFfiResultBoxCharBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal Char* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe Char* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/src/input.rs b/ffi/src/input.rs index a7ac6c75a..fcdd61684 100644 --- a/ffi/src/input.rs +++ b/ffi/src/input.rs @@ -1,6 +1,6 @@ #[diplomat::bridge] pub mod ffi { - use crate::pdu::ffi::FastPathInputEventIterator; + use crate::{error::ffi::IronRdpError, pdu::ffi::FastPathInputEventIterator}; #[diplomat::opaque] pub struct InputDatabase(pub ironrdp::input::Database); @@ -114,8 +114,10 @@ pub mod ffi { pub struct Char(pub char); impl Char { - pub fn new(c: char) -> Box { - Box::new(Char(c)) + pub fn new(c: u32) -> Result, Box> { + char::from_u32(c) + .map(|c| Box::new(Char(c))) + .ok_or_else(|| "Invalid unicode character".into()) } pub fn as_operation_unicode_key_pressed(&self) -> Box { From f97e8b93c4b49e5a720626cf2ca90132f834d743 Mon Sep 17 00:00:00 2001 From: irving ou Date: Tue, 23 Apr 2024 11:19:25 -0400 Subject: [PATCH 21/21] review fix 3 --- .../MainWindow.axaml.cs | 2 +- .../Devolutions.IronRdp/Generated/Char.cs | 8 +++- .../Devolutions.IronRdp/Generated/RawChar.cs | 2 +- ...RawInputFfiResultBoxCharBoxIronRdpError.cs | 46 +++++++++++++++++++ ffi/src/input.rs | 8 ++-- 5 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs diff --git a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs index 4c18ea02c..75720d1e0 100644 --- a/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs +++ b/ffi/dotnet/Devolutions.IronRdp.AvaloniaExample/MainWindow.axaml.cs @@ -168,7 +168,7 @@ private void Canvas_PointerMoved(object sender, PointerEventArgs e) var position = e.GetPosition((Visual?)sender); var x = (ushort)position.X; var y = (ushort)position.Y; - var mouseMovedEvent = MousePosition.New(x, y).AsOperation(); + var mouseMovedEvent = MousePosition.New(x, y).AsMoveOperation(); var fastpath = inputDatabase!.Apply(mouseMovedEvent); var output = activeStage.ProcessFastpathInput(decodedImage, fastpath); var _ = HandleActiveStageOutput(output); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs index af00c2c40..41b58453a 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/Char.cs @@ -29,6 +29,7 @@ public unsafe Char(Raw.Char* handle) _inner = handle; } + /// /// /// A Char allocated on Rust side. /// @@ -36,7 +37,12 @@ public static Char New(uint c) { unsafe { - Raw.Char* retVal = Raw.Char.New(c); + Raw.InputFfiResultBoxCharBoxIronRdpError result = Raw.Char.New(c); + if (!result.isOk) + { + throw new IronRdpException(new IronRdpError(result.Err)); + } + Raw.Char* retVal = result.Ok; return new Char(retVal); } } diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs index 8bef300cc..6e12c8b00 100644 --- a/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawChar.cs @@ -17,7 +17,7 @@ public partial struct Char private const string NativeLib = "DevolutionsIronRdp"; [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_new", ExactSpelling = true)] - public static unsafe extern Char* New(uint c); + public static unsafe extern InputFfiResultBoxCharBoxIronRdpError New(uint c); [DllImport(NativeLib, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Char_as_operation_unicode_key_pressed", ExactSpelling = true)] public static unsafe extern Operation* AsOperationUnicodeKeyPressed(Char* self); diff --git a/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs b/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs new file mode 100644 index 000000000..1e721b374 --- /dev/null +++ b/ffi/dotnet/Devolutions.IronRdp/Generated/RawInputFfiResultBoxCharBoxIronRdpError.cs @@ -0,0 +1,46 @@ +// by Diplomat + +#pragma warning disable 0105 +using System; +using System.Runtime.InteropServices; + +using Devolutions.IronRdp.Diplomat; +#pragma warning restore 0105 + +namespace Devolutions.IronRdp.Raw; + +#nullable enable + +[StructLayout(LayoutKind.Sequential)] +public partial struct InputFfiResultBoxCharBoxIronRdpError +{ + [StructLayout(LayoutKind.Explicit)] + private unsafe struct InnerUnion + { + [FieldOffset(0)] + internal Char* ok; + [FieldOffset(0)] + internal IronRdpError* err; + } + + private InnerUnion _inner; + + [MarshalAs(UnmanagedType.U1)] + public bool isOk; + + public unsafe Char* Ok + { + get + { + return _inner.ok; + } + } + + public unsafe IronRdpError* Err + { + get + { + return _inner.err; + } + } +} diff --git a/ffi/src/input.rs b/ffi/src/input.rs index a7ac6c75a..fcdd61684 100644 --- a/ffi/src/input.rs +++ b/ffi/src/input.rs @@ -1,6 +1,6 @@ #[diplomat::bridge] pub mod ffi { - use crate::pdu::ffi::FastPathInputEventIterator; + use crate::{error::ffi::IronRdpError, pdu::ffi::FastPathInputEventIterator}; #[diplomat::opaque] pub struct InputDatabase(pub ironrdp::input::Database); @@ -114,8 +114,10 @@ pub mod ffi { pub struct Char(pub char); impl Char { - pub fn new(c: char) -> Box { - Box::new(Char(c)) + pub fn new(c: u32) -> Result, Box> { + char::from_u32(c) + .map(|c| Box::new(Char(c))) + .ok_or_else(|| "Invalid unicode character".into()) } pub fn as_operation_unicode_key_pressed(&self) -> Box {