Răsfoiți Sursa

Merge branch 'master' into features/keyboard-input

danwalmsley 7 ani în urmă
părinte
comite
cd414a735c

+ 12 - 12
src/Avalonia.Native.OSX/SystemDialogs.mm

@@ -52,8 +52,8 @@ class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemD
                         
                         if(parentWindowHandle != nullptr)
                         {
-                            auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle);
-                            [windowBase->Window makeKeyAndOrderFront:windowBase->Window];
+                            auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                            [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
                         }
                         
                         return;
@@ -66,9 +66,9 @@ class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemD
             
             if(parentWindowHandle != nullptr)
             {
-                auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle);
+                auto windowBase = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
                 
-                [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler];
+                [panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler];
             }
             else
             {
@@ -144,8 +144,8 @@ class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemD
                         
                         if(parentWindowHandle != nullptr)
                         {
-                            auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle);
-                            [windowBase->Window makeKeyAndOrderFront:windowBase->Window];
+                            auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                            [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
                         }
                         
                         return;
@@ -158,9 +158,9 @@ class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemD
             
             if(parentWindowHandle != nullptr)
             {
-                auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle);
+                auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
                 
-                [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler];
+                [panel beginSheetModalForWindow:windowHolder->GetNSWindow() completionHandler:handler];
             }
             else
             {
@@ -225,8 +225,8 @@ class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemD
                     
                     if(parentWindowHandle != nullptr)
                     {
-                        auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle);
-                        [windowBase->Window makeKeyAndOrderFront:windowBase->Window];
+                        auto windowHolder = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
+                        [windowHolder->GetNSWindow() makeKeyAndOrderFront:windowHolder->GetNSWindow()];
                     }
                     
                     return;
