Feature/rdp demo - #3
Merged
Merged
Conversation
Demonstrates basic RdpBridge integration: connection form, framebuffer display via WriteableBitmap, mouse/keyboard forwarding, state/status callbacks, and fit/1:1 scale toggle. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
freerdp_check_fds() returns immediately without blocking, causing the worker thread to busy-spin at 100% CPU even when the session is idle. Replace with the correct pattern: - freerdp_get_event_handles() to retrieve OS wait handles - WaitForMultipleObjects() on Windows (100ms timeout) to block until an event arrives or the disconnect signal is set - select() on POSIX with a 100ms timeval for the same effect - freerdp_check_event_handles() to dispatch the received events Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Introduces a new cross-platform Avalonia demo application (dotnet/RdpDemo) to exercise RdpBridge via a GUI, and refactors the native bridge’s connection event loop to wait on OS events instead of busy polling.
Changes:
- Added the
RdpDemoAvalonia app (UI + view model) and included it in the .NET solution. - Implemented input forwarding (pointer/keyboard) and framebuffer rendering via
IRdpFrameReceiver. - Updated native
rdp_bridge.cppto block on OS event handles (select()/WaitForMultipleObjects) before dispatching FreeRDP events.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/RdpBridge/rdp_bridge.cpp | Refactors connection thread loop to wait on OS-level event handles before calling freerdp_check_event_handles(). |
| dotnet/RdpDemo/Views/RdpView.axaml.cs | Adds pointer + keyboard event handling and forwards input to RdpBridgeClient. |
| dotnet/RdpDemo/Views/RdpView.axaml | Defines the framebuffer image surface and status bar UI. |
| dotnet/RdpDemo/Views/MainWindow.axaml.cs | Sets up the main window and assigns the view model as DataContext. |
| dotnet/RdpDemo/Views/MainWindow.axaml | Provides connection controls and hosts the RDP view. |
| dotnet/RdpDemo/ViewModels/MainWindowViewModel.cs | Implements connect/disconnect, receives frames, and forwards user input to the bridge. |
| dotnet/RdpDemo/RdpDemo.csproj | New project file with Avalonia + MVVM toolkit dependencies and native runtime asset copying. |
| dotnet/RdpDemo/Program.cs | Avalonia app entry point / app builder bootstrap. |
| dotnet/RdpDemo/App.axaml.cs | Avalonia Application initialization and main window creation. |
| dotnet/RdpDemo/App.axaml | Registers Fluent theme styling. |
| dotnet/RdpBridge.slnx | Adds RdpDemo project to the solution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+1063
to
+1068
| const DWORD waitResult = WaitForMultipleObjects(count, handles, FALSE, 100); | ||
| if (waitResult == WAIT_FAILED) | ||
| { | ||
| set_error(session, "WaitForMultipleObjects failed."); | ||
| break; | ||
| } |
Comment on lines
+1085
to
+1087
| struct timeval tv{ 0, 100'000 }; // 100 ms | ||
| if (maxfd >= 0) | ||
| select(maxfd + 1, &rfds, nullptr, nullptr, &tv); |
Comment on lines
+94
to
+116
| if (TryGetPrintableScancode(e, out var scancode, out var needsShift)) | ||
| { | ||
| if (!needsShift && down && _syntheticShiftDown) | ||
| { | ||
| vm.SendKey(0x2A, false); | ||
| _syntheticShiftDown = false; | ||
| } | ||
| if (needsShift && down && !_syntheticShiftDown) | ||
| { | ||
| vm.SendKey(0x2A, true); | ||
| _syntheticShiftDown = true; | ||
| } | ||
|
|
||
| vm.SendKey(scancode, down); | ||
|
|
||
| if (!down && _syntheticShiftDown) | ||
| { | ||
| vm.SendKey(0x2A, false); | ||
| _syntheticShiftDown = false; | ||
| } | ||
| e.Handled = true; | ||
| return; | ||
| } |
Comment on lines
+115
to
+137
| unsafe void IRdpFrameReceiver.OnFrame(int width, int height, int stride, ReadOnlySpan<byte> pixels) | ||
| { | ||
| var copy = new byte[pixels.Length]; | ||
| pixels.CopyTo(copy); | ||
|
|
||
| Dispatcher.UIThread.Post(() => | ||
| { | ||
| var bmp = new WriteableBitmap( | ||
| new PixelSize(width, height), | ||
| new Vector(96, 96), | ||
| PixelFormat.Bgra8888, | ||
| AlphaFormat.Opaque); | ||
|
|
||
| using var locked = bmp.Lock(); | ||
| var dst = new Span<byte>((void*)locked.Address, locked.RowBytes * locked.Size.Height); | ||
| var src = copy.AsSpan(0, Math.Min(copy.Length, dst.Length)); | ||
| src.CopyTo(dst); | ||
|
|
||
| Framebuffer = bmp; | ||
| RemoteWidth = width; | ||
| RemoteHeight = height; | ||
| }); | ||
| } |
- Subscribe ClipboardReceived: remote → local clipboard via IClipboard.SetTextAsync - Poll local clipboard every 500ms with DispatcherTimer: local → remote via SetClipboardText - Track last seen text on both sides to suppress echo/feedback loops - Inject IClipboard accessors from MainWindow.Opened (requires TopLevel context) - Start/stop poll timer on Connected/Disconnected state transitions Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Native layer: - New API: RdpBridge_start_local_clipboard_monitor / RdpBridge_stop_local_clipboard_monitor - Windows: hidden HWND_MESSAGE window + AddClipboardFormatListener + WM_CLIPBOARDUPDATE; fires RdpBridge_LocalClipboardCallback on text change; runs on a dedicated thread (GetMessage blocks, zero CPU idle) - Linux / macOS: silent no-op, returns 0, callback never invoked - RdpBridge_destroy stops the monitor before disconnecting C# wrapper (RdpBridgeClient): - StartLocalClipboardMonitor / StopLocalClipboardMonitor methods - LocalClipboardChanged event - P/Invoke for both new native functions RdpDemo: - Replace 500 ms DispatcherTimer polling with LocalClipboardChanged - SetClipboardAccessors now only takes the setter (getter no longer needed) - Echo suppression: local→remote skips text that arrived from remote Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
- rdp_bridge.h: restore missing /** opening of RdpBridge_clipboard_set_text doc comment (was accidentally truncated when inserting new clipboard monitor API, causing C4430/C2146 parse errors on MSVC x86) - rdp_bridge.cpp: freerdp_get_event_handles takes 3 args in FreeRDP3 (context, handles, capacity) and returns the count as DWORD, not a BOOL with an out-param; fix call to match actual signature Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces a new Avalonia-based cross-platform demo application (
RdpDemo) for the RdpBridge project, providing a graphical user interface to connect to and interact with remote desktops via RDP. It also refactors the RDP event loop in the native bridge for improved efficiency and responsiveness.Key changes include:
New Avalonia Demo Application
RdpDemoproject, including solution and project files, to serve as a cross-platform GUI demo for RdpBridge. (dotnet/RdpBridge.slnx,dotnet/RdpDemo/RdpDemo.csproj) [1] [2]App.axaml,App.axaml.cs, andProgram.cs, to bootstrap the Avalonia app. [1] [2] [3]MainWindow.axaml/.cs) with connection controls and status display, and a dedicated RDP view (RdpView.axaml/.cs) for framebuffer rendering and input handling. [1] [2] [3] [4]MainWindowViewModelimplementing connection logic, framebuffer updates, and input forwarding to the RdpBridge backend.Native RDP Bridge Improvements
rdp_bridge.cppto use OS-level event waiting (select()on POSIX,WaitForMultipleObjectson Windows) instead of busy-polling, resulting in reduced CPU usage and more responsive session handling. [1] [2]These changes provide a modern, user-friendly way to test and demonstrate RdpBridge functionality and improve the efficiency of the underlying native bridge.