diff --git a/Avalonia.sln b/Avalonia.sln index 391d02382e..56c489fd0b 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26228.4 +VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Base", "src\Avalonia.Base\Avalonia.Base.csproj", "{B09B78D8-9B26-48B0-9149-D64A2F120F3F}" EndProject @@ -185,6 +185,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Interop", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia.RenderTests", "tests\Avalonia.RenderTests\Avalonia.Skia.RenderTests.csproj", "{E1582370-37B3-403C-917F-8209551B1634}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Remote.Protocol", "src\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj", "{D78A720C-C0C6-478B-8564-F167F9BDD01B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RemoteTest", "samples\RemoteTest\RemoteTest.csproj", "{E2999E4A-9086-401F-898C-AEB0AD38E676}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution src\Shared\RenderHelpers\RenderHelpers.projitems*{3c4c0cb4-0c0f-4450-a37b-148c84ff905f}*SharedItemsImports = 13 @@ -2514,6 +2518,86 @@ Global {E1582370-37B3-403C-917F-8209551B1634}.Release|Mono.Build.0 = Release|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|x86.ActiveCfg = Release|Any CPU {E1582370-37B3-403C-917F-8209551B1634}.Release|x86.Build.0 = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhone.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Mono.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|Mono.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|x86.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.AppStore|x86.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhone.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Mono.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|Mono.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|x86.ActiveCfg = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Debug|x86.Build.0 = Debug|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Any CPU.Build.0 = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.ActiveCfg = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhone.Build.0 = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Mono.ActiveCfg = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|Mono.Build.0 = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|x86.ActiveCfg = Release|Any CPU + {D78A720C-C0C6-478B-8564-F167F9BDD01B}.Release|x86.Build.0 = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Mono.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|Mono.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhone.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Mono.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|Mono.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|x86.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.AppStore|x86.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhone.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Mono.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|Mono.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|x86.ActiveCfg = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Debug|x86.Build.0 = Debug|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Any CPU.Build.0 = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.ActiveCfg = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhone.Build.0 = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Mono.ActiveCfg = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|Mono.Build.0 = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|x86.ActiveCfg = Release|Any CPU + {E2999E4A-9086-401F-898C-AEB0AD38E676}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2571,5 +2655,9 @@ Global {638580B0-7910-40EF-B674-DCB34DA308CD} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {CBC4FF2F-92D4-420B-BE21-9FE0B930B04E} = {B39A8919-9F95-48FE-AD7B-76E08B509888} {E1582370-37B3-403C-917F-8209551B1634} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} + {E2999E4A-9086-401F-898C-AEB0AD38E676} = {9B9E3891-2366-4253-A952-D08BCEB71098} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1E8CA5AA-707A-4C57-A682-D265A49E10C3} EndGlobalSection EndGlobal diff --git a/samples/RemoteTest/Program.cs b/samples/RemoteTest/Program.cs new file mode 100644 index 0000000000..eb5f388e9e --- /dev/null +++ b/samples/RemoteTest/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace RemoteTest +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello World!"); + } + } +} diff --git a/samples/RemoteTest/RemoteTest.csproj b/samples/RemoteTest/RemoteTest.csproj new file mode 100644 index 0000000000..2487c66e41 --- /dev/null +++ b/samples/RemoteTest/RemoteTest.csproj @@ -0,0 +1,25 @@ + + + + Exe + netcoreapp2.0 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 037546b186..47e0f88341 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -36,6 +36,7 @@ + diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs new file mode 100644 index 0000000000..cd2da692ba --- /dev/null +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevel.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Layout; +using Avalonia.Styling; + +namespace Avalonia.Controls.Embedding.Offscreen +{ + class OffscreenTopLevel : TopLevel, IStyleable + { + public OffscreenTopLevelImplBase Impl { get; } + + public OffscreenTopLevel(OffscreenTopLevelImplBase impl) : base(impl) + { + Impl = impl; + Prepare(); + } + + public void Prepare() + { + EnsureInitialized(); + ApplyTemplate(); + LayoutManager.Instance.ExecuteInitialLayoutPass(this); + } + + private void EnsureInitialized() + { + if (!this.IsInitialized) + { + var init = (ISupportInitialize)this; + init.BeginInit(); + init.EndInit(); + } + } + + private readonly NameScope _nameScope = new NameScope(); + public event EventHandler Registered + { + add { _nameScope.Registered += value; } + remove { _nameScope.Registered -= value; } + } + + public event EventHandler Unregistered + { + add { _nameScope.Unregistered += value; } + remove { _nameScope.Unregistered -= value; } + } + + public void Register(string name, object element) => _nameScope.Register(name, element); + + public object Find(string name) => _nameScope.Find(name); + + public void Unregister(string name) => _nameScope.Unregister(name); + + Type IStyleable.StyleKey => typeof(EmbeddableControlRoot); + public void Dispose() + { + PlatformImpl.Dispose(); + } + } +} diff --git a/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs new file mode 100644 index 0000000000..088f20b270 --- /dev/null +++ b/src/Avalonia.Controls/Embedding/Offscreen/OffscreenTopLevelImpl.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; + +namespace Avalonia.Controls.Embedding.Offscreen +{ + abstract class OffscreenTopLevelImplBase : ITopLevelImpl + { + private double _scaling; + private Size _clientSize; + public IInputRoot InputRoot { get; private set; } + + public virtual void Dispose() + { + //No-op + } + + public abstract void Invalidate(Rect rect); + public abstract IEnumerable Surfaces { get; } + + public Size ClientSize + { + get { return _clientSize; } + set + { + _clientSize = value; + Resized?.Invoke(value); + } + } + + public double Scaling + { + get { return _scaling; } + set + { + _scaling = value; + ScalingChanged?.Invoke(value); + } + } + + public Action Input { get; set; } + public Action Paint { get; set; } + public Action Resized { get; set; } + public Action ScalingChanged { get; set; } + public void SetInputRoot(IInputRoot inputRoot) => InputRoot = inputRoot; + + public virtual Point PointToClient(Point point) => point; + + public Point PointToScreen(Point point) + { + throw new NotImplementedException(); + } + + public virtual void SetCursor(IPlatformHandle cursor) + { + } + + public Action Closed { get; set; } + } +} diff --git a/src/Avalonia.Controls/Remote/RemoteServer.cs b/src/Avalonia.Controls/Remote/RemoteServer.cs new file mode 100644 index 0000000000..8c0509c5ba --- /dev/null +++ b/src/Avalonia.Controls/Remote/RemoteServer.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Remote.Protocol; + +namespace Avalonia.Controls.Remote +{ + public class RemoteServer + { + private readonly IAvaloniaRemoteTransport _transport; + + public RemoteServer(IAvaloniaRemoteTransport transport) + { + _transport = transport; + } + + public object Content { get; set; } + } +} diff --git a/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs new file mode 100644 index 0000000000..6c89264c64 --- /dev/null +++ b/src/Avalonia.Controls/Remote/Server/RemoteServerTopLevelImpl.cs @@ -0,0 +1,101 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Avalonia.Controls.Embedding.Offscreen; +using Avalonia.Controls.Platform.Surfaces; +using Avalonia.Platform; +using Avalonia.Remote.Protocol; +using Avalonia.Remote.Protocol.Viewport; +using Avalonia.Threading; +using PixelFormat = Avalonia.Platform.PixelFormat; +using ProtocolPixelFormat = Avalonia.Remote.Protocol.Viewport.PixelFormat; + +namespace Avalonia.Controls.Remote.Server +{ + class RemoteServerTopLevelImpl : OffscreenTopLevelImplBase, IFramebufferPlatformSurface + { + private readonly IAvaloniaRemoteTransport _transport; + private LockedFramebuffer _framebuffer; + private object _lock = new object(); + private long _lastSentFrame; + private long _lastReceivedFrame = -1; + private bool _invalidated; + private ProtocolPixelFormat[] _supportedFormats; + + public RemoteServerTopLevelImpl(IAvaloniaRemoteTransport transport) + { + _transport = transport; + _transport.OnMessage += OnMessage; + } + + private void OnMessage(object obj) + { + lock (_lock) + { + var lastFrame = obj as FrameReceivedMessage; + if (lastFrame != null) + { + lock (_lock) + { + _lastReceivedFrame = lastFrame.SequenceId; + } + Dispatcher.UIThread.InvokeAsync(CheckNeedsRender); + } + var supportedFormats = obj as ClientSupportedPixelFormatsMessage; + if (supportedFormats != null) + _supportedFormats = supportedFormats.Formats; + } + } + + public override IEnumerable Surfaces => new[] { this }; + + FrameMessage RenderFrame(int width, int height, Size dpi, ProtocolPixelFormat? format) + { + var fmt = format ?? ProtocolPixelFormat.Rgba8888; + var bpp = fmt == ProtocolPixelFormat.Rgb565 ? 2 : 4; + var data = new byte[width * height * bpp]; + var handle = GCHandle.Alloc(data, GCHandleType.Pinned); + try + { + _framebuffer = new LockedFramebuffer(handle.AddrOfPinnedObject(), width, height, width * bpp, dpi, (PixelFormat)fmt, + null); + Paint?.Invoke(new Rect(0, 0, width, height)); + } + finally + { + _framebuffer = null; + handle.Free(); + } + return new FrameMessage(); + } + + public ILockedFramebuffer Lock() + { + if (_framebuffer == null) + throw new InvalidOperationException("Paint was not requested, wait for Paint event"); + return _framebuffer; + } + + void CheckNeedsRender() + { + ProtocolPixelFormat[] formats; + lock (_lock) + { + if (_lastReceivedFrame != _lastSentFrame && !_invalidated) + return; + formats = _supportedFormats; + } + + //var frame = RenderFrame() + } + + public override void Invalidate(Rect rect) + { + _invalidated = true; + Dispatcher.UIThread.InvokeAsync(CheckNeedsRender); + } + } +} diff --git a/src/Avalonia.Input/Key.cs b/src/Avalonia.Input/Key.cs index 3ea506d79e..c10cbc0a6c 100644 --- a/src/Avalonia.Input/Key.cs +++ b/src/Avalonia.Input/Key.cs @@ -1,7 +1,10 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. - +#if AVALONIA_REMOTE_PROTOCOL +namespace Avalonia.Remote.Protocol.Input +#else namespace Avalonia.Input +#endif { /// /// Defines the keys available on a keyboard. diff --git a/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj new file mode 100644 index 0000000000..1ac7ab3a8b --- /dev/null +++ b/src/Avalonia.Remote.Protocol/Avalonia.Remote.Protocol.csproj @@ -0,0 +1,9 @@ + + + netstandard1.3 + AVALONIA_REMOTE_PROTOCOL;$(DefineConstants) + + + + + diff --git a/src/Avalonia.Remote.Protocol/AvaloniaRemoteMessageGuidAttribute.cs b/src/Avalonia.Remote.Protocol/AvaloniaRemoteMessageGuidAttribute.cs new file mode 100644 index 0000000000..12ee3178d3 --- /dev/null +++ b/src/Avalonia.Remote.Protocol/AvaloniaRemoteMessageGuidAttribute.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Remote.Protocol +{ + [AttributeUsage(AttributeTargets.Class)] + public class AvaloniaRemoteMessageGuidAttribute : Attribute + { + public Guid Guid { get; } + + public AvaloniaRemoteMessageGuidAttribute(string guid) + { + Guid = Guid.Parse(guid); + } + } +} diff --git a/src/Avalonia.Remote.Protocol/DefaultMessageTypeResolver.cs b/src/Avalonia.Remote.Protocol/DefaultMessageTypeResolver.cs new file mode 100644 index 0000000000..9d44f33627 --- /dev/null +++ b/src/Avalonia.Remote.Protocol/DefaultMessageTypeResolver.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Remote.Protocol +{ + public class DefaultMessageTypeResolver : IMessageTypeResolver + { + private readonly Dictionary _guidsToTypes = new Dictionary(); + private readonly Dictionary _typesToGuids = new Dictionary(); + public DefaultMessageTypeResolver(params Assembly[] assemblies) + { + foreach (var asm in + (assemblies ?? new Assembly[0]).Concat(new[] + {typeof(AvaloniaRemoteMessageGuidAttribute).GetTypeInfo().Assembly})) + { + foreach (var t in asm.ExportedTypes) + { + var attr = t.GetTypeInfo().GetCustomAttribute(); + if (attr != null) + { + _guidsToTypes[attr.Guid] = t; + _typesToGuids[t] = attr.Guid; + } + } + } + } + + public Type GetByGuid(Guid id) => _guidsToTypes[id]; + public Guid GetGuid(Type type) => _typesToGuids[type]; + } +} diff --git a/src/Avalonia.Remote.Protocol/IMessageTypeResolver.cs b/src/Avalonia.Remote.Protocol/IMessageTypeResolver.cs new file mode 100644 index 0000000000..2e2234b0d6 --- /dev/null +++ b/src/Avalonia.Remote.Protocol/IMessageTypeResolver.cs @@ -0,0 +1,10 @@ +using System; + +namespace Avalonia.Remote.Protocol +{ + public interface IMessageTypeResolver + { + Type GetByGuid(Guid id); + Guid GetGuid(Type type); + } +} \ No newline at end of file diff --git a/src/Avalonia.Remote.Protocol/ITransport.cs b/src/Avalonia.Remote.Protocol/ITransport.cs new file mode 100644 index 0000000000..e90a49ac71 --- /dev/null +++ b/src/Avalonia.Remote.Protocol/ITransport.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Remote.Protocol +{ + public interface IAvaloniaRemoteTransport + { + Task Send(object data); + event Action OnMessage; + event Action OnException; + } +} diff --git a/src/Avalonia.Remote.Protocol/InputMessages.cs b/src/Avalonia.Remote.Protocol/InputMessages.cs new file mode 100644 index 0000000000..f1996372fc --- /dev/null +++ b/src/Avalonia.Remote.Protocol/InputMessages.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +/* + We are keeping copies of core events here, so they can be used + without referencing Avalonia itself, e. g. from projects that + are using WPF, GTK#, etc + */ +namespace Avalonia.Remote.Protocol.Input +{ + /// + /// Keep this in sync with InputModifiers in the main library + /// + [Flags] + public enum InputModifiers + { + Alt, + Control, + Shift, + Windows, + LeftMouseButton, + RightMouseButton, + MiddleMouseButton + } + + /// + /// Keep this in sync with InputModifiers in the main library + /// + public enum MouseButton + { + None, + Left, + Right, + Middle + } + + public abstract class InputEventMessageBase + { + public InputModifiers[] Modifiers { get; set; } + } + + public abstract class PointerEventMessageBase : InputEventMessageBase + { + public double X { get; set; } + public double Y { get; set; } + } + + [AvaloniaRemoteMessageGuid("6228F0B9-99F2-4F62-A621-414DA2881648")] + public class PointerMovedEventMessage : PointerEventMessageBase + { + + } + + [AvaloniaRemoteMessageGuid("7E9E2818-F93F-411A-800E-6B1AEB11DA46")] + public class PointerPressedEventMessage : PointerEventMessageBase + { + public MouseButton Button { get; set; } + } + + [AvaloniaRemoteMessageGuid("4ADC84EE-E7C8-4BCF-986C-DE3A2F78EDE4")] + public class PointerReleasedEventMessage : PointerEventMessageBase + { + public MouseButton Button { get; set; } + } + + [AvaloniaRemoteMessageGuid("79301A05-F02D-4B90-BB39-472563B504AE")] + public class ScrollEventMessage : PointerEventMessageBase + { + public double DeltaX { get; set; } + public double DeltaY { get; set; } + } + + [AvaloniaRemoteMessageGuid("1C3B691E-3D54-4237-BFB0-9FEA83BC1DB8")] + public class KeyEventMessage : InputEventMessageBase + { + public bool IsDown { get; set; } + public Key Key { get; set; } + } + +} diff --git a/src/Avalonia.Remote.Protocol/ViewportMessages.cs b/src/Avalonia.Remote.Protocol/ViewportMessages.cs new file mode 100644 index 0000000000..8e1b0bd99e --- /dev/null +++ b/src/Avalonia.Remote.Protocol/ViewportMessages.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Avalonia.Remote.Protocol.Viewport +{ + public enum PixelFormat + { + Rgb565, + Rgba8888, + Bgra8888 + } + + [AvaloniaRemoteMessageGuid("6E3C5310-E2B1-4C3D-8688-01183AA48C5B")] + public class MeasureViewportMessage + { + public double Width { get; set; } + public double Height { get; set; } + } + + [AvaloniaRemoteMessageGuid("BD7A8DE6-3DB8-4A13-8583-D6D4AB189A31")] + public class ClientViewportAllocatedMessage + { + public double Width { get; set; } + public double Height { get; set; } + } + + [AvaloniaRemoteMessageGuid("63481025-7016-43FE-BADC-F2FD0F88609E")] + public class ClientSupportedPixelFormatsMessage + { + public PixelFormat[] Formats { get; set; } + } + + [AvaloniaRemoteMessageGuid("68014F8A-289D-4851-8D34-5367EDA7F827")] + public class FrameReceivedMessage + { + public long SequenceId { get; set; } + } + + + [AvaloniaRemoteMessageGuid("F58313EE-FE69-4536-819D-F52EDF201A0E")] + public class FrameMessage + { + public long SequenceId { get; set; } + public PixelFormat Format { get; set; } + public byte[] Data { get; set; } + public int Width { get; set; } + public int Height { get; set; } + public int Stride { get; set; } + } + +} diff --git a/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs b/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs new file mode 100644 index 0000000000..c03b714956 --- /dev/null +++ b/src/Avalonia.Visuals/Platform/LockedFramebuffer.cs @@ -0,0 +1,33 @@ +using System; + +namespace Avalonia.Platform +{ + public class LockedFramebuffer : ILockedFramebuffer + { + private readonly Action _onDispose; + + public LockedFramebuffer(IntPtr address, int width, int height, int rowBytes, Vector dpi, PixelFormat format, + Action onDispose) + { + _onDispose = onDispose; + Address = address; + Width = width; + Height = height; + RowBytes = rowBytes; + Dpi = dpi; + Format = format; + } + + public IntPtr Address { get; } + public int Width { get; } + public int Height { get; } + public int RowBytes { get; } + public Vector Dpi { get; } + public PixelFormat Format { get; } + + public void Dispose() + { + _onDispose?.Invoke(); + } + } +} \ No newline at end of file