@@ -238,9 +238,9 @@ class SystemDialogs : public ComSingleObject<IAvnSystemDialogs, &IID_IAvnSystemD
             
             if(parentWindowHandle != nullptr)
             {
-                auto windowBase = dynamic_cast<WindowBaseImpl*>(parentWindowHandle);
+                auto windowBase = dynamic_cast<INSWindowHolder*>(parentWindowHandle);
                 
-                [panel beginSheetModalForWindow:windowBase->Window completionHandler:handler];
+                [panel beginSheetModalForWindow:windowBase->GetNSWindow() completionHandler:handler];
             }
             else
             {

+ 3 - 154
src/Avalonia.Native.OSX/window.h

@@ -15,6 +15,7 @@ class WindowBaseImpl;
 -(AvnView*) initWithParent: (WindowBaseImpl*) parent;
 -(NSEvent*) lastMouseDownEvent;
 -(AvnPoint) translateLocalPoint:(AvnPoint)pt;
+-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose;
 @end
 
 @interface AvnWindow : NSWindow <NSWindowDelegate>
@@ -22,161 +23,9 @@ class WindowBaseImpl;
 -(void) setCanBecomeKeyAndMain;
 @end
 
-class WindowBaseImpl : public ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>
+struct INSWindowHolder
 {
-public:
-    AvnView* View;
-    AvnWindow* Window;
-    ComPtr<IAvnWindowBaseEvents> BaseEvents;
-    AvnPoint lastPositionSet;
-    WindowBaseImpl(IAvnWindowBaseEvents* events)
-    {
-        BaseEvents = events;
-        View = [[AvnView alloc] initWithParent:this];
-        Window = [[AvnWindow alloc] initWithParent:this];
-        
-        lastPositionSet.X = 100;
-        lastPositionSet.Y = 100;
-        
-        [Window setStyleMask:NSWindowStyleMaskBorderless];
-        [Window setBackingType:NSBackingStoreBuffered];
-        [Window setContentView: View];
-    }
-    
-    virtual HRESULT Show()
-    {
-        SetPosition(lastPositionSet);
-        UpdateStyle();
-        [Window makeKeyAndOrderFront:Window];
-        return S_OK;
-    }
-    
-    virtual HRESULT Hide ()
-    {
-        if(Window != nullptr)
-        {
-            [Window orderOut:Window];
-        }
-        return S_OK;
-    }
-    
-    virtual HRESULT Close()
-    {
-        [Window close];
-        return S_OK;
-    }
-    
-    virtual HRESULT GetClientSize(AvnSize* ret)
-    {
-        if(ret == nullptr)
-            return E_POINTER;
-        auto frame = [View frame];
-        ret->Width = frame.size.width;
-        ret->Height = frame.size.height;
-        return S_OK;
-    }
-    
-    virtual HRESULT GetScaling (double* ret)
-    {
-        if(ret == nullptr)
-            return E_POINTER;
-        
-        if(Window == nullptr)
-        {
-            *ret = 1;
-            return S_OK;
-        }
-        
-        *ret = [Window backingScaleFactor];
-        return S_OK;
-    }
-    
-    virtual HRESULT Resize(double x, double y)
-    {
-        [Window setContentSize:NSSize{x, y}];
-        return S_OK;
-    }
-    
-    virtual void Invalidate (AvnRect rect)
-    {
-        [View setNeedsDisplayInRect:[View frame]];
-    }
-    
-    virtual void BeginMoveDrag ()
-    {
-        auto lastEvent = [View lastMouseDownEvent];
-        
-        if(lastEvent == nullptr)
-        {
-            return;
-        }
-        
-        [Window performWindowDragWithEvent:lastEvent];
-    }
-    
-    
-    virtual HRESULT GetPosition (AvnPoint* ret)
-    {
-        if(ret == nullptr)
-        {
-            return E_POINTER;
-        }
-        
-        auto frame = [Window frame];
-        
-        ret->X = frame.origin.x;
-        ret->Y = frame.origin.y + frame.size.height;
-        
-        *ret = ConvertPointY(*ret);
-        
-        return S_OK;
-    }
-    
-    virtual void SetPosition (AvnPoint point)
-    {
-        lastPositionSet = point;
-        [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
-    }
-    
-    virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret)
-    {
-        if(ret == nullptr)
-        {
-            return E_POINTER;
-        }
-        
-        point = ConvertPointY(point);
-        auto viewPoint = [Window convertPointFromScreen:ToNSPoint(point)];
-        
-        *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
-        
-        return S_OK;
-    }
-    
-    virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret)
-    {
-        if(ret == nullptr)
-        {
-            return E_POINTER;
-        }
-        
-        auto cocoaViewPoint =  ToNSPoint([View translateLocalPoint:point]);
-        auto cocoaScreenPoint = [Window convertPointToScreen:cocoaViewPoint];
-        *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
-        
-        return S_OK;
-    }
-    
-protected:
-    virtual NSWindowStyleMask GetStyle()
-    {
-        return NSWindowStyleMaskBorderless;
-    }
-    
-    void UpdateStyle()
-    {
-        [Window setStyleMask:GetStyle()];
-    }
+    virtual AvnWindow* GetNSWindow () = 0;
 };
 
 #endif /* window_h */

+ 263 - 11
src/Avalonia.Native.OSX/window.mm

@@ -2,9 +2,202 @@
 #include "window.h"
 #include "KeyTransform.h"
 
+class WindowBaseImpl : public ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
+{
+public:
+    AvnView* View;
+    AvnWindow* Window;
+    ComPtr<IAvnWindowBaseEvents> BaseEvents;
+    AvnPoint lastPositionSet;
+    WindowBaseImpl(IAvnWindowBaseEvents* events)
+    {
+        BaseEvents = events;
+        View = [[AvnView alloc] initWithParent:this];
+        Window = [[AvnWindow alloc] initWithParent:this];
+        
+        lastPositionSet.X = 100;
+        lastPositionSet.Y = 100;
+        
+        [Window setStyleMask:NSWindowStyleMaskBorderless];
+        [Window setBackingType:NSBackingStoreBuffered];
+        [Window setContentView: View];
+    }
+    
+    virtual AvnWindow* GetNSWindow()
+    {
+        return Window;
+    }
+    
+    virtual HRESULT Show()
+    {
+        SetPosition(lastPositionSet);
+        UpdateStyle();
+        [Window makeKeyAndOrderFront:Window];
+        return S_OK;
+    }
+    
+    virtual HRESULT Hide ()
+    {
+        if(Window != nullptr)
+        {
+            [Window orderOut:Window];
+        }
+        return S_OK;
+    }
+    
+    virtual HRESULT SetTopMost (bool value)
+    {
+        [Window setLevel: value ? NSFloatingWindowLevel : NSNormalWindowLevel];
+        
+        return S_OK;
+    }
+    
+    virtual HRESULT Close()
+    {
+        [Window close];
+        return S_OK;
+    }
+    
+    virtual HRESULT GetClientSize(AvnSize* ret)
+    {
+        if(ret == nullptr)
+            return E_POINTER;
+        auto frame = [View frame];
+        ret->Width = frame.size.width;
+        ret->Height = frame.size.height;
+        return S_OK;
+    }
+    
+    virtual HRESULT GetMaxClientSize(AvnSize* ret)
+    {
+        if(ret == nullptr)
+            return E_POINTER;
+        
+        auto size = [NSScreen.screens objectAtIndex:0].frame.size;
+        
+        ret->Height = size.height;
+        ret->Width = size.width;
+        
+        return S_OK;
+    }
+    
+    virtual HRESULT GetScaling (double* ret)
+    {
+        if(ret == nullptr)
+            return E_POINTER;
+        
+        if(Window == nullptr)
+        {
+            *ret = 1;
+            return S_OK;
+        }
+        
+        *ret = [Window backingScaleFactor];
+        return S_OK;
+    }
+    
+    virtual HRESULT Resize(double x, double y)
+    {
+        [Window setContentSize:NSSize{x, y}];
+        return S_OK;
+    }
+    
+    virtual void Invalidate (AvnRect rect)
+    {
+        [View setNeedsDisplayInRect:[View frame]];
+    }
+    
+    virtual void BeginMoveDrag ()
+    {
+        auto lastEvent = [View lastMouseDownEvent];
+        
+        if(lastEvent == nullptr)
+        {
+            return;
+        }
+        
+        [Window performWindowDragWithEvent:lastEvent];
+    }
+    
+    
+    virtual HRESULT GetPosition (AvnPoint* ret)
+    {
+        if(ret == nullptr)
+        {
+            return E_POINTER;
+        }
+        
+        auto frame = [Window frame];
+        
+        ret->X = frame.origin.x;
+        ret->Y = frame.origin.y + frame.size.height;
+        
+        *ret = ConvertPointY(*ret);
+        
+        return S_OK;
+    }
+    
+    virtual void SetPosition (AvnPoint point)
+    {
+        lastPositionSet = point;
+        [Window setFrameTopLeftPoint:ToNSPoint(ConvertPointY(point))];
+    }
+    
+    virtual HRESULT PointToClient (AvnPoint point, AvnPoint* ret)
+    {
+        if(ret == nullptr)
+        {
+            return E_POINTER;
+        }
+        
+        point = ConvertPointY(point);
+        auto viewPoint = [Window convertScreenToBase:ToNSPoint(point)];
+        
+        *ret = [View translateLocalPoint:ToAvnPoint(viewPoint)];
+        
+        return S_OK;
+    }
+    
+    virtual HRESULT PointToScreen (AvnPoint point, AvnPoint* ret)
+    {
+        if(ret == nullptr)
+        {
+            return E_POINTER;
+        }
+        
+        auto cocoaViewPoint =  ToNSPoint([View translateLocalPoint:point]);
+        auto cocoaScreenPoint = [Window convertBaseToScreen:cocoaViewPoint];
+        *ret = ConvertPointY(ToAvnPoint(cocoaScreenPoint));
+        
+        return S_OK;
+    }
+    
+    virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose)
+    {
+        [View setSwRenderedFrame: fb dispose: dispose];
+        return S_OK;
+    }
+    
+protected:
+    virtual NSWindowStyleMask GetStyle()
+    {
+        return NSWindowStyleMaskBorderless;
+    }
+    
+    void UpdateStyle()
+    {
+        [Window setStyleMask:GetStyle()];
+    }
+};
+
+NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEventTrackingRunLoopMode, NSModalPanelRunLoopMode, NSRunLoopCommonModes, NSConnectionReplyMode, nil];
+
 @implementation AvnView
 {
     ComPtr<WindowBaseImpl> _parent;
+    ComPtr<IUnknown> _swRenderedFrame;
+    AvnFramebuffer _swRenderedFrameBuffer;
+    bool _queuedDisplayFromThread;
     NSTrackingArea* _area;
     bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isMouseOver;
     NSEvent* _lastMouseDownEvent;
@@ -49,18 +242,10 @@
     _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
 }
 
-- (void)drawRect:(NSRect)dirtyRect
+- (void) drawFb: (AvnFramebuffer*) fb
 {
-    auto logicalSize = [self frame].size;
-    auto pixelSize = [self convertSizeToBacking:logicalSize];
-    int w = pixelSize.width;
-    int h = pixelSize.height;
-    int stride = w * 4;
-    void*ptr = malloc(h * stride);
-    _parent->BaseEvents->SoftwareDraw(ptr, stride, w, h, AvnSize{logicalSize.width, logicalSize.height});
-    
     auto colorSpace = CGColorSpaceCreateDeviceRGB();
-    auto bctx = CGBitmapContextCreate(ptr, w, h, 8, stride, colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
+    auto bctx = CGBitmapContextCreate(fb->Data, fb->Width, fb->Height, 8, fb->Stride, colorSpace, kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast);
     auto image = CGBitmapContextCreateImage(bctx);
     CGContextRelease(bctx);
     CGColorSpaceRelease(colorSpace);
@@ -70,13 +255,74 @@
     [ctx saveGraphicsState];
     auto cgc = [ctx CGContext];
     
-    CGContextDrawImage(cgc, CGRect{0,0, logicalSize.width, logicalSize.height}, image);
+    CGContextDrawImage(cgc, CGRect{0,0, fb->Width/(fb->Dpi.X/96), fb->Height/(fb->Dpi.Y/96)}, image);
     CGImageRelease(image);
     
     [ctx restoreGraphicsState];
+
+}
+
+- (void)drawRect:(NSRect)dirtyRect
+{
+    _parent->BaseEvents->RunRenderPriorityJobs();
+    @synchronized (self) {
+        if(_swRenderedFrame != NULL)
+        {
+            [self drawFb: &_swRenderedFrameBuffer];
+            return;
+        }
+    }
+    
+    auto logicalSize = [self frame].size;
+    auto pixelSize = [self convertSizeToBacking:logicalSize];
+    int w = pixelSize.width;
+    int h = pixelSize.height;
+    int stride = w * 4;
+    void*ptr = malloc(h * stride);
+    AvnFramebuffer fb = {
+        .Data = ptr,
+        .Stride = stride,
+        .Width = w,
+        .Height = h,
+        .PixelFormat = kAvnRgba8888,
+        .Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96}
+    };
+    _parent->BaseEvents->SoftwareDraw(&fb);
+    [self drawFb: &fb];
     free(ptr);
 }
 
+-(void) redrawSelf
+{
+    @autoreleasepool
+    {
+        @synchronized(self)
+        {
+            if(!_queuedDisplayFromThread)
+                return;
+            _queuedDisplayFromThread = false;
+        }
+        [self setNeedsDisplayInRect:[self frame]];
+        [self display];
+        
+    }
+}
+
+-(void) setSwRenderedFrame: (AvnFramebuffer*) fb dispose: (IUnknown*) dispose
+{
+    @autoreleasepool {
+        @synchronized (self) {
+            _swRenderedFrame = dispose;
+            _swRenderedFrameBuffer = *fb;
+            if(!_queuedDisplayFromThread)
+            {
+                _queuedDisplayFromThread = true;
+                [self performSelector:@selector(redrawSelf) onThread:[NSThread mainThread] withObject:NULL waitUntilDone:false modes: AllLoopModes];
+            }
+        }
+    }
+}
+
 - (AvnPoint) translateLocalPoint:(AvnPoint)pt
 {
     pt.Y = [self bounds].size.height - pt.Y;
@@ -93,6 +339,12 @@
     return result;
 }
 
+- (void) viewDidChangeBackingProperties
+{
+    _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
+    [super viewDidChangeBackingProperties];
+}
+
 - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type
 {
     auto localPoint = [self convertPoint:[event locationInWindow] toView:self];

+ 77 - 0
src/Avalonia.Native/DeferredFramebuffer.cs

@@ -0,0 +1,77 @@
+using System;
+using System.Runtime.InteropServices;
+using Avalonia.Native.Interop;
+using Avalonia.Platform;
+using SharpGen.Runtime;
+
+namespace Avalonia.Native
+{
+    public class DeferredFramebuffer : ILockedFramebuffer
+    {
+        private readonly Func<Action<IAvnWindowBase>, bool> _lockWindow;
+        public DeferredFramebuffer(Func<Action<IAvnWindowBase>, bool> lockWindow,
+                                   int width, int height, Vector dpi)
+        {
+            _lockWindow = lockWindow;
+            Address = Marshal.AllocHGlobal(width * height * 4);
+            Width = width;
+            Height = height;
+            RowBytes = width * 4;
+            Dpi = dpi;
+            Format = PixelFormat.Rgba8888;
+        }
+
+        public IntPtr Address { get; set; }
+        public int Width { get; set; }
+        public int Height { get; set; }
+        public int RowBytes { get; set; }
+        public Vector Dpi { get; set; }
+        public PixelFormat Format { get; set; }
+
+
+        class Disposer : CallbackBase
+        {
+            private IntPtr _ptr;
+
+            public Disposer(IntPtr ptr)
+            {
+                _ptr = ptr;
+            }
+
+            protected override void Destroyed()
+            {
+                if(_ptr != IntPtr.Zero)
+                {
+                    Marshal.FreeHGlobal(_ptr);
+                    _ptr = IntPtr.Zero;
+                }
+            }
+        }
+
+        public void Dispose()
+        {
+            if (Address == IntPtr.Zero)
+                return;
+            if (!_lockWindow(win =>
+            {
+                var fb = new AvnFramebuffer
+                {
+                    Data = Address,
+                    Dpi = new AvnVector
+                    {
+                        X = Dpi.X,
+                        Y = Dpi.Y
+                    },
+                    Width = Width,
+                    Height = Height,
+                    PixelFormat = (AvnPixelFormat)Format,
+                    Stride = RowBytes
+                };
+                using (var d = new Disposer(Address))
+                    win.ThreadSafeSetSwRenderedFrame(ref fb, d);
+            }))
+                Marshal.FreeHGlobal(Address);
+            Address = IntPtr.Zero;
+        }
+    }
+}

+ 78 - 0
src/Avalonia.Native/DeferredRendererProxy.cs

@@ -0,0 +1,78 @@
+using System;
+using System.Collections.Generic;
+using Avalonia.Platform;
+using Avalonia.Rendering;
+using Avalonia.VisualTree;
+
+namespace Avalonia.Native
+{
+    public class DeferredRendererProxy : IRenderer, IRenderLoopTask, IRenderLoop
+    {
+        object _lock = new object();
+        void IRenderLoop.Add(IRenderLoopTask i)
+        {
+            AvaloniaLocator.Current.GetService<IRenderLoop>().Add(this);
+        }
+
+        void IRenderLoop.Remove(IRenderLoopTask i)
+        {
+            AvaloniaLocator.Current.GetService<IRenderLoop>().Remove(this);
+        }
+
+
+        DeferredRenderer _renderer;
+        IRenderLoopTask _rendererTask;
+        public DeferredRendererProxy(IRenderRoot root)
+        {
+            _renderer = new DeferredRenderer(root, this);
+            _rendererTask = (IRenderLoopTask)_renderer;
+        }
+
+        public bool DrawFps{
+            get => _renderer.DrawFps;
+            set => _renderer.DrawFps = value;
+        }
+        public bool DrawDirtyRects 
+        {
+            get => _renderer.DrawDirtyRects;
+            set => _renderer.DrawDirtyRects = value;
+        }
+
+        public bool NeedsUpdate => _rendererTask.NeedsUpdate;
+
+        public void AddDirty(IVisual visual) => _renderer.AddDirty(visual);
+
+        public void Dispose() => _renderer.Dispose();
+
+        public IEnumerable<IVisual> HitTest(Point p, IVisual root, Func<IVisual, bool> filter)
+        {
+            return _renderer.HitTest(p, root, filter);
+        }
+
+        public void Paint(Rect rect)
+        {
+            if (NeedsUpdate)
+                Update(TimeSpan.FromMilliseconds(Environment.TickCount));
+            Render();
+        }
+
+        public void Resized(Size size) => _renderer.Resized(size);
+
+        public void Start() => _renderer.Start();
+
+        public void Stop() => _renderer.Stop();
+
+        public void Update(TimeSpan time)
+        {
+            _rendererTask.Update(time);
+        }
+
+        public void Render()
+        {
+            lock(_lock)
+            {
+                _rendererTask.Render();
+            }
+        }
+    }
+}

+ 5 - 0
src/Avalonia.Native/Helpers.cs

@@ -14,5 +14,10 @@ namespace Avalonia.Native
         {
             return new AvnPoint { X = pt.X, Y = pt.Y };
         }
+
+        public static Size ToAvaloniaSize (this AvnSize size)
+        {
+            return new Size(size.Width, size.Height);
+        }
     }
 }

