|
@@ -6,6 +6,8 @@ using System.Text;
|
|
|
using System.Threading.Tasks;
|
|
using System.Threading.Tasks;
|
|
|
using Avalonia.Controls.Embedding.Offscreen;
|
|
using Avalonia.Controls.Embedding.Offscreen;
|
|
|
using Avalonia.Controls.Platform.Surfaces;
|
|
using Avalonia.Controls.Platform.Surfaces;
|
|
|
|
|
+using Avalonia.Input;
|
|
|
|
|
+using Avalonia.Layout;
|
|
|
using Avalonia.Platform;
|
|
using Avalonia.Platform;
|
|
|
using Avalonia.Remote.Protocol;
|
|
using Avalonia.Remote.Protocol;
|
|
|
using Avalonia.Remote.Protocol.Viewport;
|
|
using Avalonia.Remote.Protocol.Viewport;
|
|
@@ -15,17 +17,20 @@ using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat;
|
|
|
|
|
|
|
|
namespace Avalonia.Controls.Remote.Server
|
|
namespace Avalonia.Controls.Remote.Server
|
|
|
{
|
|
{
|
|
|
- class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface
|
|
|
|
|
|
|
+ public class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface
|
|
|
{
|
|
{
|
|
|
- private readonly IAvaloniaRemoteTransport _transport;
|
|
|
|
|
|
|
+ private readonly IAvaloniaRemoteTransportConnection _transport;
|
|
|
private LockedFramebuffer _framebuffer;
|
|
private LockedFramebuffer _framebuffer;
|
|
|
private object _lock = new object();
|
|
private object _lock = new object();
|
|
|
- private long _lastSentFrame;
|
|
|
|
|
|
|
+ private long _lastSentFrame = -1;
|
|
|
private long _lastReceivedFrame = -1;
|
|
private long _lastReceivedFrame = -1;
|
|
|
|
|
+ private long _nextFrameNumber = 1;
|
|
|
|
|
+ private ClientViewportAllocatedMessage _pendingAllocation;
|
|
|
private bool _invalidated;
|
|
private bool _invalidated;
|
|
|
|
|
+ private Vector _dpi = new Vector(96, 96);
|
|
|
private ProtocolPixelFormat[] _supportedFormats;
|
|
private ProtocolPixelFormat[] _supportedFormats;
|
|
|
|
|
|
|
|
- public RemoteServerTopLevelImpl(IAvaloniaRemoteTransport transport)
|
|
|
|
|
|
|
+ public RemoteServerTopLevelImpl(IAvaloniaRemoteTransportConnection transport)
|
|
|
{
|
|
{
|
|
|
_transport = transport;
|
|
_transport = transport;
|
|
|
_transport.OnMessage += OnMessage;
|
|
_transport.OnMessage += OnMessage;
|
|
@@ -35,24 +40,64 @@ namespace Avalonia.Controls.Remote.Server
|
|
|
{
|
|
{
|
|
|
lock (_lock)
|
|
lock (_lock)
|
|
|
{
|
|
{
|
|
|
- var lastFrame = obj as FrameReceivedMessage;
|
|
|
|
|
- if (lastFrame != null)
|
|
|
|
|
|
|
+ if (obj is FrameReceivedMessage lastFrame)
|
|
|
{
|
|
{
|
|
|
lock (_lock)
|
|
lock (_lock)
|
|
|
{
|
|
{
|
|
|
_lastReceivedFrame = lastFrame.SequenceId;
|
|
_lastReceivedFrame = lastFrame.SequenceId;
|
|
|
}
|
|
}
|
|
|
- Dispatcher.UIThread.InvokeAsync(CheckNeedsRender);
|
|
|
|
|
|
|
+ Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (obj is ClientSupportedPixelFormatsMessage supportedFormats)
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_lock)
|
|
|
|
|
+ _supportedFormats = supportedFormats.Formats;
|
|
|
|
|
+ Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (obj is MeasureViewportMessage measure)
|
|
|
|
|
+ Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
|
|
+ {
|
|
|
|
|
+ var m = Measure(new Size(measure.Width, measure.Height));
|
|
|
|
|
+ _transport.Send(new MeasureViewportMessage
|
|
|
|
|
+ {
|
|
|
|
|
+ Width = m.Width,
|
|
|
|
|
+ Height = m.Height
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+ if (obj is ClientViewportAllocatedMessage allocated)
|
|
|
|
|
+ {
|
|
|
|
|
+ lock (_lock)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (_pendingAllocation == null)
|
|
|
|
|
+ Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
|
|
+ {
|
|
|
|
|
+ ClientViewportAllocatedMessage allocation;
|
|
|
|
|
+ lock (_lock)
|
|
|
|
|
+ {
|
|
|
|
|
+ allocation = _pendingAllocation;
|
|
|
|
|
+ _pendingAllocation = null;
|
|
|
|
|
+ }
|
|
|
|
|
+ _dpi = new Vector(allocation.DpiX, allocation.DpiY);
|
|
|
|
|
+ ClientSize = new Size(allocation.Width, allocation.Height);
|
|
|
|
|
+ RenderIfNeeded();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ _pendingAllocation = allocated;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- var supportedFormats = obj as ClientSupportedPixelFormatsMessage;
|
|
|
|
|
- if (supportedFormats != null)
|
|
|
|
|
- _supportedFormats = supportedFormats.Formats;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ protected virtual Size Measure(Size constaint)
|
|
|
|
|
+ {
|
|
|
|
|
+ var l = (ILayoutable) InputRoot;
|
|
|
|
|
+ l.Measure(constaint);
|
|
|
|
|
+ return l.DesiredSize;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
public override IEnumerable<object> Surfaces => new[] { this };
|
|
public override IEnumerable<object> Surfaces => new[] { this };
|
|
|
|
|
|
|
|
- FrameMessage RenderFrame(int width, int height, Size dpi, ProtocolPixelFormat? format)
|
|
|
|
|
|
|
+ FrameMessage RenderFrame(int width, int height, ProtocolPixelFormat? format)
|
|
|
{
|
|
{
|
|
|
var fmt = format ?? ProtocolPixelFormat.Rgba8888;
|
|
var fmt = format ?? ProtocolPixelFormat.Rgba8888;
|
|
|
var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4;
|
|
var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4;
|
|
@@ -60,7 +105,7 @@ namespace Avalonia.Controls.Remote.Server
|
|
|
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
|
|
try
|
|
try
|
|
|
{
|
|
{
|
|
|
- _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), width, height, width * bpp, dpi, (PixelFormat)fmt,
|
|
|
|
|
|
|
+ _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), width, height, width * bpp, _dpi, (PixelFormat)fmt,
|
|
|
null);
|
|
null);
|
|
|
Paint?.Invoke(new Rect(0, 0, width, height));
|
|
Paint?.Invoke(new Rect(0, 0, width, height));
|
|
|
}
|
|
}
|
|
@@ -69,7 +114,14 @@ namespace Avalonia.Controls.Remote.Server
|
|
|
_framebuffer = null;
|
|
_framebuffer = null;
|
|
|
handle.Free();
|
|
handle.Free();
|
|
|
}
|
|
}
|
|
|
- return new FrameMessage();
|
|
|
|
|
|
|
+ return new FrameMessage
|
|
|
|
|
+ {
|
|
|
|
|
+ Data = data,
|
|
|
|
|
+ Format = (ProtocolPixelFormat) format,
|
|
|
|
|
+ Width = width,
|
|
|
|
|
+ Height = height,
|
|
|
|
|
+ Stride = width * bpp,
|
|
|
|
|
+ };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public ILockedFramebuffer Lock()
|
|
public ILockedFramebuffer Lock()
|
|
@@ -79,23 +131,40 @@ namespace Avalonia.Controls.Remote.Server
|
|
|
return _framebuffer;
|
|
return _framebuffer;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- void CheckNeedsRender()
|
|
|
|
|
|
|
+ void RenderIfNeeded()
|
|
|
{
|
|
{
|
|
|
- ProtocolPixelFormat[] formats;
|
|
|
|
|
lock (_lock)
|
|
lock (_lock)
|
|
|
{
|
|
{
|
|
|
- if (_lastReceivedFrame != _lastSentFrame && !_invalidated)
|
|
|
|
|
|
|
+ if (_lastReceivedFrame != _lastSentFrame || !_invalidated || _supportedFormats == null)
|
|
|
return;
|
|
return;
|
|
|
- formats = _supportedFormats;
|
|
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
+ if (ClientSize.Width < 1 || ClientSize.Height < 1)
|
|
|
|
|
+ return;
|
|
|
|
|
+ var format = ProtocolPixelFormat.Rgba8888;
|
|
|
|
|
+ foreach(var fmt in _supportedFormats)
|
|
|
|
|
+ if (fmt <= ProtocolPixelFormat.MaxValue)
|
|
|
|
|
+ {
|
|
|
|
|
+ format = fmt;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- //var frame = RenderFrame()
|
|
|
|
|
|
|
+ var frame = RenderFrame((int) ClientSize.Width, (int) ClientSize.Height, format);
|
|
|
|
|
+ lock (_lock)
|
|
|
|
|
+ {
|
|
|
|
|
+ _lastSentFrame = _nextFrameNumber++;
|
|
|
|
|
+ frame.SequenceId = _lastSentFrame;
|
|
|
|
|
+ _invalidated = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ _transport.Send(frame);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public override void Invalidate(Rect rect)
|
|
public override void Invalidate(Rect rect)
|
|
|
{
|
|
{
|
|
|
_invalidated = true;
|
|
_invalidated = true;
|
|
|
- Dispatcher.UIThread.InvokeAsync(CheckNeedsRender);
|
|
|
|
|
|
|
+ Dispatcher.UIThread.InvokeAsync(RenderIfNeeded);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ public override IMouseDevice MouseDevice { get; } = new MouseDevice();
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|