From 6f4b3e1b65b1fe13d4870eac63f962ddf39fd71f Mon Sep 17 00:00:00 2001 From: Nelson Carrillo Date: Sat, 21 Jul 2018 16:47:03 -0400 Subject: [PATCH] WIP: Initial commit of winit support --- .../Avalonia.Windowing.csproj | 18 +++ src/Avalonia.Windowing/Bindings/EventsLoop.cs | 66 +++++++++ src/Avalonia.Windowing/Bindings/GlWindow.cs | 37 +++++ .../Bindings/IWindowWrapper.cs | 11 ++ src/Avalonia.Windowing/Bindings/Monitors.cs | 11 ++ src/Avalonia.Windowing/Bindings/Window.cs | 30 ++++ src/Avalonia.Windowing/IconLoader.cs | 35 +++++ src/Avalonia.Windowing/WIndowingPlatform.cs | 93 ++++++++++++ src/Avalonia.Windowing/WindowImpl.cs | 140 ++++++++++++++++++ .../WindowingPlatformExtensions.cs | 15 ++ 10 files changed, 456 insertions(+) create mode 100644 src/Avalonia.Windowing/Avalonia.Windowing.csproj create mode 100644 src/Avalonia.Windowing/Bindings/EventsLoop.cs create mode 100644 src/Avalonia.Windowing/Bindings/GlWindow.cs create mode 100644 src/Avalonia.Windowing/Bindings/IWindowWrapper.cs create mode 100644 src/Avalonia.Windowing/Bindings/Monitors.cs create mode 100644 src/Avalonia.Windowing/Bindings/Window.cs create mode 100644 src/Avalonia.Windowing/IconLoader.cs create mode 100644 src/Avalonia.Windowing/WIndowingPlatform.cs create mode 100644 src/Avalonia.Windowing/WindowImpl.cs create mode 100644 src/Avalonia.Windowing/WindowingPlatformExtensions.cs diff --git a/src/Avalonia.Windowing/Avalonia.Windowing.csproj b/src/Avalonia.Windowing/Avalonia.Windowing.csproj new file mode 100644 index 0000000000..758ec7cf29 --- /dev/null +++ b/src/Avalonia.Windowing/Avalonia.Windowing.csproj @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + netstandard2.0 + + + diff --git a/src/Avalonia.Windowing/Bindings/EventsLoop.cs b/src/Avalonia.Windowing/Bindings/EventsLoop.cs new file mode 100644 index 0000000000..7ace090666 --- /dev/null +++ b/src/Avalonia.Windowing/Bindings/EventsLoop.cs @@ -0,0 +1,66 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.Windowing.Bindings +{ + public class EventsLoop : IDisposable + { + [DllImport("winit_wrapper")] + private static extern IntPtr winit_events_loop_new(out IntPtr eventsLoopProxyHandle); + + [DllImport("winit_wrapper")] + private static extern void winit_events_loop_destroy(IntPtr handle); + + [DllImport("winit_wrapper")] + private static extern void winit_events_loop_run(IntPtr handle); + + public IntPtr Handle { get; private set; } + private readonly EventsLoopProxy _eventsLoopProxy; + + public EventsLoop() + { + Handle = winit_events_loop_new(out var elpHandle); + _eventsLoopProxy = new EventsLoopProxy(elpHandle); + } + + public void Run() + { + // We need a delegate callback here to support talking back to the C# code. + // Send an event type enum and then unsafely construct the event + winit_events_loop_run(Handle); + } + + public void Wakeup() + { + _eventsLoopProxy.Wakeup(); + } + + public void Dispose() + { + _eventsLoopProxy.Dispose(); + winit_events_loop_destroy(Handle); + } + + private class EventsLoopProxy : IDisposable + { + [DllImport("winit_wrapper")] + private static extern void winit_events_loop_proxy_destroy(IntPtr handle); + + private readonly IntPtr _handle; + public EventsLoopProxy(IntPtr handle) + { + _handle = handle; + } + + public void Wakeup() + { + + } + + public void Dispose() + { + winit_events_loop_proxy_destroy(_handle); + } + } + } +} diff --git a/src/Avalonia.Windowing/Bindings/GlWindow.cs b/src/Avalonia.Windowing/Bindings/GlWindow.cs new file mode 100644 index 0000000000..f41529637c --- /dev/null +++ b/src/Avalonia.Windowing/Bindings/GlWindow.cs @@ -0,0 +1,37 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.Windowing.Bindings +{ + /// + /// A window and GL context pair. + /// Due to platform specific quirks, the ordering of window and context creation must be controlled by winit. + /// + public class GlWindowWrapper : IWindowWrapper + { + [DllImport("winit_wrapper")] + private static extern IntPtr winit_gl_window_new(IntPtr eventsLoopHandle); + + [DllImport("winit_wrapper")] + private static extern IntPtr winit_gl_window_destroy(IntPtr handle); + + [DllImport("winit_wrapper")] + private static extern IntPtr winit_gl_window_set_title(IntPtr handle, string title); + + private IntPtr _handle; + public GlWindowWrapper(EventsLoop eventsLoop) + { + _handle = winit_gl_window_new(eventsLoop.Handle); + } + + public void Dispose() + { + winit_gl_window_destroy(_handle); + } + + public void SetTitle(string title) + { + winit_gl_window_set_title(_handle, title); + } + } +} diff --git a/src/Avalonia.Windowing/Bindings/IWindowWrapper.cs b/src/Avalonia.Windowing/Bindings/IWindowWrapper.cs new file mode 100644 index 0000000000..fe12ac54c6 --- /dev/null +++ b/src/Avalonia.Windowing/Bindings/IWindowWrapper.cs @@ -0,0 +1,11 @@ +using System; +namespace Avalonia.Windowing.Bindings +{ + /// + /// An interface used to abstract between Window and GlWindow. + /// + public interface IWindowWrapper : IDisposable + { + void SetTitle(string title); + } +} diff --git a/src/Avalonia.Windowing/Bindings/Monitors.cs b/src/Avalonia.Windowing/Bindings/Monitors.cs new file mode 100644 index 0000000000..cb5aba6591 --- /dev/null +++ b/src/Avalonia.Windowing/Bindings/Monitors.cs @@ -0,0 +1,11 @@ +using System; +using Avalonia.Platform; + +namespace Avalonia.Windowing.Bindings +{ + public class Monitors : IScreenImpl + { + public int ScreenCount => 1; + public Screen[] AllScreens => new Screen[] { new Screen(Rect.Empty, Rect.Empty, true) }; + } +} diff --git a/src/Avalonia.Windowing/Bindings/Window.cs b/src/Avalonia.Windowing/Bindings/Window.cs new file mode 100644 index 0000000000..24dc47db17 --- /dev/null +++ b/src/Avalonia.Windowing/Bindings/Window.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices; + +namespace Avalonia.Windowing.Bindings +{ + public class WindowWrapper : IWindowWrapper + { + [DllImport("winit_wrapper")] + private static extern IntPtr winit_window_new(IntPtr eventsLoopHandle); + + [DllImport("winit_wrapper")] + private static extern IntPtr winit_window_destroy(IntPtr handle); + + private IntPtr _handle; + public WindowWrapper(EventsLoop eventsLoop) + { + _handle = winit_window_new(eventsLoop.Handle); + } + + public void Dispose() + { + winit_window_destroy(_handle); + } + + public void SetTitle(string title) + { + + } + } +} diff --git a/src/Avalonia.Windowing/IconLoader.cs b/src/Avalonia.Windowing/IconLoader.cs new file mode 100644 index 0000000000..a3902bf12c --- /dev/null +++ b/src/Avalonia.Windowing/IconLoader.cs @@ -0,0 +1,35 @@ +using System; +using System.IO; +using Avalonia.Platform; + +namespace Avalonia.Windowing +{ + public class WindowIcon : IWindowIconImpl + { + public void Save(Stream outputStream) + { + } + } + + public class IconLoader : IPlatformIconLoader + { + public IconLoader() + { + } + + public IWindowIconImpl LoadIcon(string fileName) + { + return new WindowIcon(); + } + + public IWindowIconImpl LoadIcon(Stream stream) + { + return new WindowIcon(); + } + + public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) + { + return new WindowIcon(); + } + } +} diff --git a/src/Avalonia.Windowing/WIndowingPlatform.cs b/src/Avalonia.Windowing/WIndowingPlatform.cs new file mode 100644 index 0000000000..696171dda7 --- /dev/null +++ b/src/Avalonia.Windowing/WIndowingPlatform.cs @@ -0,0 +1,93 @@ +using System; +using System.Reactive.Disposables; +using System.Runtime.InteropServices; +using System.Threading; +using Avalonia.Input; +using Avalonia.Platform; +using Avalonia.Threading; +using Avalonia.Windowing.Bindings; + +namespace Avalonia.Windowing +{ + // Exposing C# Enums to Rust will again prove to be a massive PITA. + public enum WinitEventType + { + MouseMove + } + + public struct MouseMoveData + { + } + + public class DummyPlatformHandle : IPlatformHandle + { + public IntPtr Handle => IntPtr.Zero; + + public string HandleDescriptor => "Dummy"; + } + + + public class CursorFactory : IStandardCursorFactory + { + public IPlatformHandle GetCursor(StandardCursorType cursorType) + { + return new DummyPlatformHandle(); + } + } + + public class WindowingPlatform : IPlatformThreadingInterface, IWindowingPlatform + { + internal static WindowingPlatform Instance { get; private set; } + private readonly EventsLoop _eventsLoop; + + public WindowingPlatform() + { + _eventsLoop = new EventsLoop(); + } + + public static void Initialize() + { + Instance = new WindowingPlatform(); + Instance.DoInitialize(); + } + + public void DoInitialize() + { + AvaloniaLocator.CurrentMutable + .Bind().ToConstant(this) + .Bind().ToConstant(new IconLoader()) + .Bind().ToConstant(new CursorFactory()) + .Bind().ToConstant(this); + } + + public bool CurrentThreadIsLoopThread => true; + public event Action Signaled; + + public IEmbeddableWindowImpl CreateEmbeddableWindow() + { + throw new NotImplementedException(); + } + + public IPopupImpl CreatePopup() + { + throw new NotImplementedException(); + } + + public IWindowImpl CreateWindow() => new Window(new GlWindowWrapper(_eventsLoop)); + + public void RunLoop(CancellationToken cancellationToken) + { + _eventsLoop.Run(); + } + + public void Signal(DispatcherPriority priority) + { + _eventsLoop.Wakeup(); + } + + public IDisposable StartTimer(DispatcherPriority priority, TimeSpan interval, Action tick) + { + return Disposable.Create(() => { }); + } + } +} diff --git a/src/Avalonia.Windowing/WindowImpl.cs b/src/Avalonia.Windowing/WindowImpl.cs new file mode 100644 index 0000000000..ed9d6e2031 --- /dev/null +++ b/src/Avalonia.Windowing/WindowImpl.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Input.Raw; +using Avalonia.Platform; +using Avalonia.Rendering; +using Avalonia.Windowing.Bindings; + +namespace Avalonia.Windowing +{ + public class Window : IWindowImpl + { + IWindowWrapper _windowWrapper; + + public Window(IWindowWrapper wrapper) + { + _windowWrapper = wrapper; + } + + public WindowState WindowState { get; set; } + public Action WindowStateChanged { get; set; } + public Func Closing { get; set; } + public Point Position { get; set; } + public Action PositionChanged { get; set; } + public Action Deactivated { get; set; } + public Action Activated { get; set; } + + public IPlatformHandle Handle => new DummyPlatformHandle(); + + public Size MaxClientSize => Size.Empty; + + public IScreenImpl Screen => new Monitors(); + + public Size ClientSize => Size.Empty; + public double Scaling => 1.0; + + public IEnumerable Surfaces => new List() { this }; + + public Action Input { get; set; } + public Action Paint { get; set; } + public Action Resized { get; set; } + public Action ScalingChanged { get; set; } + public Action Closed { get; set; } + + public IMouseDevice MouseDevice => throw new NotImplementedException(); + + public void Activate() + { + } + + public void BeginMoveDrag() + { + } + + public void BeginResizeDrag(WindowEdge edge) + { + } + + public void CanResize(bool value) + { + } + + public IRenderer CreateRenderer(IRenderRoot root) + { + return new ImmediateRenderer(root); + } + + public void Dispose() + { + _windowWrapper.Dispose(); + } + + public void Hide() + { + } + + public void Invalidate(Rect rect) + { + //Paint?.Invoke(rect); + } + + public Point PointToClient(Point point) + { + return point; + } + + public Point PointToScreen(Point point) + { + return point; + } + + public void Resize(Size clientSize) + { + // This is where we size the window accordingly.. + } + + public void SetCursor(IPlatformHandle cursor) + { + } + + public void SetIcon(IWindowIconImpl icon) + { + } + + public void SetInputRoot(IInputRoot inputRoot) + { + } + + public void SetMinMaxSize(Size minSize, Size maxSize) + { + } + + public void SetSystemDecorations(bool enabled) + { + } + + public void SetTitle(string title) + { + _windowWrapper.SetTitle(title); + } + + public void SetTopmost(bool value) + { + } + + public void Show() + { + } + + public IDisposable ShowDialog() + { + return null; + } + + public void ShowTaskbarIcon(bool value) + { + } + } +} diff --git a/src/Avalonia.Windowing/WindowingPlatformExtensions.cs b/src/Avalonia.Windowing/WindowingPlatformExtensions.cs new file mode 100644 index 0000000000..2307adbeca --- /dev/null +++ b/src/Avalonia.Windowing/WindowingPlatformExtensions.cs @@ -0,0 +1,15 @@ +using System; +using Avalonia.Controls; + +namespace Avalonia +{ + public static class WindowingPlatformExtensions + { + public static T UseWinit(this T builder, bool? useDeferredRendering = null) where T : AppBuilderBase, new() + { + // if (useDeferredRendering.HasValue) + // MonoMac.MonoMacPlatform.UseDeferredRendering = useDeferredRendering.Value; + return builder.UseWindowingSubsystem(Windowing.WindowingPlatform.Initialize, "Winit"); + } + } +} \ No newline at end of file