+ 72 - 23
src/Avalonia.Native/WindowImplBase.cs

@@ -15,10 +15,12 @@ namespace Avalonia.Native
     {
         IInputRoot _inputRoot;
         IAvnWindowBase _native;
-
-        private bool _deferredRendering = false;
+        private object _syncRoot = new object();
+        private bool _deferredRendering = true;
         private readonly IMouseDevice _mouse;
         private readonly IKeyboardDevice _keyboard;
+        private Size _savedLogicalSize;
+        private double _savedScaling;
 
         public WindowBaseImpl()
         {
@@ -29,6 +31,8 @@ namespace Avalonia.Native
         protected void Init(IAvnWindowBase window)
         {
             _native = window;
+            _savedLogicalSize = ClientSize;
+            _savedScaling = Scaling;
         }
 
         public Size ClientSize 
@@ -44,7 +48,28 @@ namespace Avalonia.Native
         public IEnumerable<object> Surfaces => new[] { this };
         public ILockedFramebuffer Lock()
         {
-            return _framebuffer;
+            if(_deferredRendering)
+            {
+                var w = _savedLogicalSize.Width * _savedScaling;
+                var h = _savedLogicalSize.Height * _savedScaling;
+                var dpi = _savedScaling * 96;
+                return new DeferredFramebuffer(cb =>
+                {
+                    lock (_syncRoot)
+                    {
+                        if (_native == null)
+                            return false;
+                        cb(_native);
+                        return true;
+                    }
+                }, (int)w, (int)h, new Vector(dpi, dpi));
+            }
+
+            var fb = _framebuffer;
+            _framebuffer = null;
+            if (fb == null)
+                throw new InvalidOperationException("Lock call without corresponding Paint event");
+            return fb;
         }
 
         public Action<Rect> Paint { get; set; }
@@ -82,26 +107,31 @@ namespace Avalonia.Native
 
             void IAvnWindowBaseEvents.Deactivated() => _parent.Deactivated?.Invoke();
 
-            void IAvnWindowBaseEvents.SoftwareDraw(IntPtr ptr, int stride, int pixelWidth, int pixelHeight, AvnSize logicalSize)
+            void IAvnWindowBaseEvents.SoftwareDraw(ref AvnFramebuffer fb)
             {
                 Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
 
                 _parent._framebuffer = new SavedFramebuffer
                 {
-                    Address = ptr,
-                    RowBytes = stride,
-                    Width = pixelWidth,
-                    Height = pixelHeight,
-                    Dpi = new Vector(pixelWidth / logicalSize.Width * 96, pixelHeight / logicalSize.Height * 96)
+                    Address = fb.Data,
+                    RowBytes = fb.Stride,
+                    Width = fb.Width,
+                    Height = fb.Height,
+                    Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y)
                 };
 
-                _parent.Paint?.Invoke(new Rect(0, 0, logicalSize.Width, logicalSize.Height));
+                _parent.Paint?.Invoke(new Rect(0, 0, fb.Width / (fb.Dpi.X / 96), fb.Height / (fb.Dpi.Y / 96)));
 
             }
 
-            void IAvnWindowBaseEvents.Resized(AvnSize size) => _parent.Resized?.Invoke(new Size(size.Width, size.Height));
+            void IAvnWindowBaseEvents.Resized(AvnSize size)
+            {
+                var s = new Size(size.Width, size.Height);
+                _parent._savedLogicalSize = s;
+                _parent.Resized?.Invoke(s);
+            }
 
-            public void RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
+            void IAvnWindowBaseEvents.RawMouseEvent(AvnRawMouseEventType type, uint timeStamp, AvnInputModifiers modifiers, AvnPoint point, AvnVector delta)
             {
                 _parent.RawMouseEvent(type, timeStamp, modifiers, point, delta);
             }
@@ -110,8 +140,21 @@ namespace Avalonia.Native
             {
                 return _parent.RawKeyEvent(type, timeStamp, modifiers, key);
             }
-        }
+            
+            void IAvnWindowBaseEvents.ScalingChanged(double scaling)
+            {
+                _parent._savedScaling = scaling;
+                _parent.ScalingChanged?.Invoke(scaling);
+            }
 
