Skip to content

Feature/rdp demo - #3

Merged
Soar360 merged 5 commits into
mainfrom
feature/rdp-demo
Jun 30, 2026
Merged

Feature/rdp demo#3
Soar360 merged 5 commits into
mainfrom
feature/rdp-demo

Conversation

@Soar360

@Soar360 Soar360 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

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

  • Added the RdpDemo project, including solution and project files, to serve as a cross-platform GUI demo for RdpBridge. (dotnet/RdpBridge.slnx, dotnet/RdpDemo/RdpDemo.csproj) [1] [2]
  • Implemented the main application structure, including App.axaml, App.axaml.cs, and Program.cs, to bootstrap the Avalonia app. [1] [2] [3]
  • Created the main window UI (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]
  • Developed a MainWindowViewModel implementing connection logic, framebuffer updates, and input forwarding to the RdpBridge backend.

Native RDP Bridge Improvements

  • Refactored the event loop in rdp_bridge.cpp to use OS-level event waiting (select() on POSIX, WaitForMultipleObjects on 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.

Soar360 and others added 2 commits June 30, 2026 16:43
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>
Copilot AI review requested due to automatic review settings June 30, 2026 08:47

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 RdpDemo Avalonia 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.cpp to 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;
});
}
Soar360 and others added 3 commits June 30, 2026 16:59
- 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>
@Soar360
Soar360 merged commit daefe45 into main Jun 30, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants