Browse Source

upgrade `Quamotion.RemoteViewing` to 1.1.211 to work with `RealVNC Viewer`, Add `password` parameter to `StartWithHeadlessVncPlatform` (#15406)

* upgrade `Quamotion.RemoteViewing` to 1.1.211 to work with `RealVNC Viewer`

* change AfterSetup to AfterApplicationSetup

* remove netstandard2.0 as latest Quamotion.RemoteViewing doest not support it.

* downgrade RemoteViewer to 1.1.179 to work with netstandard2.0; remove ILogger parameter use Avalonia.Logging.Logger instead.

* adding password method overload to avoid binary break change.
pull/14603/head
Dameng 2 years ago
committed by GitHub
parent
commit
fa1fdd463f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 5
      src/Avalonia.Base/Logging/LogArea.cs
  2. 2
      src/Headless/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj
  3. 39
      src/Headless/Avalonia.Headless.Vnc/AvaloniaVncLogger.cs
  4. 28
      src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs
  5. 83
      src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs

5
src/Avalonia.Base/Logging/LogArea.cs

@ -79,5 +79,10 @@ namespace Avalonia.Logging
/// The log event comes from Browser Platform
/// </summary>
public const string BrowserPlatform = nameof(BrowserPlatform);
/// <summary>
/// The log event comes from VNC Platform
/// </summary>
public const string VncPlatform = nameof(VncPlatform);
}
}

2
src/Headless/Avalonia.Headless.Vnc/Avalonia.Headless.Vnc.csproj

@ -7,7 +7,7 @@
<ItemGroup>
<ProjectReference Include="..\Avalonia.Headless\Avalonia.Headless.csproj" />
<PackageReference Include="Quamotion.RemoteViewing" Version="1.1.21" />
<PackageReference Include="Quamotion.RemoteViewing" Version="1.1.179" />
</ItemGroup>
<Import Project="..\..\..\build\DevAnalyzers.props" />

39
src/Headless/Avalonia.Headless.Vnc/AvaloniaVncLogger.cs

@ -0,0 +1,39 @@
using System;
using Avalonia.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions.Internal;
namespace Avalonia.Headless.Vnc;
internal class AvaloniaVncLogger : ILogger
{
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
Logger.TryGet(ToLogEventLevel(logLevel), LogArea.VncPlatform)
?.Log(state, formatter(state,exception));
}
public bool IsEnabled(LogLevel logLevel)
{
return Logger.IsEnabled(ToLogEventLevel(logLevel), LogArea.VncPlatform);
}
public IDisposable BeginScope<TState>(TState state)
{
return NullScope.Instance;
}
private static LogEventLevel ToLogEventLevel(LogLevel logLevel)
{
return logLevel switch
{
LogLevel.Trace => LogEventLevel.Verbose,
LogLevel.Debug => LogEventLevel.Debug,
LogLevel.Information => LogEventLevel.Information,
LogLevel.Warning => LogEventLevel.Warning,
LogLevel.Error => LogEventLevel.Error,
LogLevel.Critical => LogEventLevel.Fatal,
_ => throw new ArgumentOutOfRangeException(nameof(logLevel))
};
}
}

28
src/Headless/Avalonia.Headless.Vnc/HeadlessVncFramebufferSource.cs

@ -23,7 +23,7 @@ namespace Avalonia.Headless.Vnc
session.PointerChanged += (_, args) =>
{
var pt = new Point(args.X, args.Y);
var buttons = (VncButton)args.PressedButtons;
MouseButton TranslateButton(VncButton vncButton) =>
@ -36,14 +36,14 @@ namespace Avalonia.Headless.Vnc
};
var modifiers = (RawInputModifiers)(((int)buttons & 7) << 4);
Dispatcher.UIThread.Post(() =>
{
Window?.MouseMove(pt);
foreach (var btn in CheckedButtons)
if (_previousButtons.HasFlag(btn) && !buttons.HasFlag(btn))
Window?.MouseUp(pt, TranslateButton(btn), modifiers);
foreach (var btn in CheckedButtons)
if (!_previousButtons.HasFlag(btn) && buttons.HasFlag(btn))
Window?.MouseDown(pt, TranslateButton(btn), modifiers);
@ -96,11 +96,11 @@ namespace Avalonia.Headless.Vnc
KeySym.AltLeft or KeySym.AltRight => RawInputModifiers.Alt,
_ => null
};
if(!toggleModifier.HasValue)
if (!toggleModifier.HasValue)
return false;
if(args.Pressed)
if (args.Pressed)
_keyState |= toggleModifier.Value;
else
_keyState &= ~toggleModifier.Value;
@ -309,9 +309,9 @@ namespace Avalonia.Headless.Vnc
ScrollUp = 8,
ScrollDown = 16
}
private static VncButton[] CheckedButtons = new[] {VncButton.Left, VncButton.Middle, VncButton.Right};
private static VncButton[] CheckedButtons = new[] { VncButton.Left, VncButton.Middle, VncButton.Right };
public unsafe VncFramebuffer Capture()
{
@ -338,5 +338,17 @@ namespace Avalonia.Headless.Vnc
return _framebuffer;
}
public ExtendedDesktopSizeStatus SetDesktopSize(int width, int height)
{
Dispatcher.UIThread.Post(() =>
{
Window.Width = width;
Window.Height = height;
});
return ExtendedDesktopSizeStatus.Success;
}
public bool SupportsResizing => true;
}
}

83
src/Headless/Avalonia.Headless.Vnc/HeadlessVncPlatformExtensions.cs

@ -1,10 +1,12 @@
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Headless;
using Avalonia.Headless.Vnc;
using Avalonia.Logging;
using Avalonia.Platform;
using RemoteViewing.Vnc;
using RemoteViewing.Vnc.Server;
@ -13,13 +15,45 @@ namespace Avalonia
{
public static class HeadlessVncPlatformExtensions
{
/// <summary>
/// Start Avalonia application with Headless VNC platform without password.
/// </summary>
/// <param name="builder">Application Builder</param>
/// <param name="host">VNC Server IP will be bind, if null or empty IPAddress.LoopBack will be used.</param>
/// <param name="port">VNC Server port will be bind</param>
/// <param name="args">Avalonia application start args</param>
/// <param name="shutdownMode">shut down mode <see cref="ShutdownMode"/></param>
/// <returns></returns>
public static int StartWithHeadlessVncPlatform(
this AppBuilder builder,
string host, int port,
string[] args, ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
string[] args,
ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
{
var tcpServer = new TcpListener(host == null ? IPAddress.Loopback : IPAddress.Parse(host), port);
tcpServer.Start();
return StartWithHeadlessVncPlatform(builder, host, port, null, args, shutdownMode);
}
/// <summary>
/// Start Avalonia application with Headless VNC platform with password.
/// </summary>
/// <param name="builder">Application Builder</param>
/// <param name="host">VNC Server IP will be bind, if null or empty IPAddress.LoopBack will be used.</param>
/// <param name="port">VNC Server port will be bind</param>
/// <param name="password">VNC connection auth password</param>
/// <param name="args">Avalonia application start args</param>
/// <param name="shutdownMode">shut down mode <see cref="ShutdownMode"/></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static int StartWithHeadlessVncPlatform(
this AppBuilder builder,
string host, int port,
string? password,
string[] args,
ShutdownMode shutdownMode = ShutdownMode.OnLastWindowClose)
{
var vncLogger = new AvaloniaVncLogger();
var tcpServer = new TcpListener(string.IsNullOrEmpty(host) ? IPAddress.Loopback : IPAddress.Parse(host), port);
tcpServer.Start();
return builder
.UseHeadless(new AvaloniaHeadlessPlatformOptions
{
@ -28,23 +62,44 @@ namespace Avalonia
})
.AfterApplicationSetup(_ =>
{
var lt = ((IClassicDesktopStyleApplicationLifetime)builder.Instance!.ApplicationLifetime!);
var lt = ((IClassicDesktopStyleApplicationLifetime) builder.Instance!.ApplicationLifetime!);
lt.Startup += async delegate
{
while (true)
{
var client = await tcpServer.AcceptTcpClientAsync();
var options = new VncServerSessionOptions
try
{
var client = await tcpServer.AcceptTcpClientAsync();
var options = new VncServerSessionOptions
{
AuthenticationMethod = string.IsNullOrWhiteSpace(password)
? AuthenticationMethod.None
: AuthenticationMethod.Password
};
var session = new VncServerSession(new VncPasswordChallenge(), logger:vncLogger);
if (string.IsNullOrWhiteSpace(password) == false)
{
session.PasswordProvided += (s, e) =>
{
e.Accept(password.ToCharArray());
};
}
session.SetFramebufferSource(new HeadlessVncFramebufferSource(
session,
lt.MainWindow ??
throw new InvalidOperationException("MainWindow wasn't initialized")));
session.Connect(client.GetStream(), options);
}
catch (Exception e)
{
Logger.TryGet(LogEventLevel.Error, LogArea.VncPlatform)?.Log(tcpServer,"Error accepting client:{Exception}", e);
}
finally
{
AuthenticationMethod = AuthenticationMethod.None
};
var session = new VncServerSession();
session.SetFramebufferSource(new HeadlessVncFramebufferSource(
session, lt.MainWindow ?? throw new InvalidOperationException("MainWindow wasn't initialized")));
session.Connect(client.GetStream(), options);
await Task.Delay(100);
}
}
};
})
.StartWithClassicDesktopLifetime(args, shutdownMode);

Loading…
Cancel
Save