+            void IAvnWindowBaseEvents.RunRenderPriorityJobs()
+            {
+                if (_parent._deferredRendering)
+                    // Hack to trigger Paint event on the renderer
+                    _parent.Paint?.Invoke(new Rect());
+                Dispatcher.UIThread.RunJobs(DispatcherPriority.Render);
+            }
+        }
 
         public void Activate()
         {
@@ -150,8 +193,8 @@ namespace Avalonia.Native
 
         public IRenderer CreateRenderer(IRenderRoot root)
         {
-            //_deferredRendering = true;
-            //return new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
+            if(_deferredRendering)
+                return new DeferredRendererProxy(root);
             return new ImmediateRenderer(root);
         }
 
@@ -165,7 +208,8 @@ namespace Avalonia.Native
 
         public void Invalidate(Rect rect)
         {
-            _native.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y });
+            if (!_deferredRendering)
+                _native.Invalidate(new AvnRect { Height = rect.Height, Width = rect.Width, X = rect.X, Y = rect.Y });
         }
 
         public void SetInputRoot(IInputRoot inputRoot)
@@ -206,27 +250,32 @@ namespace Avalonia.Native
             _native.BeginMoveDrag();
         }
 
-        #region Stubs
+        public Size MaxClientSize => _native.GetMaxClientSize().ToAvaloniaSize();
+
+        public void SetTopmost(bool value)
+        {
+            _native.SetTopMost(value);
+        }
+
         public double Scaling => _native.GetScaling();
 
-        public Action<Point> PositionChanged { get; set; }
         public Action Deactivated { get; set; }
         public Action Activated { get; set; }
+
+        #region Stubs
+
+        public Action<Point> PositionChanged { get; set; }
+
         public Action<RawInputEventArgs> Input { get; set; }
 
         Action<double> ScalingChanged { get; set; }
         public IPlatformHandle Handle => new PlatformHandle(IntPtr.Zero, "NOT SUPPORTED");
 
-        public Size MaxClientSize => new Size(1600, 900);
 
         public IScreenImpl Screen => new ScreenImpl();
 
         Action<double> ITopLevelImpl.ScalingChanged { get; set; }
 
-        public void SetTopmost(bool value)
-        {
-        }
-
         public void SetMinMaxSize(Size minSize, Size maxSize)
         {
         }

+ 23 - 2
src/headers/avalonia-native.h

@@ -31,6 +31,23 @@ struct AvnPoint
     double X, Y;
 };
 
+enum AvnPixelFormat
+{
+    kAvnRgb565,
+    kAvnRgba8888,
+    kAvnBgra8888
+};
+
+struct AvnFramebuffer
+{
+    void* Data;
+    int Width;
+    int Height;
+    int Stride;
+    AvnVector Dpi;
+    AvnPixelFormat PixelFormat;
+};
+
 enum AvnRawMouseEventType
 {
     LeaveWindow,
@@ -80,6 +97,7 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown
     virtual HRESULT Hide () = 0;
     virtual HRESULT Close() = 0;
     virtual HRESULT GetClientSize(AvnSize*ret) = 0;
+    virtual HRESULT GetMaxClientSize(AvnSize* ret) = 0;
     virtual HRESULT GetScaling(double*ret)=0;
     virtual HRESULT Resize(double width, double height) = 0;
     virtual void Invalidate (AvnRect rect) = 0;
@@ -88,6 +106,8 @@ AVNCOM(IAvnWindowBase, 02) : virtual IUnknown
     virtual void SetPosition (AvnPoint point) = 0;
     virtual HRESULT PointToClient (AvnPoint point, AvnPoint*ret) = 0;
     virtual HRESULT PointToScreen (AvnPoint point, AvnPoint*ret) = 0;
+    virtual HRESULT ThreadSafeSetSwRenderedFrame(AvnFramebuffer* fb, IUnknown* dispose) = 0;
+    virtual HRESULT SetTopMost (bool value) = 0;
 };
 
 AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
@@ -103,7 +123,7 @@ AVNCOM(IAvnWindow, 04) : virtual IAvnWindowBase
 
 AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
 {
-    virtual HRESULT SoftwareDraw(void* ptr, int stride, int pixelWidth, int pixelHeight, const AvnSize& logicalSize) = 0;
+    virtual HRESULT SoftwareDraw(AvnFramebuffer* fb) = 0;
     virtual void Closed() = 0;
     virtual void Activated() = 0;
     virtual void Deactivated() = 0;
@@ -113,8 +133,9 @@ AVNCOM(IAvnWindowBaseEvents, 05) : IUnknown
                                 AvnInputModifiers modifiers,
                                 AvnPoint point,
                                 AvnVector delta) = 0;
-    
     virtual bool RawKeyEvent (AvnRawKeyEventType type, unsigned int timeStamp, AvnInputModifiers modifiers, unsigned int key) = 0;
+    virtual void ScalingChanged(double scaling) = 0;
+    virtual void RunRenderPriorityJobs() = 0;
 };