浏览代码

Merge branch 'master' into refactor/remove-property-initialized

Dariusz Komosiński 5 年之前
父节点
当前提交
779285698a
共有 42 个文件被更改,包括 943 次插入705 次删除
  1. 1 1
      build/SharedVersion.props
  2. 11 17
      native/Avalonia.Native/inc/avalonia-native.h
  3. 13 0
      native/Avalonia.Native/inc/comimpl.h
  4. 12 0
      native/Avalonia.Native/inc/rendertarget.h
  5. 16 4
      native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj
  6. 166 0
      native/Avalonia.Native/src/OSX/cgl.mm
  7. 3 4
      native/Avalonia.Native/src/OSX/common.h
  8. 0 261
      native/Avalonia.Native/src/OSX/gl.mm
  9. 6 6
      native/Avalonia.Native/src/OSX/main.mm
  10. 284 0
      native/Avalonia.Native/src/OSX/rendertarget.mm
  11. 50 157
      native/Avalonia.Native/src/OSX/window.mm
  12. 1 1
      readme.md
  13. 7 2
      src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs
  14. 2 2
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  15. 10 7
      src/Avalonia.Controls.DataGrid/Themes/Default.xaml
  16. 1 1
      src/Avalonia.Controls/AutoCompleteBox.cs
  17. 6 6
      src/Avalonia.Controls/Calendar/CalendarItem.cs
  18. 1 1
      src/Avalonia.Controls/Calendar/DatePicker.cs
  19. 2 2
      src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs
  20. 173 110
      src/Avalonia.Controls/Primitives/Popup.cs
  21. 4 4
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  22. 2 2
      src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs
  23. 16 0
      src/Avalonia.Controls/ScrollViewer.cs
  24. 3 3
      src/Avalonia.Controls/SelectionChangedEventArgs.cs
  25. 2 2
      src/Avalonia.Controls/TreeView.cs
  26. 1 1
      src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml
  27. 0 42
      src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs
  28. 7 3
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  29. 1 1
      src/Avalonia.Native/DeferredFramebuffer.cs
  30. 21 10
      src/Avalonia.Native/GlPlatformFeature.cs
  31. 8 4
      src/Avalonia.Native/PopupImpl.cs
  32. 7 3
      src/Avalonia.Native/WindowImpl.cs
  33. 17 46
      src/Avalonia.Native/WindowImplBase.cs
  34. 1 0
      src/Avalonia.OpenGL/EglGlPlatformSurface.cs
  35. 1 0
      src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs
  36. 1 0
      src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs
  37. 2 0
      src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs
  38. 1 1
      src/Skia/Avalonia.Skia/GlRenderTarget.cs
  39. 1 0
      src/Skia/Avalonia.Skia/PlatformRenderInterface.cs
  40. 31 0
      tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs
  41. 27 1
      tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs
  42. 24 0
      tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

+ 1 - 1
build/SharedVersion.props

@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Product>Avalonia</Product>
     <Version>0.9.999</Version>
-    <Copyright>Copyright 2019 &#169; The AvaloniaUI Project</Copyright>
+    <Copyright>Copyright 2020 &#169; The AvaloniaUI Project</Copyright>
     <PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl>
     <RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>

+ 11 - 17
native/Avalonia.Native/inc/avalonia-native.h

@@ -177,14 +177,14 @@ AVNCOM(IAvaloniaNativeFactory, 01) : IUnknown
 public:
     virtual HRESULT Initialize() = 0;
     virtual IAvnMacOptions* GetMacOptions() = 0;
-    virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv) = 0;
-    virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnPopup** ppv) = 0;
+    virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv) = 0;
+    virtual HRESULT CreatePopup (IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) = 0;
     virtual HRESULT CreatePlatformThreadingInterface(IAvnPlatformThreadingInterface** ppv) = 0;
     virtual HRESULT CreateSystemDialogs (IAvnSystemDialogs** ppv) = 0;
     virtual HRESULT CreateScreens (IAvnScreens** ppv) = 0;
     virtual HRESULT CreateClipboard(IAvnClipboard** ppv) = 0;
     virtual HRESULT CreateCursorFactory(IAvnCursorFactory** ppv) = 0;
-    virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) = 0;
+    virtual HRESULT ObtainGlDisplay(IAvnGlDisplay** ppv) = 0;
     virtual HRESULT ObtainAppMenu(IAvnAppMenu** retOut) = 0;
     virtual HRESULT SetAppMenu(IAvnAppMenu* menu) = 0;
     virtual HRESULT CreateMenu (IAvnAppMenu** ppv) = 0;
@@ -219,15 +219,12 @@ AVNCOM(IAvnWindowBase, 02) : IUnknown
     virtual HRESULT SetTopMost (bool value) = 0;
     virtual HRESULT SetCursor(IAvnCursor* cursor) = 0;
     virtual HRESULT CreateGlRenderTarget(IAvnGlSurfaceRenderTarget** ret) = 0;
-    virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) = 0;
     virtual HRESULT SetMainMenu(IAvnAppMenu* menu) = 0;
     virtual HRESULT ObtainMainMenu(IAvnAppMenu** retOut) = 0;
     virtual HRESULT ObtainNSWindowHandle(void** retOut) = 0;
     virtual HRESULT ObtainNSWindowHandleRetained(void** retOut) = 0;
     virtual HRESULT ObtainNSViewHandle(void** retOut) = 0;
     virtual HRESULT ObtainNSViewHandleRetained(void** retOut) = 0;
-    virtual bool TryLock() = 0;
-    virtual void Unlock() = 0;
 };
 
 AVNCOM(IAvnPopup, 03) : virtual IAvnWindowBase
@@ -360,24 +357,21 @@ AVNCOM(IAvnCursorFactory, 11) : IUnknown
     virtual HRESULT GetCursor (AvnStandardCursorType cursorType, IAvnCursor** retOut) = 0;
 };
 
-
-AVNCOM(IAvnGlFeature, 12) : IUnknown
-{
-    virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut) = 0;
-    virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut) = 0;
-};
-
 AVNCOM(IAvnGlDisplay, 13) : IUnknown
 {
-    virtual HRESULT GetSampleCount(int* ret) = 0;
-    virtual HRESULT GetStencilSize(int* ret) = 0;
-    virtual HRESULT ClearContext() = 0;
+    virtual HRESULT CreateContext(IAvnGlContext* share, IAvnGlContext**ppv) = 0;
+    virtual void LegacyClearCurrentContext() = 0;
+    virtual HRESULT WrapContext(void* native, IAvnGlContext**ppv) = 0;
     virtual void* GetProcAddress(char* proc) = 0;
 };
 
 AVNCOM(IAvnGlContext, 14) : IUnknown
 {
-    virtual HRESULT MakeCurrent() = 0;
+    virtual HRESULT MakeCurrent(IUnknown** ppv) = 0;
+    virtual HRESULT LegacyMakeCurrent() = 0;
+    virtual int GetSampleCount() = 0;
+    virtual int GetStencilSize() = 0;
+    virtual void* GetNativeHandle() = 0;
 };
 
 AVNCOM(IAvnGlSurfaceRenderTarget, 15) : IUnknown

+ 13 - 0
native/Avalonia.Native/inc/comimpl.h

@@ -162,6 +162,19 @@ public:
         return _obj;
     }
     
+    TInterface* getRetainedReference()
+    {
+        if(_obj == NULL)
+            return NULL;
+        _obj->AddRef();
+        return _obj;
+    }
+    
+    TInterface** getPPV()
+    {
+        return &_obj;
+    }
+    
     operator TInterface*() const
     {
         return _obj;

+ 12 - 0
native/Avalonia.Native/inc/rendertarget.h

@@ -0,0 +1,12 @@
+
+@protocol IRenderTarget
+-(void) setNewLayer: (CALayer*) layer;
+-(HRESULT) setSwFrame: (AvnFramebuffer*) fb;
+-(void) resize: (AvnPixelSize) size withScale: (float) scale;
+-(AvnPixelSize) pixelSize;
+-(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget;
+@end
+
+@interface IOSurfaceRenderTarget : NSObject<IRenderTarget>
+-(IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context;
+@end

+ 16 - 4
native/Avalonia.Native/src/OSX/Avalonia.Native.OSX.xcodeproj/project.pbxproj

@@ -8,6 +8,10 @@
 
 /* Begin PBXBuildFile section */
 		1A002B9E232135EE00021753 /* app.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A002B9D232135EE00021753 /* app.mm */; };
+		1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */; };
+		1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */; };
+		1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1A3E5EAD23E9FB1300EDE661 /* cgl.mm */; };
+		1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */; };
 		37155CE4233C00EB0034DCE9 /* menu.h in Headers */ = {isa = PBXBuildFile; fileRef = 37155CE3233C00EB0034DCE9 /* menu.h */; };
 		37A517B32159597E00FBA241 /* Screens.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37A517B22159597E00FBA241 /* Screens.mm */; };
 		37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */ = {isa = PBXBuildFile; fileRef = 37C09D8721580FE4006A6758 /* SystemDialogs.mm */; };
@@ -18,7 +22,6 @@
 		5B8BD94F215BFEA6005ED2A7 /* clipboard.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */; };
 		AB00E4F72147CA920032A60A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB00E4F62147CA920032A60A /* main.mm */; };
 		AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB1E522B217613570091CD71 /* OpenGL.framework */; };
-		AB573DC4217605E400D389A2 /* gl.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB573DC3217605E400D389A2 /* gl.mm */; };
 		AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AB661C1D2148230F00291242 /* AppKit.framework */; };
 		AB661C202148286E00291242 /* window.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB661C1F2148286E00291242 /* window.mm */; };
 		AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */ = {isa = PBXBuildFile; fileRef = AB8F7D6A21482D7F0057DBA5 /* platformthreading.mm */; };
@@ -26,6 +29,10 @@
 
 /* Begin PBXFileReference section */
 		1A002B9D232135EE00021753 /* app.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = app.mm; sourceTree = "<group>"; };
+		1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = rendertarget.mm; sourceTree = "<group>"; };
+		1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOSurface.framework; path = System/Library/Frameworks/IOSurface.framework; sourceTree = SDKROOT; };
+		1A3E5EAD23E9FB1300EDE661 /* cgl.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = cgl.mm; sourceTree = "<group>"; };
+		1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		37155CE3233C00EB0034DCE9 /* menu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = menu.h; sourceTree = "<group>"; };
 		379860FE214DA0C000CD0246 /* KeyTransform.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KeyTransform.h; sourceTree = "<group>"; };
 		37A4E71A2178846A00EACBCD /* headers */ = {isa = PBXFileReference; lastKnownFileType = folder; name = headers; path = ../../inc; sourceTree = "<group>"; };
@@ -41,7 +48,6 @@
 		5BF943652167AD1D009CAE35 /* cursor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cursor.h; sourceTree = "<group>"; };
 		AB00E4F62147CA920032A60A /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
 		AB1E522B217613570091CD71 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = System/Library/Frameworks/OpenGL.framework; sourceTree = SDKROOT; };
-		AB573DC3217605E400D389A2 /* gl.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = gl.mm; sourceTree = "<group>"; };
 		AB661C1D2148230F00291242 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
 		AB661C1F2148286E00291242 /* window.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = window.mm; sourceTree = "<group>"; };
 		AB661C212148288600291242 /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = common.h; sourceTree = "<group>"; };
@@ -54,6 +60,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				1A3E5EB023E9FE8300EDE661 /* QuartzCore.framework in Frameworks */,
+				1A3E5EAA23E9F26C00EDE661 /* IOSurface.framework in Frameworks */,
 				AB1E522C217613570091CD71 /* OpenGL.framework in Frameworks */,
 				AB661C1E2148230F00291242 /* AppKit.framework in Frameworks */,
 			);
@@ -65,6 +73,8 @@
 		AB661C1C2148230E00291242 /* Frameworks */ = {
 			isa = PBXGroup;
 			children = (
+				1A3E5EAF23E9FE8300EDE661 /* QuartzCore.framework */,
+				1A3E5EA923E9F26C00EDE661 /* IOSurface.framework */,
 				AB1E522B217613570091CD71 /* OpenGL.framework */,
 				AB661C1D2148230F00291242 /* AppKit.framework */,
 			);
@@ -78,7 +88,7 @@
 				37DDA9B121933371002E132B /* AvnString.h */,
 				37DDA9AF219330F8002E132B /* AvnString.mm */,
 				37A4E71A2178846A00EACBCD /* headers */,
-				AB573DC3217605E400D389A2 /* gl.mm */,
+				1A3E5EAD23E9FB1300EDE661 /* cgl.mm */,
 				5BF943652167AD1D009CAE35 /* cursor.h */,
 				5B21A981216530F500CEE36E /* cursor.mm */,
 				5B8BD94E215BFEA6005ED2A7 /* clipboard.mm */,
@@ -91,6 +101,7 @@
 				AB00E4F62147CA920032A60A /* main.mm */,
 				37155CE3233C00EB0034DCE9 /* menu.h */,
 				520624B222973F4100C4DCEF /* menu.mm */,
+				1A3E5EA723E9E83B00EDE661 /* rendertarget.mm */,
 				37A517B22159597E00FBA241 /* Screens.mm */,
 				37C09D8721580FE4006A6758 /* SystemDialogs.mm */,
 				AB7A61F02147C815003C5833 /* Products */,
@@ -180,12 +191,13 @@
 				5B21A982216530F500CEE36E /* cursor.mm in Sources */,
 				37DDA9B0219330F8002E132B /* AvnString.mm in Sources */,
 				AB8F7D6B21482D7F0057DBA5 /* platformthreading.mm in Sources */,
+				1A3E5EA823E9E83B00EDE661 /* rendertarget.mm in Sources */,
+				1A3E5EAE23E9FB1300EDE661 /* cgl.mm in Sources */,
 				37E2330F21583241000CB7E2 /* KeyTransform.mm in Sources */,
 				520624B322973F4100C4DCEF /* menu.mm in Sources */,
 				37A517B32159597E00FBA241 /* Screens.mm in Sources */,
 				AB00E4F72147CA920032A60A /* main.mm in Sources */,
 				37C09D8821580FE4006A6758 /* SystemDialogs.mm in Sources */,
-				AB573DC4217605E400D389A2 /* gl.mm in Sources */,
 				AB661C202148286E00291242 /* window.mm in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 166 - 0
native/Avalonia.Native/src/OSX/cgl.mm

@@ -0,0 +1,166 @@
+#include "common.h"
+#include <dlfcn.h>
+
+static CGLContextObj CreateCglContext(CGLContextObj share)
+{
+    int attributes[] = {
+        kCGLPFAAccelerated,
+        kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core,
+        kCGLPFADepthSize, 8,
+        kCGLPFAStencilSize, 8,
+        kCGLPFAColorSize, 32,
+        0
+    };
+    
+    CGLPixelFormatObj pix;
+    CGLError errorCode;
+    GLint num; // stores the number of possible pixel formats
+    errorCode = CGLChoosePixelFormat( (CGLPixelFormatAttribute*)attributes, &pix, &num );
+    if(errorCode != 0)
+        return nil;
+    CGLContextObj ctx = nil;
+    errorCode = CGLCreateContext(pix, share, &ctx );
+    CGLDestroyPixelFormat( pix );
+    if(errorCode != 0)
+        return nil;
+    return ctx;
+};
+
+
+
+class AvnGlContext : public virtual ComSingleObject<IAvnGlContext, &IID_IAvnGlContext>
+{
+    // Debug
+    int _usageCount = 0;
+public:
+    CGLContextObj Context;
+    int SampleCount = 0, StencilBits = 0;
+    FORWARD_IUNKNOWN()
+    
+    class SavedGlContext : public virtual ComUnknownObject
+    {
+        CGLContextObj _savedContext;
+        ComPtr<AvnGlContext> _parent;
+    public:
+        SavedGlContext(CGLContextObj saved, AvnGlContext* parent)
+        {
+            _savedContext = saved;
+            _parent = parent;
+            _parent->_usageCount++;
+        }
+        
+        ~SavedGlContext()
+        {
+            if(_parent->Context == CGLGetCurrentContext())
+                CGLSetCurrentContext(_savedContext);
+            _parent->_usageCount--;
+            CGLUnlockContext(_parent->Context);
+        }
+    };
+    
+    AvnGlContext(CGLContextObj context)
+    {
+        Context = context;
+        CGLPixelFormatObj fmt = CGLGetPixelFormat(context);
+        CGLDescribePixelFormat(fmt, 0, kCGLPFASamples, &SampleCount);
+        CGLDescribePixelFormat(fmt, 0, kCGLPFAStencilSize, &StencilBits);
+        
+    }
+    
+    virtual HRESULT LegacyMakeCurrent() override
+    {
+        if(CGLSetCurrentContext(Context) != 0)
+            return E_FAIL;
+        return S_OK;
+    }
+    
+    virtual HRESULT MakeCurrent(IUnknown** ppv) override
+    {
+        CGLContextObj saved = CGLGetCurrentContext();
+        CGLLockContext(Context);
+        if(CGLSetCurrentContext(Context) != 0)
+        {
+            CGLUnlockContext(Context);
+            return E_FAIL;
+        }
+        *ppv = new SavedGlContext(saved, this);
+        
+        return S_OK;
+    }
+    
+    virtual int GetSampleCount() override
+    {
+        return SampleCount;
+    }
+    
+    virtual int GetStencilSize() override
+    {
+        return StencilBits;
+    }
+    
+    virtual void* GetNativeHandle() override
+    {
+        return Context;
+    }
+    
+    ~AvnGlContext()
+    {
+        CGLReleaseContext(Context);
+    }
+};
+
+class AvnGlDisplay : public virtual ComSingleObject<IAvnGlDisplay, &IID_IAvnGlDisplay>
+{
+    void* _libgl;
+    
+public:
+    FORWARD_IUNKNOWN()
+    
+    AvnGlDisplay()
+    {
+        _libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", RTLD_LAZY);
+    }
+    
+    virtual void* GetProcAddress(char* proc)  override
+    {
+        return dlsym(_libgl, proc);
+    }
+    
+    virtual HRESULT CreateContext(IAvnGlContext* share, IAvnGlContext**ppv) override
+    {
+        CGLContextObj shareContext = nil;
+        if(share != nil)
+        {
+            AvnGlContext* shareCtx = dynamic_cast<AvnGlContext*>(share);
+            if(shareCtx != nil)
+                shareContext = shareCtx->Context;
+        }
+        CGLContextObj ctx = ::CreateCglContext(shareContext);
+        if(ctx == nil)
+            return E_FAIL;
+        *ppv = new AvnGlContext(ctx);
+        return S_OK;
+    }
+    
+    virtual HRESULT WrapContext(void* native, IAvnGlContext**ppv) override
+    {
+        if(native == nil)
+            return E_INVALIDARG;
+        *ppv = new AvnGlContext((CGLContextObj) native);
+        return S_OK;
+    }
+    
+    virtual void LegacyClearCurrentContext() override
+    {
+        CGLSetCurrentContext(nil);
+    }
+};
+
+static IAvnGlDisplay* GlDisplay = new AvnGlDisplay();
+
+
+extern IAvnGlDisplay* GetGlDisplay()
+{
+    return GlDisplay;
+};
+

+ 3 - 4
native/Avalonia.Native/src/OSX/common.h

@@ -11,14 +11,13 @@
 #include <pthread.h>
 
 extern IAvnPlatformThreadingInterface* CreatePlatformThreading();
-extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events);
-extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events);
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl);
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl);
 extern IAvnSystemDialogs* CreateSystemDialogs();
 extern IAvnScreens* CreateScreens();
 extern IAvnClipboard* CreateClipboard();
 extern IAvnCursorFactory* CreateCursorFactory();
-extern IAvnGlFeature* GetGlFeature();
-extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view);
+extern IAvnGlDisplay* GetGlDisplay();
 extern IAvnAppMenu* CreateAppMenu();
 extern IAvnAppMenuItem* CreateAppMenuItem();
 extern IAvnAppMenuItem* CreateAppMenuItemSeperator();

+ 0 - 261
native/Avalonia.Native/src/OSX/gl.mm

@@ -1,261 +0,0 @@
-#include "common.h"
-#include <OpenGL/gl.h>
-#include <dlfcn.h>
-#include "window.h"
-
-template <typename T, size_t N> char (&ArrayCounter(T (&a)[N]))[N];
-#define ARRAY_COUNT(a) (sizeof(ArrayCounter(a)))
-
-NSOpenGLPixelFormat* CreateFormat()
-{
-    NSOpenGLPixelFormatAttribute attribs[] =
-    {
-        NSOpenGLPFADoubleBuffer,
-        NSOpenGLPFAColorSize, 32,
-        NSOpenGLPFAStencilSize, 8,
-        NSOpenGLPFADepthSize, 8,
-        0
-    };
-    return [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs];
-}
-
-class AvnGlContext : public virtual ComSingleObject<IAvnGlContext, &IID_IAvnGlContext>
-{
-public:
-    FORWARD_IUNKNOWN()
-    NSOpenGLContext* GlContext;
-    GLuint Framebuffer, RenderBuffer, StencilBuffer;
-    AvnGlContext(NSOpenGLContext* gl, bool offscreen)
-    {
-        Framebuffer = 0;
-        RenderBuffer = 0;
-        StencilBuffer = 0;
-        GlContext = gl;
-        if(offscreen)
-        {
-            [GlContext makeCurrentContext];
-
-            glGenFramebuffersEXT(1, &Framebuffer);
-            glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer);
-            glGenRenderbuffersEXT(1, &RenderBuffer);
-            glGenRenderbuffersEXT(1, &StencilBuffer);
-
-            glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer);
-            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer);
-            glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer);
-            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer);
-        }
-        
-    }
-    
-    
-    virtual HRESULT MakeCurrent()  override
-    {
-        [GlContext makeCurrentContext];/*
-        glBindFramebufferEXT(GL_FRAMEBUFFER, Framebuffer);
-        glBindRenderbufferEXT(GL_RENDERBUFFER, RenderBuffer);
-        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, RenderBuffer);
-        glBindRenderbufferEXT(GL_RENDERBUFFER, StencilBuffer);
-        glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, StencilBuffer);*/
-        return S_OK;
-    }
-};
-
-class AvnGlDisplay : public virtual ComSingleObject<IAvnGlDisplay, &IID_IAvnGlDisplay>
-{
-    int _sampleCount, _stencilSize;
-    void* _libgl;
-    
-public:
-    FORWARD_IUNKNOWN()
-    
-    AvnGlDisplay(int sampleCount, int stencilSize)
-    {
-        _sampleCount = sampleCount;
-        _stencilSize = stencilSize;
-        _libgl = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/A/Libraries/libGL.dylib", RTLD_LAZY);
-    }
-    
-    virtual HRESULT GetSampleCount(int* ret)  override
-    {
-        *ret = _sampleCount;
-        return S_OK;
-    }
-    virtual HRESULT GetStencilSize(int* ret) override
-    {
-        *ret = _stencilSize;
-        return S_OK;
-    }
-    
-    virtual HRESULT ClearContext()  override
-    {
-        [NSOpenGLContext clearCurrentContext];
-        return S_OK;
-    }
-    
-    virtual void* GetProcAddress(char* proc)  override
-    {
-        return dlsym(_libgl, proc);
-    }
-};
-
-
-class GlFeature : public virtual ComSingleObject<IAvnGlFeature, &IID_IAvnGlFeature>
-{
-    IAvnGlDisplay* _display;
-    AvnGlContext *_immediate;
-    NSOpenGLContext* _shared;
-public:
-    FORWARD_IUNKNOWN()
-    NSOpenGLPixelFormat* _format;
-    GlFeature(IAvnGlDisplay* display, AvnGlContext* immediate, NSOpenGLPixelFormat* format)
-    {
-        _display = display;
-        _immediate = immediate;
-        _format = format;
-        _shared = [[NSOpenGLContext alloc] initWithFormat:_format shareContext:_immediate->GlContext];
-    }
-    
-    NSOpenGLContext* CreateContext()
-    {
-        return _shared;
-        //return [[NSOpenGLContext alloc] initWithFormat:_format shareContext:nil];
-    }
-    
-    virtual HRESULT ObtainDisplay(IAvnGlDisplay**retOut)  override
-    {
-        *retOut = _display;
-        _display->AddRef();
-        return S_OK;
-    }
-    
-    virtual HRESULT ObtainImmediateContext(IAvnGlContext**retOut)  override
-    {
-        *retOut = _immediate;
-        _immediate->AddRef();
-        return S_OK;
-    }
-};
-
-static GlFeature* Feature;
-
-GlFeature* CreateGlFeature()
-{
-    auto format = CreateFormat();
-    if(format == nil)
-    {
-        NSLog(@"Unable to choose pixel format");
-        return NULL;
-    }
-    
-    auto immediateContext = [[NSOpenGLContext alloc] initWithFormat:format shareContext:nil];
-    if(immediateContext == nil)
-    {
-        NSLog(@"Unable to create NSOpenGLContext");
-        return NULL;
-    }
-
-    int stencilBits = 0, sampleCount = 0;
-    
-    auto fmt = CGLGetPixelFormat([immediateContext CGLContextObj]);
-    CGLDescribePixelFormat(fmt, 0, kCGLPFASamples, &sampleCount);
-    CGLDescribePixelFormat(fmt, 0, kCGLPFAStencilSize, &stencilBits);
-    
-    auto offscreen = new AvnGlContext(immediateContext, true);
-    auto display = new AvnGlDisplay(sampleCount, stencilBits);
-    
-    return new GlFeature(display, offscreen, format);
-}
-
-
-static GlFeature* GetFeature()
-{
-    if(Feature == nil)
-        Feature = CreateGlFeature();
-    return Feature;
-}
-
-extern IAvnGlFeature* GetGlFeature()
-{
-    return GetFeature();
-}
-
-class AvnGlRenderingSession : public ComSingleObject<IAvnGlSurfaceRenderingSession, &IID_IAvnGlSurfaceRenderingSession>
-{
-    AvnView* _view;
-    AvnWindow* _window;
-    NSOpenGLContext* _context;
-public:
-    FORWARD_IUNKNOWN()
-    AvnGlRenderingSession(AvnWindow*window, AvnView* view, NSOpenGLContext* context)
-    {
-        _context = context;
-        _window = window;
-        _view = view;
-    }
-    
-    virtual HRESULT GetPixelSize(AvnPixelSize* ret)  override
-    {
-        *ret = [_view getPixelSize];
-        return S_OK;
-    }
-    virtual HRESULT GetScaling(double* ret)  override
-    {
-        *ret = [_window getScaling];
-        return S_OK;
-    }
-    
-    virtual ~AvnGlRenderingSession()
-    {
-        [_context flushBuffer];
-        [NSOpenGLContext clearCurrentContext];
-        CGLUnlockContext([_context CGLContextObj]);
-        [_view unlockFocus];
-    }
-};
-
-class AvnGlRenderTarget : public ComSingleObject<IAvnGlSurfaceRenderTarget, &IID_IAvnGlSurfaceRenderTarget>
-{
-    NSView* _view;
-    NSWindow* _window;
-    NSOpenGLContext* _context;
-public:
-    FORWARD_IUNKNOWN()
-    AvnGlRenderTarget(NSWindow* window, NSView*view)
-    {
-        _window = window;
-        _view = view;
-        _context = GetFeature()->CreateContext();
-    }
-    
-    virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret)  override
-    {
-        auto f = GetFeature();
-        if(f == NULL)
-            return E_FAIL;
-        
-        @try
-        {
-            if(![_view lockFocusIfCanDraw])
-                return E_ABORT;
-        }
-        @catch(NSException* exception)
-        {
-            return E_ABORT;
-        }
-        
-        
-        auto gl = _context;
-        CGLLockContext([_context CGLContextObj]);
-        [gl setView: _view];
-        [gl update];
-        [gl makeCurrentContext];
-        *ret = new AvnGlRenderingSession(_window, _view, gl);
-        return S_OK;
-    }
-};
-
-extern IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(NSWindow* window, NSView* view)
-{
-    return new AvnGlRenderTarget(window, view);
-}

+ 6 - 6
native/Avalonia.Native/src/OSX/main.mm

@@ -174,20 +174,20 @@ public:
         return (IAvnMacOptions*)new MacOptions();
     }
     
-    virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnWindow** ppv)  override
+    virtual HRESULT CreateWindow(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnWindow** ppv)  override
     {
         if(cb == nullptr || ppv == nullptr)
             return E_POINTER;
-        *ppv = CreateAvnWindow(cb);
+        *ppv = CreateAvnWindow(cb, gl);
         return S_OK;
     };
     
-    virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnPopup** ppv) override
+    virtual HRESULT CreatePopup(IAvnWindowEvents* cb, IAvnGlContext* gl, IAvnPopup** ppv) override
     {
         if(cb == nullptr || ppv == nullptr)
             return E_POINTER;
         
-        *ppv = CreateAvnPopup(cb);
+        *ppv = CreateAvnPopup(cb, gl);
         return S_OK;
     }
     
@@ -221,9 +221,9 @@ public:
         return S_OK;
     }
     
-    virtual HRESULT ObtainGlFeature(IAvnGlFeature** ppv) override
+    virtual HRESULT ObtainGlDisplay(IAvnGlDisplay** ppv) override
     {
-        auto rv = ::GetGlFeature();
+        auto rv = ::GetGlDisplay();
         if(rv == NULL)
             return E_FAIL;
         rv->AddRef();

+ 284 - 0
native/Avalonia.Native/src/OSX/rendertarget.mm

@@ -0,0 +1,284 @@
+#include "common.h"
+#include "rendertarget.h"
+#import <IOSurface/IOSurface.h>
+#import <IOSurface/IOSurfaceObjC.h>
+
+#include <OpenGL/CGLIOSurface.h>
+#include <OpenGL/OpenGL.h>
+#include <OpenGL/glext.h>
+#include <OpenGL/gl3.h>
+#include <OpenGL/gl3ext.h>
+
+@interface IOSurfaceHolder : NSObject
+@end
+
+@implementation IOSurfaceHolder
+{
+    @public IOSurfaceRef surface;
+    @public AvnPixelSize size;
+    @public float scale;
+    ComPtr<IAvnGlContext> _context;
+    GLuint _framebuffer, _texture, _renderbuffer;
+}
+
+- (IOSurfaceHolder*) initWithSize: (AvnPixelSize) size
+                        withScale: (float)scale
+                withOpenGlContext: (IAvnGlContext*) context
+{
+    long bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, size.Width * 4);
+    long allocSize = IOSurfaceAlignProperty(kIOSurfaceAllocSize, size.Height * bytesPerRow);
+    NSDictionary* options = @{
+                              (id)kIOSurfaceWidth: @(size.Width),
+                              (id)kIOSurfaceHeight:  @(size.Height),
+                              (id)kIOSurfacePixelFormat: @((uint)'BGRA'),
+                              (id)kIOSurfaceBytesPerElement: @(4),
+                              (id)kIOSurfaceBytesPerRow: @(bytesPerRow),
+                              (id)kIOSurfaceAllocSize: @(allocSize),
+                              
+                              //(id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache),
+                              (id)kIOSurfaceElementWidth: @(1),
+                              (id)kIOSurfaceElementHeight: @(1)
+                              };
+    
+    surface = IOSurfaceCreate((CFDictionaryRef)options);
+    self->scale = scale;
+    self->size = size;
+    self->_context = context;
+    return self;
+}
+
+-(HRESULT) prepareForGlRender
+{
+    if(_context == nil)
+        return E_FAIL;
+    if(CGLGetCurrentContext() != _context->GetNativeHandle())
+        return E_FAIL;
+    if(_framebuffer == 0)
+        glGenFramebuffersEXT(1, &_framebuffer);
+    
+    
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, _framebuffer);
+    if(_texture == 0)
+    {
+        glGenTextures(1, &_texture);
+    
+        glBindTexture(GL_TEXTURE_RECTANGLE_EXT, _texture);
+        CGLError res = CGLTexImageIOSurface2D((CGLContextObj)_context->GetNativeHandle(),
+                               GL_TEXTURE_RECTANGLE_EXT, GL_RGBA8,
+                               size.Width, size.Height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, surface, 0);
+        glBindTexture(GL_TEXTURE_RECTANGLE_EXT, 0);
+        
+        if(res != 0)
+        {
+            glDeleteTextures(1, &_texture);
+            _texture = 0;
+            glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+            return E_FAIL;
+        }
+        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_RECTANGLE_EXT, _texture, 0);
+    }
+    
+    if(_renderbuffer == 0)
+    {
+        glGenRenderbuffers(1, &_renderbuffer);
+        glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, size.Width, size.Height);
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _renderbuffer);
+    }
+    
+    return S_OK;
+}
+
+-(void) finishDraw
+{
+    ComPtr<IUnknown> release;
+    _context->MakeCurrent(release.getPPV());
+    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
+    glFlush();
+}
+
+-(void) dealloc
+{
+    
+    if(_framebuffer != 0)
+    {
+        ComPtr<IUnknown> release;
+        _context->MakeCurrent(release.getPPV());
+        glDeleteFramebuffers(1, &_framebuffer);
+        if(_texture != 0)
+            glDeleteTextures(1, &_texture);
+        if(_renderbuffer != 0)
+            glDeleteRenderbuffers(1, &_renderbuffer);
+    }
+    IOSurfaceDecrementUseCount(surface);
+}
+@end
+
+static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target);
+
+@implementation IOSurfaceRenderTarget
+{
+    CALayer* _layer;
+    @public IOSurfaceHolder* surface;
+    @public NSObject* lock;
+    ComPtr<IAvnGlContext> _glContext;
+}
+
+- (IOSurfaceRenderTarget*) initWithOpenGlContext: (IAvnGlContext*) context;
+{
+    self = [super init];
+    _glContext = context;
+    lock = [NSObject new];
+    surface  = nil;
+    [self resize:{1,1} withScale: 1];
+    
+    return self;
+}
+
+- (AvnPixelSize) pixelSize {
+    return {1, 1};
+}
+
+- (CALayer *)layer {
+    return _layer;
+}
+
+- (void)resize:(AvnPixelSize)size withScale: (float) scale;{
+    @synchronized (lock) {
+        if(surface == nil
+           || surface->size.Width != size.Width
+           || surface->size.Height != size.Height
+           || surface->scale != scale)
+            surface = [[IOSurfaceHolder alloc] initWithSize:size withScale:scale withOpenGlContext:_glContext.getRaw()];
+    }
+}
+
+- (void)updateLayer {
+    if ([NSThread isMainThread])
+    {
+        @synchronized (lock) {
+            if(_layer == nil)
+                return;
+            [_layer setContents: nil];
+            if(surface != nil)
+            {
+                [_layer setContentsScale: surface->scale];
+                [_layer setContents: (__bridge IOSurface*) surface->surface];
+            }
+        }
+    }
+    else
+        dispatch_async(dispatch_get_main_queue(), ^{
+            [self updateLayer];
+        });
+}
+
+- (void) setNewLayer:(CALayer *)layer {
+    _layer = layer;
+    [self updateLayer];
+}
+
+- (HRESULT)setSwFrame:(AvnFramebuffer *)fb {
+    @synchronized (lock) {
+        if(fb->PixelFormat == AvnPixelFormat::kAvnRgb565)
+            return E_INVALIDARG;
+        if(surface == nil)
+            return E_FAIL;
+        IOSurfaceRef surf = surface->surface;
+        if(IOSurfaceLock(surf, 0, nil))
+            return E_FAIL;
+        size_t w = MIN(fb->Width, IOSurfaceGetWidth(surf));
+        size_t h = MIN(fb->Height, IOSurfaceGetHeight(surf));
+        size_t wbytes = w*4;
+        size_t sstride = IOSurfaceGetBytesPerRow(surf);
+        size_t fstride = fb->Stride;
+        char*pSurface = (char*)IOSurfaceGetBaseAddress(surf);
+        char*pFb = (char*)fb->Data;
+        for(size_t y = 0; y < h; y++)
+        {
+            memcpy(pSurface + y*sstride, pFb + y*fstride, wbytes);
+        }
+        IOSurfaceUnlock(surf, 0, nil);
+        [self updateLayer];
+        return S_OK;
+    }
+}
+
+-(IAvnGlSurfaceRenderTarget*) createSurfaceRenderTarget
+{
+    return CreateGlRenderTarget(self);
+}
+
+@end
+
+class AvnGlRenderingSession : public ComSingleObject<IAvnGlSurfaceRenderingSession, &IID_IAvnGlSurfaceRenderingSession>
+{
+    ComPtr<IUnknown> _releaseContext;
+    IOSurfaceRenderTarget* _target;
+    IOSurfaceHolder* _surface;
+public:
+    FORWARD_IUNKNOWN()
+    AvnGlRenderingSession(IOSurfaceRenderTarget* target, ComPtr<IUnknown> releaseContext)
+    {
+        _target = target;
+        // This happens in a synchronized block set up by AvnRenderTarget, so we take the current surface for this
+        // particular render session
+        _surface = _target->surface;
+        _releaseContext = releaseContext;
+    }
+    
+    virtual HRESULT GetPixelSize(AvnPixelSize* ret)  override
+    {
+        if(!_surface)
+            return E_FAIL;
+        *ret = _surface->size;
+        return S_OK;
+    }
+    
+    virtual HRESULT GetScaling(double* ret)  override
+    {
+        if(!_surface)
+            return E_FAIL;
+        *ret = _surface->scale;
+        return S_OK;
+    }
+    
+    virtual ~AvnGlRenderingSession()
+    {
+        [_surface finishDraw];
+        [_target updateLayer];
+        _releaseContext = nil;
+    }
+};
+
+class AvnGlRenderTarget : public ComSingleObject<IAvnGlSurfaceRenderTarget, &IID_IAvnGlSurfaceRenderTarget>
+{
+    IOSurfaceRenderTarget* _target;
+public:
+    FORWARD_IUNKNOWN()
+    AvnGlRenderTarget(IOSurfaceRenderTarget* target)
+    {
+        _target = target;
+    }
+    
+    virtual HRESULT BeginDrawing(IAvnGlSurfaceRenderingSession** ret)  override
+    {
+        ComPtr<IUnknown> releaseContext;
+        @synchronized (_target->lock) {
+            if(_target->surface == nil)
+                return E_FAIL;
+            _target->_glContext->MakeCurrent(releaseContext.getPPV());
+            HRESULT res = [_target->surface prepareForGlRender];
+            if(res)
+                return res;
+            *ret = new AvnGlRenderingSession(_target, releaseContext);
+            return S_OK;
+        }
+    }
+};
+
+
+static IAvnGlSurfaceRenderTarget* CreateGlRenderTarget(IOSurfaceRenderTarget* target)
+{
+    return new AvnGlRenderTarget(target);
+}

+ 50 - 157
native/Avalonia.Native/src/OSX/window.mm

@@ -7,44 +7,9 @@
 #include "cursor.h"
 #include "menu.h"
 #include <OpenGL/gl.h>
+#include "rendertarget.h"
+
 
-class SoftwareDrawingOperation
-{
-public:
-    void* Data = 0;
-    AvnFramebuffer Desc;
-    void Alloc(NSView* view)
-    {
-        auto logicalSize = [view frame].size;
-        auto pixelSize = [view convertSizeToBacking:logicalSize];
-        int w = pixelSize.width;
-        int h = pixelSize.height;
-        int stride = w * 4;
-        Data = malloc(h * stride);
-        Desc = {
-            .Data = Data,
-            .Stride = stride,
-            .Width = w,
-            .Height = h,
-            .PixelFormat = kAvnRgba8888,
-            .Dpi = AvnVector { .X = w / logicalSize.width * 96, .Y = h / logicalSize.height * 96}
-        };
-    }
-    
-    void Dealloc()
-    {
-        if(Data != NULL)
-        {
-            free(Data);
-            Data = NULL;
-        }
-    }
-    
-    ~SoftwareDrawingOperation()
-    {
-        Dealloc();
-    }
-};
 
 class WindowBaseImpl : public virtual ComSingleObject<IAvnWindowBase, &IID_IAvnWindowBase>, public INSWindowHolder
 {
@@ -61,15 +26,18 @@ public:
     AvnView* View;
     AvnWindow* Window;
     ComPtr<IAvnWindowBaseEvents> BaseEvents;
-    SoftwareDrawingOperation CurrentSwDrawingOperation;
+    ComPtr<IAvnGlContext> _glContext;
+    NSObject<IRenderTarget>* renderTarget;
     AvnPoint lastPositionSet;
     NSString* _lastTitle;
     IAvnAppMenu* _mainMenu;
     
-    WindowBaseImpl(IAvnWindowBaseEvents* events)
+    WindowBaseImpl(IAvnWindowBaseEvents* events, IAvnGlContext* gl)
     {
         _mainMenu = nullptr;
         BaseEvents = events;
+        _glContext = gl;
+        renderTarget = [[IOSurfaceRenderTarget alloc] initWithOpenGlContext: gl];
         View = [[AvnView alloc] initWithParent:this];
 
         Window = [[AvnWindow alloc] initWithParent:this];
@@ -291,29 +259,6 @@ public:
         return S_OK;
     }
     
-    virtual bool TryLock() override
-    {
-        @autoreleasepool
-        {
-            @try
-            {
-                return [View lockFocusIfCanDraw] == YES;
-            }
-            @catch (NSException*)
-            {
-                return NO;
-            }
-        }
-    }
-    
-    virtual void Unlock() override
-    {
-        @autoreleasepool
-        {
-            [View unlockFocus];
-        }
-    }
-    
     virtual HRESULT BeginMoveDrag () override
     {
         @autoreleasepool
@@ -408,16 +353,6 @@ public:
         return S_OK;
     }
     
-    virtual HRESULT GetSoftwareFramebuffer(AvnFramebuffer*ret) override
-    {
-        if(![[NSThread currentThread] isMainThread])
-            return E_FAIL;
-        if(CurrentSwDrawingOperation.Data == NULL)
-            CurrentSwDrawingOperation.Alloc(View);
-        *ret = CurrentSwDrawingOperation.Desc;
-        return S_OK;
-    }
-    
     virtual HRESULT SetCursor(IAvnCursor* cursor) override
     {
         @autoreleasepool
@@ -451,8 +386,8 @@ public:
     {
         if(View == NULL)
             return E_FAIL;
-        *ppv = ::CreateGlRenderTarget(Window, View);
-        return S_OK;
+        *ppv = [renderTarget createSurfaceRenderTarget];
+        return *ppv == nil ? E_FAIL : S_OK;
     }
 
 protected:
@@ -490,7 +425,7 @@ private:
     }
     
     ComPtr<IAvnWindowEvents> WindowEvents;
-    WindowImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
+    WindowImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
     {
         WindowEvents = events;
         [Window setCanBecomeKeyAndMain];
@@ -731,6 +666,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
     NSEvent* _lastMouseDownEvent;
     bool _lastKeyHandled;
     AvnPixelSize _lastPixelSize;
+    NSObject<IRenderTarget>* _renderTarget;
 }
 
 - (void)onClosed
@@ -741,19 +677,6 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
     }
 }
 
-- (BOOL)lockFocusIfCanDraw
-{
-    @synchronized (self)
-    {
-        if(_parent == nullptr)
-        {
-            return NO;
-        }
-    }
-    
-    return [super lockFocusIfCanDraw];
-}
-
 -(AvnPixelSize) getPixelSize
 {
     return _lastPixelSize;
@@ -764,18 +687,43 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
     return _lastMouseDownEvent;
 }
 
+- (void) updateRenderTarget
+{
+    [_renderTarget resize:_lastPixelSize withScale: [[self window] backingScaleFactor]];
+    [self setNeedsDisplayInRect:[self frame]];
+}
+
 -(AvnView*)  initWithParent: (WindowBaseImpl*) parent
 {
     self = [super init];
-    [self setWantsBestResolutionOpenGLSurface:true];
+    _renderTarget = parent->renderTarget;
     [self setWantsLayer:YES];
+    [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize];
+    
     _parent = parent;
     _area = nullptr;
     _lastPixelSize.Height = 100;
     _lastPixelSize.Width = 100;
+
     return self;
 }
 
+- (BOOL)isFlipped
+{
+    return YES;
+}
+
+- (BOOL)wantsUpdateLayer
+{
+    return YES;
+}
+
+- (void)setLayer:(CALayer *)layer
+{
+    [_renderTarget setNewLayer: layer];
+    [super setLayer: layer];
+}
+
 - (BOOL)isOpaque
 {
     return YES;
@@ -823,87 +771,32 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
     auto fsize = [self convertSizeToBacking: [self frame].size];
     _lastPixelSize.Width = (int)fsize.width;
     _lastPixelSize.Height = (int)fsize.height;
-
+    [self updateRenderTarget];
     _parent->BaseEvents->Resized(AvnSize{newSize.width, newSize.height});
 }
 
-- (void) drawFb: (AvnFramebuffer*) fb
-{
-    auto colorSpace = CGColorSpaceCreateDeviceRGB();
-    auto dataProvider = CGDataProviderCreateWithData(NULL, fb->Data, fb->Height*fb->Stride, NULL);
 
-    
-    auto image = CGImageCreate(fb->Width, fb->Height, 8, 32, fb->Stride, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast,
-                               dataProvider, nullptr, false, kCGRenderingIntentDefault);
-    
-    auto ctx = [NSGraphicsContext currentContext];
-    
-    [ctx saveGraphicsState];
-    auto cgc = [ctx CGContext];
-    
-    CGContextDrawImage(cgc, CGRect{0,0, fb->Width/(fb->Dpi.X/96), fb->Height/(fb->Dpi.Y/96)}, image);
-    CGImageRelease(image);
-    CGColorSpaceRelease(colorSpace);
-    CGDataProviderRelease(dataProvider);
-    
-    [ctx restoreGraphicsState];
-
-}
-
-- (void)drawRect:(NSRect)dirtyRect
+- (void)updateLayer
 {
     if (_parent == nullptr)
     {
         return;
     }
-        
-    _parent->BaseEvents->RunRenderPriorityJobs();
     
-    @synchronized (self) {
-        if(_swRenderedFrame != NULL)
-        {
-            [self drawFb: &_swRenderedFrameBuffer];
-            return;
-        }
-    }
-    
-    auto swOp = &_parent->CurrentSwDrawingOperation;
+    _parent->BaseEvents->RunRenderPriorityJobs();
     _parent->BaseEvents->Paint();
-    if(swOp->Data != NULL)
-        [self drawFb: &swOp->Desc];
-    
-    swOp->Dealloc();
-    return;
 }
 
--(void) redrawSelf
+- (void)drawRect:(NSRect)dirtyRect
 {
-    @autoreleasepool
-    {
-        @synchronized(self)
-        {
-            if(!_queuedDisplayFromThread)
-                return;
-            _queuedDisplayFromThread = false;
-        }
-        [self setNeedsDisplayInRect:[self frame]];
-        [self display];
-        
-    }
+    return;
 }
 
 -(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];
-            }
-        }
+        [_renderTarget setSwFrame:fb];
+        dispose->Release();
     }
 }
 
@@ -928,7 +821,7 @@ NSArray* AllLoopModes = [NSArray arrayWithObjects: NSDefaultRunLoopMode, NSEvent
     auto fsize = [self convertSizeToBacking: [self frame].size];
     _lastPixelSize.Width = (int)fsize.width;
     _lastPixelSize.Height = (int)fsize.height;
-    
+    [self updateRenderTarget];
     _parent->BaseEvents->ScalingChanged([_parent->Window backingScaleFactor]);
     
     [super viewDidChangeBackingProperties];
@@ -1478,7 +1371,7 @@ private:
     END_INTERFACE_MAP()
     virtual ~PopupImpl(){}
     ComPtr<IAvnWindowEvents> WindowEvents;
-    PopupImpl(IAvnWindowEvents* events) : WindowBaseImpl(events)
+    PopupImpl(IAvnWindowEvents* events, IAvnGlContext* gl) : WindowBaseImpl(events, gl)
     {
         WindowEvents = events;
         [Window setLevel:NSPopUpMenuWindowLevel];
@@ -1502,20 +1395,20 @@ protected:
     }
 };
 
-extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events)
+extern IAvnPopup* CreateAvnPopup(IAvnWindowEvents*events, IAvnGlContext* gl)
 {
     @autoreleasepool
     {
-        IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events));
+        IAvnPopup* ptr = dynamic_cast<IAvnPopup*>(new PopupImpl(events, gl));
         return ptr;
     }
 }
 
-extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events)
+extern IAvnWindow* CreateAvnWindow(IAvnWindowEvents*events, IAvnGlContext* gl)
 {
     @autoreleasepool
     {
-        IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events);
+        IAvnWindow* ptr = (IAvnWindow*)new WindowImpl(events, gl);
         return ptr;
     }
 }

+ 1 - 1
readme.md

@@ -10,7 +10,7 @@
 
 **Avalonia** is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), macOS and with experimental support for Android and iOS.
 
-**Avalonia** is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and breaking changes as we continue along into this project's development. To see the status of some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239).
+**Avalonia** is ready for **General-Purpose Desktop App Development**. However, there may be some bugs and [breaking changes](https://github.com/AvaloniaUI/Avalonia/wiki/Breaking-Changes) as we continue along into this project's development. To see the status of some of our features, please see our [Roadmap here](https://github.com/AvaloniaUI/Avalonia/issues/2239).
 
 | Control catalog | Desktop platforms | Mobile platforms |
 |---|---|---|

+ 7 - 2
src/Avalonia.Base/Utilities/WeakEventHandlerManager.cs

@@ -183,11 +183,16 @@ namespace Avalonia.Utilities
                 for (int c = 0; c < _count; c++)
                 {
                     var r = _data[c];
+
+                    TSubscriber target = null;
+
+                    r.Subscriber?.TryGetTarget(out target);
+
                     //Mark current index as first empty
-                    if (r.Subscriber == null && empty == -1)
+                    if (target == null && empty == -1)
                         empty = c;
                     //If current element isn't null and we have an empty one
-                    if (r.Subscriber != null && empty != -1)
+                    if (target != null && empty != -1)
                     {
                         _data[c] = default;
                         _data[empty] = r;

+ 2 - 2
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@@ -326,7 +326,7 @@ namespace Avalonia.Controls
 
             if (OwningGrid != null && OwningGrid.ColumnHeaders != null)
             {
-                args.Device.Capture(this);
+                args.Pointer.Capture(this);
 
                 _dragMode = DragMode.MouseDown;
                 _frozenColumnsWidth = OwningGrid.ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth();
@@ -391,7 +391,7 @@ namespace Avalonia.Controls
                 SetDragCursor(mousePosition);
 
                 // Variables that track drag mode states get reset in DataGridColumnHeader_LostMouseCapture
-                args.Device.Capture(null);
+                args.Pointer.Capture(null);
                 OnLostMouseCapture();
                 _dragMode = DragMode.None;
                 handled = true;

+ 10 - 7
src/Avalonia.Controls.DataGrid/Themes/Default.xaml

@@ -45,7 +45,7 @@
 
             <Path Name="SortIcon"
                   Grid.Column="1"
-                  Fill="#FF444444"
+                  Fill="{TemplateBinding Foreground}"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Center"
                   Stretch="Uniform"
@@ -113,7 +113,7 @@
 
   <Style Selector="DataGridRow /template/ Rectangle#BackgroundRectangle">
     <Setter Property="IsVisible" Value="False"/>
-    <Setter Property="Fill" Value="#FFBADDE9" />
+    <Setter Property="Fill" Value="{DynamicResource HighlightBrush}" />
   </Style>
 
   <Style Selector="DataGridRow:pointerover /template/ Rectangle#BackgroundRectangle">
@@ -126,6 +126,10 @@
     <Setter Property="Opacity" Value="1"/>
   </Style>
 
+  <Style Selector="DataGridRow:selected">
+    <Setter Property="Foreground" Value="{DynamicResource HighlightForegroundBrush}" />
+  </Style>
+
   <Style Selector="DataGridRowHeader">
     <Setter Property="Template">
       <ControlTemplate>
@@ -139,7 +143,7 @@
   </Style>
 
   <Style Selector="DataGridRowGroupHeader">
-    <Setter Property="Background" Value="#FFE4E8EA" />
+    <Setter Property="Background" Value="{DynamicResource ThemeControlMidHighBrush}" />
     <Setter Property="Height" Value="20"/>
     <Setter Property="Template">
       <ControlTemplate>
@@ -148,7 +152,6 @@
                                  ColumnDefinitions="Auto,Auto,Auto,Auto"
                                  RowDefinitions="Auto,*,Auto">
 
-          <Rectangle Grid.Column="1" Grid.ColumnSpan="5" Fill="#FFFFFFFF" Height="1"/>
           <Rectangle Grid.Column="1" Grid.Row="1" Name="IndentSpacer" />
           <ToggleButton Grid.Column="2" Grid.Row="1" Name="ExpanderButton" Margin="2,0,0,0"/>
 
@@ -169,7 +172,7 @@
     <Setter Property="Template">
       <ControlTemplate>
         <Border Grid.Column="0" Width="20" Height="20" Background="Transparent" HorizontalAlignment="Center" VerticalAlignment="Center">
-          <Path Fill="Black"
+          <Path Fill="{TemplateBinding Foreground}"
                 HorizontalAlignment="Center"
                 VerticalAlignment="Center"
                 Data="M 0 2 L 4 6 L 0 10 Z" />
@@ -217,12 +220,12 @@
             <DataGridRowsPresenter Name="PART_RowsPresenter" Grid.ColumnSpan="2" Grid.Row="1" />
             <Rectangle Name="BottomRightCorner" Fill="#FFE9EEF4" Grid.Column="2" Grid.Row="2" />
             <Rectangle Name="BottomLeftCorner" Fill="#FFE9EEF4" Grid.Row="2" Grid.ColumnSpan="2" />
-            <ScrollBar Name="PART_VerticalScrollbar" Orientation="Vertical" Grid.Column="2" Grid.Row="1" Width="{DynamicResource ScrollBarThickness}" Margin="0,-1,-1,-1"/>
+            <ScrollBar Name="PART_VerticalScrollbar" Orientation="Vertical" Grid.Column="2" Grid.Row="1" Width="{DynamicResource ScrollBarThickness}"/>
 
             <Grid Grid.Column="1" Grid.Row="2"
                   ColumnDefinitions="Auto,*">
               <Rectangle Name="PART_FrozenColumnScrollBarSpacer" />
-              <ScrollBar Name="PART_HorizontalScrollbar" Grid.Column="1" Orientation="Horizontal" Height="{DynamicResource ScrollBarThickness}" Margin="-1,0,-1,-1"/>
+              <ScrollBar Name="PART_HorizontalScrollbar" Grid.Column="1" Orientation="Horizontal" Height="{DynamicResource ScrollBarThickness}"/>
             </Grid>
           </Grid>
         </Border>

+ 1 - 1
src/Avalonia.Controls/AutoCompleteBox.cs

@@ -683,7 +683,7 @@ namespace Avalonia.Controls
                 added.Add(e.NewValue);
             }
 
-            OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, added, removed));
+            OnSelectionChanged(new SelectionChangedEventArgs(SelectionChangedEvent, removed, added));
         }
 
         /// <summary>

+ 6 - 6
src/Avalonia.Controls/Calendar/CalendarItem.cs

@@ -943,8 +943,8 @@ namespace Avalonia.Controls.Primitives
             {
                 CalendarDayButton b = (CalendarDayButton)sender;
                 // The button is in Pressed state. Change the state to normal.
-                if (e.Device.Captured == b)
-                    e.Device.Capture(null);
+                if (e.Pointer.Captured == b)
+                    e.Pointer.Capture(null);
                 _lastCalendarDayButton = b;
             }
         }
@@ -1213,8 +1213,8 @@ namespace Avalonia.Controls.Primitives
             {
                 CalendarButton b = (CalendarButton)sender;
                 // The button is in Pressed state. Change the state to normal.
-                if (e.Device.Captured == b)
-                    e.Device.Capture(null);
+                if (e.Pointer.Captured == b)
+                    e.Pointer.Capture(null);
                 //b.ReleaseMouseCapture();
 
                 _lastCalendarButton = b;
@@ -1224,7 +1224,7 @@ namespace Avalonia.Controls.Primitives
         {
             if (_lastCalendarDayButton != null)
             {
-                e.Device.Capture(_lastCalendarDayButton);
+                e.Pointer.Capture(_lastCalendarDayButton);
             }
         }
 
@@ -1232,7 +1232,7 @@ namespace Avalonia.Controls.Primitives
         {
             if (_lastCalendarButton != null)
             {
-                e.Device.Capture(_lastCalendarButton);
+                e.Pointer.Capture(_lastCalendarButton);
             }
         }
         

+ 1 - 1
src/Avalonia.Controls/Calendar/DatePicker.cs

@@ -788,7 +788,7 @@ namespace Avalonia.Controls
                     removedItems.Add(removedDate.Value);
                 }
 
-                handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, addedItems, removedItems));
+                handler(this, new SelectionChangedEventArgs(SelectingItemsControl.SelectionChangedEvent, removedItems, addedItems));
             }
         }
         private void OnCalendarClosed(EventArgs e)

+ 2 - 2
src/Avalonia.Controls/Calendar/SelectedDatesCollection.cs

@@ -49,7 +49,7 @@ namespace Avalonia.Controls.Primitives
 
         private void InvokeCollectionChanged(System.Collections.IList removedItems, System.Collections.IList addedItems)
         {
-            _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, addedItems, removedItems));
+            _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, removedItems, addedItems));
         }
 
         /// <summary>
@@ -119,7 +119,7 @@ namespace Avalonia.Controls.Primitives
                 }
             }
 
-            _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _addedItems, _owner.RemovedItems));
+            _owner.OnSelectedDatesCollectionChanged(new SelectionChangedEventArgs(null, _owner.RemovedItems, _addedItems));
             _owner.RemovedItems.Clear();
             _owner.UpdateMonths();
             _isRangeAdded = false;

+ 173 - 110
src/Avalonia.Controls/Primitives/Popup.cs

@@ -2,12 +2,10 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 
 using System;
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Linq;
 using System.Reactive.Disposables;
 using Avalonia.Controls.Presenters;
-using Avalonia.Data;
 using Avalonia.Input;
 using Avalonia.Input.Raw;
 using Avalonia.Interactivity;
@@ -15,6 +13,8 @@ using Avalonia.LogicalTree;
 using Avalonia.Metadata;
 using Avalonia.VisualTree;
 
+#nullable enable
+
 namespace Avalonia.Controls.Primitives
 {
     /// <summary>
@@ -25,8 +25,8 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// Defines the <see cref="Child"/> property.
         /// </summary>
-        public static readonly StyledProperty<Control> ChildProperty =
-            AvaloniaProperty.Register<Popup, Control>(nameof(Child));
+        public static readonly StyledProperty<Control?> ChildProperty =
+            AvaloniaProperty.Register<Popup, Control?>(nameof(Child));
 
         /// <summary>
         /// Defines the <see cref="IsOpen"/> property.
@@ -43,11 +43,13 @@ namespace Avalonia.Controls.Primitives
         public static readonly StyledProperty<PlacementMode> PlacementModeProperty =
             AvaloniaProperty.Register<Popup, PlacementMode>(nameof(PlacementMode), defaultValue: PlacementMode.Bottom);
 
+#pragma warning disable 618
         /// <summary>
         /// Defines the <see cref="ObeyScreenEdges"/> property.
         /// </summary>
         public static readonly StyledProperty<bool> ObeyScreenEdgesProperty =
             AvaloniaProperty.Register<Popup, bool>(nameof(ObeyScreenEdges), true);
+#pragma warning restore 618
 
         /// <summary>
         /// Defines the <see cref="HorizontalOffset"/> property.
@@ -64,8 +66,8 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// Defines the <see cref="PlacementTarget"/> property.
         /// </summary>
-        public static readonly StyledProperty<Control> PlacementTargetProperty =
-            AvaloniaProperty.Register<Popup, Control>(nameof(PlacementTarget));
+        public static readonly StyledProperty<Control?> PlacementTargetProperty =
+            AvaloniaProperty.Register<Popup, Control?>(nameof(PlacementTarget));
 
         /// <summary>
         /// Defines the <see cref="StaysOpen"/> property.
@@ -80,12 +82,8 @@ namespace Avalonia.Controls.Primitives
             AvaloniaProperty.Register<Popup, bool>(nameof(Topmost));
 
         private bool _isOpen;
-        private IPopupHost _popupHost;
-        private TopLevel _topLevel;
-        private IDisposable _nonClientListener;
-        private IDisposable _presenterSubscription;
-        bool _ignoreIsOpenChanged = false;
-        private List<IDisposable> _bindings = new List<IDisposable>();
+        private bool _ignoreIsOpenChanged;
+        private PopupOpenState? _openState;
 
         /// <summary>
         /// Initializes static members of the <see cref="Popup"/> class.
@@ -94,31 +92,26 @@ namespace Avalonia.Controls.Primitives
         {
             IsHitTestVisibleProperty.OverrideDefaultValue<Popup>(false);
             ChildProperty.Changed.AddClassHandler<Popup>((x, e) => x.ChildChanged(e));
-            IsOpenProperty.Changed.AddClassHandler<Popup>((x, e) => x.IsOpenChanged(e));
-        }
-
-        public Popup()
-        {
-            
+            IsOpenProperty.Changed.AddClassHandler<Popup>((x, e) => x.IsOpenChanged((AvaloniaPropertyChangedEventArgs<bool>)e));
         }
 
         /// <summary>
         /// Raised when the popup closes.
         /// </summary>
-        public event EventHandler Closed;
+        public event EventHandler? Closed;
 
         /// <summary>
         /// Raised when the popup opens.
         /// </summary>
-        public event EventHandler Opened;
+        public event EventHandler? Opened;
 
-        public IPopupHost Host => _popupHost;
+        public IPopupHost? Host => _openState?.PopupHost;
 
         /// <summary>
         /// Gets or sets the control to display in the popup.
         /// </summary>
         [Content]
-        public Control Child
+        public Control? Child
         {
             get { return GetValue(ChildProperty); }
             set { SetValue(ChildProperty, value); }
@@ -131,7 +124,7 @@ namespace Avalonia.Controls.Primitives
         /// This property allows a client to customize the behaviour of the popup by injecting
         /// a specialized dependency resolver into the <see cref="PopupRoot"/>'s constructor.
         /// </remarks>
-        public IAvaloniaDependencyResolver DependencyResolver
+        public IAvaloniaDependencyResolver? DependencyResolver
         {
             get;
             set;
@@ -183,7 +176,7 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// Gets or sets the control that is used to determine the popup's position.
         /// </summary>
-        public Control PlacementTarget
+        public Control? PlacementTarget
         {
             get { return GetValue(PlacementTargetProperty); }
             set { SetValue(PlacementTargetProperty, value); }
@@ -211,7 +204,7 @@ namespace Avalonia.Controls.Primitives
         /// <summary>
         /// Gets the root of the popup window.
         /// </summary>
-        IVisual IVisualTreeHost.Root => _popupHost?.HostedVisualTreeRoot;
+        IVisual? IVisualTreeHost.Root => _openState?.PopupHost.HostedVisualTreeRoot;
 
         /// <summary>
         /// Opens the popup.
@@ -219,50 +212,91 @@ namespace Avalonia.Controls.Primitives
         public void Open()
         {
             // Popup is currently open
-            if (_topLevel != null)
+            if (_openState != null)
+            {
                 return;
-            CloseCurrent();
+            }
+
             var placementTarget = PlacementTarget ?? this.GetLogicalAncestors().OfType<IVisual>().FirstOrDefault();
+
             if (placementTarget == null)
+            {
                 throw new InvalidOperationException("Popup has no logical parent and PlacementTarget is null");
+            }
             
-            _topLevel = placementTarget.GetVisualRoot() as TopLevel;
+            var topLevel = placementTarget.VisualRoot as TopLevel;
 
-            if (_topLevel == null)
+            if (topLevel == null)
             {
                 throw new InvalidOperationException(
                     "Attempted to open a popup not attached to a TopLevel");
             }
 
-            _popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
+            var popupHost = OverlayPopupHost.CreatePopupHost(placementTarget, DependencyResolver);
+
+            var handlerCleanup = new CompositeDisposable(5);
 
-            _bindings.Add(_popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty,
+            void DeferCleanup(IDisposable? disposable)
+            {
+                if (disposable is null)
+                {
+                    return;
+                }
+
+                handlerCleanup.Add(disposable);
+            }
+
+            DeferCleanup(popupHost.BindConstraints(this, WidthProperty, MinWidthProperty, MaxWidthProperty,
                 HeightProperty, MinHeightProperty, MaxHeightProperty, TopmostProperty));
 
-            _popupHost.SetChild(Child);
-            ((ISetLogicalParent)_popupHost).SetParent(this);
-            _popupHost.ConfigurePosition(placementTarget,
-                PlacementMode, new Point(HorizontalOffset, VerticalOffset));
-            _popupHost.TemplateApplied += RootTemplateApplied;
-            
-            var window = _topLevel as Window;
-            if (window != null)
+            popupHost.SetChild(Child);
+            ((ISetLogicalParent)popupHost).SetParent(this);
+
+            popupHost.ConfigurePosition(
+                placementTarget,
+                PlacementMode, 
+                new Point(HorizontalOffset, VerticalOffset));
+
+            DeferCleanup(SubscribeToEventHandler<IPopupHost, EventHandler<TemplateAppliedEventArgs>>(popupHost, RootTemplateApplied,
+                (x, handler) => x.TemplateApplied += handler,
+                (x, handler) => x.TemplateApplied -= handler));
+
+            if (topLevel is Window window)
             {
-                window.Deactivated += WindowDeactivated;
+                DeferCleanup(SubscribeToEventHandler<Window, EventHandler>(window, WindowDeactivated,
+                    (x, handler) => x.Deactivated += handler,
+                    (x, handler) => x.Deactivated -= handler));
             }
             else
             {
-                var parentPopuproot = _topLevel as PopupRoot;
-                if (parentPopuproot?.Parent is Popup popup)
+                var parentPopupRoot = topLevel as PopupRoot;
+
+                if (parentPopupRoot?.Parent is Popup popup)
                 {
-                    popup.Closed += ParentClosed;
+                    DeferCleanup(SubscribeToEventHandler<Popup, EventHandler>(popup, ParentClosed,
+                        (x, handler) => x.Closed += handler,
+                        (x, handler) => x.Closed -= handler));
                 }
             }
-            _topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel);
-            _nonClientListener = InputManager.Instance?.Process.Subscribe(ListenForNonClientClick);
-        
 
-            _popupHost.Show();
+            DeferCleanup(topLevel.AddHandler(PointerPressedEvent, PointerPressedOutside, RoutingStrategies.Tunnel));
+
+            DeferCleanup(InputManager.Instance?.Process.Subscribe(ListenForNonClientClick));
+
+            var cleanupPopup = Disposable.Create((popupHost, handlerCleanup), state =>
+            {
+                state.handlerCleanup.Dispose();
+
+                state.popupHost.SetChild(null);
+                state.popupHost.Hide();
+
+                ((ISetLogicalParent)state.popupHost).SetParent(null);
+                state.popupHost.Dispose();
+            });
+
+            _openState = new PopupOpenState(topLevel, popupHost, cleanupPopup);
+
+            popupHost.Show();
 
             using (BeginIgnoringIsOpen())
             {
@@ -277,14 +311,19 @@ namespace Avalonia.Controls.Primitives
         /// </summary>
         public void Close()
         {
-            if (_popupHost != null)
+            if (_openState is null)
             {
-                _popupHost.TemplateApplied -= RootTemplateApplied;
+                using (BeginIgnoringIsOpen())
+                {
+                    IsOpen = false;
+                }
+
+                return;
             }
 
-            _presenterSubscription?.Dispose();
+            _openState.Dispose();
+            _openState = null;
 
-            CloseCurrent();
             using (BeginIgnoringIsOpen())
             {
                 IsOpen = false;
@@ -293,41 +332,6 @@ namespace Avalonia.Controls.Primitives
             Closed?.Invoke(this, EventArgs.Empty);
         }
 
-        void CloseCurrent()
-        {
-            if (_topLevel != null)
-            {
-                _topLevel.RemoveHandler(PointerPressedEvent, PointerPressedOutside);
-                var window = _topLevel as Window;
-                if (window != null)
-                    window.Deactivated -= WindowDeactivated;
-                else
-                {
-                    var parentPopuproot = _topLevel as PopupRoot;
-                    if (parentPopuproot?.Parent is Popup popup)
-                    {
-                        popup.Closed -= ParentClosed;
-                    }
-                }
-                _nonClientListener?.Dispose();
-                _nonClientListener = null;
-                
-                _topLevel = null;
-            }
-            if (_popupHost != null)
-            {
-                foreach(var b in _bindings)
-                    b.Dispose();
-                _bindings.Clear();
-                _popupHost.SetChild(null);
-                _popupHost.Hide();
-                ((ISetLogicalParent)_popupHost).SetParent(null);
-                _popupHost.Dispose();
-                _popupHost = null;
-            }
-
-        }
-
         /// <summary>
         /// Measures the control.
         /// </summary>
@@ -345,16 +349,22 @@ namespace Avalonia.Controls.Primitives
             Close();
         }
 
+        private static IDisposable SubscribeToEventHandler<T, TEventHandler>(T target, TEventHandler handler, Action<T, TEventHandler> subscribe, Action<T, TEventHandler> unsubscribe)
+        {
+            subscribe(target, handler);
+
+            return Disposable.Create((unsubscribe, target, handler), state => state.unsubscribe(state.target, state.handler));
+        }
 
         /// <summary>
         /// Called when the <see cref="IsOpen"/> property changes.
         /// </summary>
         /// <param name="e">The event args.</param>
-        private void IsOpenChanged(AvaloniaPropertyChangedEventArgs e)
+        private void IsOpenChanged(AvaloniaPropertyChangedEventArgs<bool> e)
         {
             if (!_ignoreIsOpenChanged)
             {
-                if ((bool)e.NewValue)
+                if (e.NewValue.Value)
                 {
                     Open();
                 }
@@ -373,7 +383,7 @@ namespace Avalonia.Controls.Primitives
         {
             LogicalChildren.Clear();
 
-            ((ISetLogicalParent)e.OldValue)?.SetParent(null);
+            ((ISetLogicalParent?)e.OldValue)?.SetParent(null);
 
             if (e.NewValue != null)
             {
@@ -394,34 +404,37 @@ namespace Avalonia.Controls.Primitives
 
         private void PointerPressedOutside(object sender, PointerPressedEventArgs e)
         {
-            if (!StaysOpen)
+            if (!StaysOpen && !IsChildOrThis((IVisual)e.Source))
             {
-                if (!IsChildOrThis((IVisual)e.Source))
-                {
-                    Close();
-                    e.Handled = true;
-                }
+                Close();
+                e.Handled = true;
             }
         }
 
         private void RootTemplateApplied(object sender, TemplateAppliedEventArgs e)
         {
-            _popupHost.TemplateApplied -= RootTemplateApplied;
-
-            if (_presenterSubscription != null)
+            if (_openState is null)
             {
-                _presenterSubscription.Dispose();
-                _presenterSubscription = null;
+                return;
             }
 
+            var popupHost = _openState.PopupHost;
+
+            popupHost.TemplateApplied -= RootTemplateApplied;
+
+            _openState.SetPresenterSubscription(null);
+
             // If the Popup appears in a control template, then the child controls
             // that appear in the popup host need to have their TemplatedParent
             // properties set.
-            if (TemplatedParent != null)
+            if (TemplatedParent != null && popupHost.Presenter != null)
             {
-                _popupHost.Presenter?.ApplyTemplate();
-                _popupHost.Presenter?.GetObservable(ContentPresenter.ChildProperty)
+                popupHost.Presenter.ApplyTemplate();
+
+                var presenterSubscription = popupHost.Presenter.GetObservable(ContentPresenter.ChildProperty)
                     .Subscribe(SetTemplatedParentAndApplyChildTemplates);
+
+                _openState.SetPresenterSubscription(presenterSubscription);
             }
         }
 
@@ -440,7 +453,7 @@ namespace Avalonia.Controls.Primitives
 
                 if (!(control is IPresenter) && control.TemplatedParent == templatedParent)
                 {
-                    foreach (IControl child in control.GetVisualChildren())
+                    foreach (IControl child in control.VisualChildren)
                     {
                         SetTemplatedParentAndApplyChildTemplates(child);
                     }
@@ -450,22 +463,41 @@ namespace Avalonia.Controls.Primitives
 
         private bool IsChildOrThis(IVisual child)
         {
-            IVisual root = child.GetVisualRoot();
-            while (root is IHostedVisualTreeRoot hostedRoot )
+            if (_openState is null)
             {
-                if (root == this._popupHost)
+                return false;
+            }
+
+            var popupHost = _openState.PopupHost;
+
+            IVisual? root = child.VisualRoot;
+            
+            while (root is IHostedVisualTreeRoot hostedRoot)
+            {
+                if (root == popupHost)
+                {
                     return true;
-                root = hostedRoot.Host?.GetVisualRoot();
+                }
+
+                root = hostedRoot.Host?.VisualRoot;
             }
+
             return false;
         }
         
         public bool IsInsidePopup(IVisual visual)
         {
-            return _popupHost != null && ((IVisual)_popupHost)?.IsVisualAncestorOf(visual) == true;
+            if (_openState is null)
+            {
+                return false;
+            }
+
+            var popupHost = _openState.PopupHost;
+
+            return popupHost != null && ((IVisual)popupHost).IsVisualAncestorOf(visual);
         }
 
-        public bool IsPointerOverPopup => ((IInputElement)_popupHost).IsPointerOver;
+        public bool IsPointerOverPopup => ((IInputElement?)_openState?.PopupHost)?.IsPointerOver ?? false;
 
         private void WindowDeactivated(object sender, EventArgs e)
         {
@@ -503,5 +535,36 @@ namespace Avalonia.Controls.Primitives
                 _owner._ignoreIsOpenChanged = false;
             }
         }
+
+        private class PopupOpenState : IDisposable
+        {
+            private readonly IDisposable _cleanup;
+            private IDisposable? _presenterCleanup;
+
+            public PopupOpenState(TopLevel topLevel, IPopupHost popupHost, IDisposable cleanup)
+            {
+                TopLevel = topLevel;
+                PopupHost = popupHost;
+                _cleanup = cleanup;
+            }
+
+            public TopLevel TopLevel { get; }
+
+            public IPopupHost PopupHost { get; }
+
+            public void SetPresenterSubscription(IDisposable? presenterCleanup)
+            {
+                _presenterCleanup?.Dispose();
+
+                _presenterCleanup = presenterCleanup;
+            }
+
+            public void Dispose()
+            {
+                _presenterCleanup?.Dispose();
+
+                _cleanup.Dispose();
+            }
+        }
     }
 }

+ 4 - 4
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@@ -939,8 +939,8 @@ namespace Avalonia.Controls.Primitives
             {
                 var changed = new SelectionChangedEventArgs(
                     SelectionChangedEvent,
-                    added ?? Empty,
-                    removed ?? Empty);
+                    removed ?? Empty,
+                    added ?? Empty);
                 RaiseEvent(changed);
             }
         }
@@ -1055,8 +1055,8 @@ namespace Avalonia.Controls.Primitives
 
                 var e = new SelectionChangedEventArgs(
                     SelectionChangedEvent,
-                    added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty<object>(),
-                    removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty<object>());
+                    removed?.Select(x => ElementAt(Items, x)).ToArray() ?? Array.Empty<object>(),
+                    added != -1 ? new[] { ElementAt(Items, added) } : Array.Empty<object>());
                 RaiseEvent(e);
             }
 

+ 2 - 2
src/Avalonia.Controls/Repeater/ItemsRepeaterElementIndexChangedEventArgs.cs

@@ -12,11 +12,11 @@ namespace Avalonia.Controls
     /// </summary>
     public class ItemsRepeaterElementIndexChangedEventArgs : EventArgs
     {
-        internal ItemsRepeaterElementIndexChangedEventArgs(IControl element, int newIndex, int oldIndex)
+        internal ItemsRepeaterElementIndexChangedEventArgs(IControl element, int oldIndex, int newIndex)
         {
             Element = element;
-            NewIndex = newIndex;
             OldIndex = oldIndex;
+            NewIndex = newIndex;
         }
 
         /// <summary>

+ 16 - 0
src/Avalonia.Controls/ScrollViewer.cs

@@ -249,6 +249,22 @@ namespace Avalonia.Controls
             set { SetValue(VerticalScrollBarVisibilityProperty, value); }
         }
 
+        /// <summary>
+        /// Scrolls to the top-left corner of the content.
+        /// </summary>
+        public void ScrollToHome()
+        {
+            Offset = new Vector(double.NegativeInfinity, double.NegativeInfinity);
+        }
+
+        /// <summary>
+        /// Scrolls to the bottom-left corner of the content.
+        /// </summary>
+        public void ScrollToEnd()
+        {
+            Offset = new Vector(double.NegativeInfinity, double.PositiveInfinity);
+        }
+
         /// <summary>
         /// Gets a value indicating whether the viewer can scroll horizontally.
         /// </summary>

+ 3 - 3
src/Avalonia.Controls/SelectionChangedEventArgs.cs

@@ -16,13 +16,13 @@ namespace Avalonia.Controls
         /// Initializes a new instance of the <see cref="SelectionChangedEventArgs"/> class.
         /// </summary>
         /// <param name="routedEvent">The event being raised.</param>
-        /// <param name="addedItems">The items added to the selection.</param>
         /// <param name="removedItems">The items removed from the selection.</param>
-        public SelectionChangedEventArgs(RoutedEvent routedEvent, IList addedItems, IList removedItems)
+        /// <param name="addedItems">The items added to the selection.</param>
+        public SelectionChangedEventArgs(RoutedEvent routedEvent, IList removedItems, IList addedItems)
             : base(routedEvent)
         {
-            AddedItems = addedItems;
             RemovedItems = removedItems;
+            AddedItems = addedItems;
         }
 
         /// <summary>

+ 2 - 2
src/Avalonia.Controls/TreeView.cs

@@ -324,8 +324,8 @@ namespace Avalonia.Controls
             {
                 var changed = new SelectionChangedEventArgs(
                     SelectingItemsControl.SelectionChangedEvent,
-                    added ?? Empty,
-                    removed ?? Empty);
+                    removed ?? Empty,
+                    added ?? Empty);
                 RaiseEvent(changed);
             }
         }

+ 1 - 1
src/Avalonia.Dialogs/AboutAvaloniaDialog.xaml

@@ -99,7 +99,7 @@
       </StackPanel>
      </StackPanel> 
     <StackPanel VerticalAlignment="Bottom" Margin="10">
-      <TextBlock Text="© 2019 The Avalonia Project" TextWrapping="Wrap" HorizontalAlignment="Center" />
+      <TextBlock Text="© 2020 The Avalonia Project" TextWrapping="Wrap" HorizontalAlignment="Center" />
     </StackPanel>
   </Grid>
 </Window>

+ 0 - 42
src/Avalonia.Native/AvaloniaNativeDeferredRendererLock.cs

@@ -1,42 +0,0 @@
-// 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.
-
-using System;
-using System.Threading;
-using Avalonia.Native.Interop;
-using Avalonia.Rendering;
-
-namespace Avalonia.Native
-{
-    public class AvaloniaNativeDeferredRendererLock : IDeferredRendererLock
-    {
-        private readonly IAvnWindowBase _window;
-
-        public AvaloniaNativeDeferredRendererLock(IAvnWindowBase window)
-        {
-            _window = window;
-        }
-
-        public IDisposable TryLock()
-        {
-            if (_window.TryLock())
-                return new UnlockDisposable(_window);
-            return null;
-        }
-
-        private sealed class UnlockDisposable : IDisposable
-        {
-            private IAvnWindowBase _window;
-
-            public UnlockDisposable(IAvnWindowBase window)
-            {
-                _window = window;
-            }
-
-            public void Dispose()
-            {
-                Interlocked.Exchange(ref _window, null)?.Unlock();
-            }
-        }
-    }
-}

+ 7 - 3
src/Avalonia.Native/AvaloniaNativePlatform.cs

@@ -2,6 +2,7 @@
 // Licensed under the MIT license. See licence.md file in the project root for full license information.
 using System;
 using System.Runtime.InteropServices;
+using System.Security.Cryptography;
 using Avalonia.Controls.Platform;
 using Avalonia.Input;
 using Avalonia.Input.Platform;
@@ -17,6 +18,7 @@ namespace Avalonia.Native
     {
         private readonly IAvaloniaNativeFactory _factory;
         private AvaloniaNativePlatformOptions _options;
+        private GlPlatformFeature _glFeature;
 
         [DllImport("libAvaloniaNative")]
         static extern IntPtr CreateAvaloniaNative();
@@ -87,7 +89,7 @@ namespace Avalonia.Native
 
                 _factory.MacOptions.ShowInDock = macOpts?.ShowInDock != false ? 1 : 0;
             }
-
+            
             AvaloniaLocator.CurrentMutable
                 .Bind<IPlatformThreadingInterface>()
                 .ToConstant(new PlatformThreadingInterface(_factory.CreatePlatformThreadingInterface()))
@@ -100,14 +102,16 @@ namespace Avalonia.Native
                 .Bind<IRenderLoop>().ToConstant(new RenderLoop())
                 .Bind<IRenderTimer>().ToConstant(new DefaultRenderTimer(60))
                 .Bind<ISystemDialogImpl>().ToConstant(new SystemDialogs(_factory.CreateSystemDialogs()))
-                .Bind<IWindowingPlatformGlFeature>().ToConstant(new GlPlatformFeature(_factory.ObtainGlFeature()))
                 .Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration(KeyModifiers.Meta))
                 .Bind<IMountedVolumeInfoProvider>().ToConstant(new MacOSMountedVolumeInfoProvider());
+            if (_options.UseGpu)
+                AvaloniaLocator.CurrentMutable.Bind<IWindowingPlatformGlFeature>()
+                    .ToConstant(_glFeature = new GlPlatformFeature(_factory.ObtainGlDisplay()));
         }
 
         public IWindowImpl CreateWindow()
         {
-            return new WindowImpl(_factory, _options);
+            return new WindowImpl(_factory, _options, _glFeature);
         }
 
         public IEmbeddableWindowImpl CreateEmbeddableWindow()

+ 1 - 1
src/Avalonia.Native/DeferredFramebuffer.cs

@@ -21,7 +21,7 @@ namespace Avalonia.Native
             Size = new PixelSize(width, height);
             RowBytes = width * 4;
             Dpi = dpi;
-            Format = PixelFormat.Rgba8888;
+            Format = PixelFormat.Bgra8888;
         }
 
         public IntPtr Address { get; set; }

+ 21 - 10
src/Avalonia.Native/GlPlatformFeature.cs

@@ -8,24 +8,31 @@ namespace Avalonia.Native
 {
     class GlPlatformFeature : IWindowingPlatformGlFeature
     {
-
-        public GlPlatformFeature(IAvnGlFeature feature)
+        public GlPlatformFeature(IAvnGlDisplay display)
         {
-            Display = new GlDisplay(feature.ObtainDisplay());
-            ImmediateContext = new GlContext(Display, feature.ObtainImmediateContext());
+            var immediate = display.CreateContext(null);
+            var deferred = display.CreateContext(immediate);
+            GlDisplay = new GlDisplay(display, immediate.SampleCount, immediate.StencilSize);
+            
+            ImmediateContext = new GlContext(Display, immediate);
+            DeferredContext = new GlContext(Display, deferred);
         }
 
         public IGlContext ImmediateContext { get; }
-        public GlDisplay Display { get; }
+        internal GlContext DeferredContext { get; }
+        internal GlDisplay GlDisplay;
+        public GlDisplay Display => GlDisplay;
     }
 
     class GlDisplay : IGlDisplay
     {
         private readonly IAvnGlDisplay _display;
 
-        public GlDisplay(IAvnGlDisplay display)
+        public GlDisplay(IAvnGlDisplay display, int sampleCount, int stencilSize)
         {
             _display = display;
+            SampleCount = sampleCount;
+            StencilSize = stencilSize;
             GlInterface = new GlInterface((name, optional) =>
             {
                 var rv = _display.GetProcAddress(name);
@@ -39,11 +46,11 @@ namespace Avalonia.Native
 
         public GlInterface GlInterface { get; }
 
-        public int SampleCount => _display.GetSampleCount();
+        public int SampleCount { get; }
 
-        public int StencilSize => _display.GetStencilSize();
+        public int StencilSize { get; }
 
-        public void ClearContext() => _display.ClearContext();
+        public void ClearContext() => _display.LegacyClearCurrentContext();
     }
 
     class GlContext : IGlContext
@@ -60,7 +67,7 @@ namespace Avalonia.Native
 
         public void MakeCurrent()
         {
-            Context.MakeCurrent();
+            Context.LegacyMakeCurrent();
         }
     }
 
@@ -109,6 +116,9 @@ namespace Avalonia.Native
 
         public double Scaling => _session.GetScaling();
 
+
+        public bool IsYFlipped => true;
+        
         public void Dispose()
         {
             _session?.Dispose();
@@ -128,5 +138,6 @@ namespace Avalonia.Native
         {
             return new GlPlatformSurfaceRenderTarget(_window.CreateGlRenderTarget());
         }
+
     }
 }

+ 8 - 4
src/Avalonia.Native/PopupImpl.cs

@@ -8,19 +8,23 @@ using Avalonia.Platform;
 
 namespace Avalonia.Native
 {
-    public class PopupImpl : WindowBaseImpl, IPopupImpl
+    class PopupImpl : WindowBaseImpl, IPopupImpl
     {
         private readonly IAvaloniaNativeFactory _factory;
         private readonly AvaloniaNativePlatformOptions _opts;
+        private readonly GlPlatformFeature _glFeature;
+
         public PopupImpl(IAvaloniaNativeFactory factory,
             AvaloniaNativePlatformOptions opts,
-            IWindowBaseImpl parent) : base(opts)
+            GlPlatformFeature glFeature,
+            IWindowBaseImpl parent) : base(opts, glFeature)
         {
             _factory = factory;
             _opts = opts;
+            _glFeature = glFeature;
             using (var e = new PopupEvents(this))
             {
-                Init(factory.CreatePopup(e), factory.CreateScreens());
+                Init(factory.CreatePopup(e, _opts.UseGpu ? glFeature?.DeferredContext.Context : null), factory.CreateScreens());
             }
             PopupPositioner = new ManagedPopupPositioner(new OsxManagedPopupPositionerPopupImplHelper(parent, MoveResize));
         }
@@ -51,7 +55,7 @@ namespace Avalonia.Native
             }
         }
 
-        public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, this);
+        public override IPopupImpl CreatePopup() => new PopupImpl(_factory, _opts, _glFeature, this);
         public IPopupPositioner PopupPositioner { get; }
     }
 }

+ 7 - 3
src/Avalonia.Native/WindowImpl.cs

@@ -14,14 +14,18 @@ namespace Avalonia.Native
     {
         private readonly IAvaloniaNativeFactory _factory;
         private readonly AvaloniaNativePlatformOptions _opts;
+        private readonly GlPlatformFeature _glFeature;
         IAvnWindow _native;
-        public WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts) : base(opts)
+        internal WindowImpl(IAvaloniaNativeFactory factory, AvaloniaNativePlatformOptions opts,
+            GlPlatformFeature glFeature) : base(opts, glFeature)
         {
             _factory = factory;
             _opts = opts;
+            _glFeature = glFeature;
             using (var e = new WindowEvents(this))
             {
-                Init(_native = factory.CreateWindow(e), factory.CreateScreens());
+                Init(_native = factory.CreateWindow(e,
+                    _opts.UseGpu ? glFeature?.DeferredContext.Context : null), factory.CreateScreens());
             }
 
             NativeMenuExporter = new AvaloniaNativeMenuExporter(_native, factory);
@@ -113,6 +117,6 @@ namespace Avalonia.Native
         public void Move(PixelPoint point) => Position = point;
 
         public override IPopupImpl CreatePopup() =>
-            _opts.OverlayPopups ? null : new PopupImpl(_factory, _opts, this);
+            _opts.OverlayPopups ? null : new PopupImpl(_factory, _opts, _glFeature, this);
     }
 }

+ 17 - 46
src/Avalonia.Native/WindowImplBase.cs

@@ -60,9 +60,9 @@ namespace Avalonia.Native
         private double _savedScaling;
         private GlPlatformSurface _glSurface;
 
-        public WindowBaseImpl(AvaloniaNativePlatformOptions opts)
+        internal WindowBaseImpl(AvaloniaNativePlatformOptions opts, GlPlatformFeature glFeature)
         {
-            _gpu = opts.UseGpu;
+            _gpu = opts.UseGpu && glFeature != null;
             _deferredRendering = opts.UseDeferredRendering;
 
             _keyboard = AvaloniaLocator.Current.GetService<IKeyboardDevice>();
@@ -75,8 +75,8 @@ namespace Avalonia.Native
             _native = window;
 
             Handle = new MacOSTopLevelWindowHandle(window);
-            
-            _glSurface = new GlPlatformSurface(window);
+            if (_gpu)
+                _glSurface = new GlPlatformSurface(window);
             Screen = new ScreenImpl(screens);
             _savedLogicalSize = ClientSize;
             _savedScaling = Scaling;
@@ -103,25 +103,20 @@ namespace Avalonia.Native
 
         public ILockedFramebuffer Lock()
         {
-            if(_deferredRendering)
+            var w = _savedLogicalSize.Width * _savedScaling;
+            var h = _savedLogicalSize.Height * _savedScaling;
+            var dpi = _savedScaling * 96;
+            return new DeferredFramebuffer(cb =>
             {
-                var w = _savedLogicalSize.Width * _savedScaling;
-                var h = _savedLogicalSize.Height * _savedScaling;
-                var dpi = _savedScaling * 96;
-                return new DeferredFramebuffer(cb =>
+                lock (_syncRoot)
                 {
-                    lock (_syncRoot)
-                    {
-                        if (_native == null)
-                            return false;
-                        cb(_native);
-                        _lastRenderedLogicalSize = _savedLogicalSize;
-                        return true;
-                    }
-                }, (int)w, (int)h, new Vector(dpi, dpi));
-           }
-
-            return new FramebufferWrapper(_native.GetSoftwareFramebuffer());
+                    if (_native == null)
+                        return false;
+                    cb(_native);
+                    _lastRenderedLogicalSize = _savedLogicalSize;
+                    return true;
+                }
+            }, (int)w, (int)h, new Vector(dpi, dpi));
         }
 
         public Action<Rect> Paint { get; set; }
@@ -130,28 +125,6 @@ namespace Avalonia.Native
         public IMouseDevice MouseDevice => _mouse;
         public abstract IPopupImpl CreatePopup();
 
-
-        class FramebufferWrapper : ILockedFramebuffer
-        {
-            public FramebufferWrapper(AvnFramebuffer fb)
-            {
-                Address = fb.Data;
-                Size = new PixelSize(fb.Width, fb.Height);
-                RowBytes = fb.Stride;
-                Dpi = new Vector(fb.Dpi.X, fb.Dpi.Y);
-                Format = (PixelFormat)fb.PixelFormat;
-            }
-            public IntPtr Address { get; set; }
-            public PixelSize Size { get; set; }
-            public int RowBytes {get;set;}
-            public Vector Dpi { get; set; }
-            public PixelFormat Format { get; }
-            public void Dispose()
-            {
-                // Do nothing
-            }
-        }
-
         protected class WindowBaseEvents : CallbackBase, IAvnWindowBaseEvents
         {
             private readonly WindowBaseImpl _parent;
@@ -278,9 +251,7 @@ namespace Avalonia.Native
         public IRenderer CreateRenderer(IRenderRoot root)
         {
             if (_deferredRendering)
-                return new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>(),
-                    rendererLock:
-                    _gpu ? new AvaloniaNativeDeferredRendererLock(_native) : null);
+                return new DeferredRenderer(root, AvaloniaLocator.Current.GetService<IRenderLoop>());
             return new ImmediateRenderer(root);
         }
 

+ 1 - 0
src/Avalonia.OpenGL/EglGlPlatformSurface.cs

@@ -107,6 +107,7 @@ namespace Avalonia.OpenGL
                 public IGlDisplay Display => _context.Display;
                 public PixelSize Size => _info.Size;
                 public double Scaling => _info.Scaling;
+                public bool IsYFlipped { get; }
             }
         }
     }

+ 1 - 0
src/Avalonia.OpenGL/IGlPlatformSurfaceRenderingSession.cs

@@ -7,5 +7,6 @@ namespace Avalonia.OpenGL
         IGlDisplay Display { get; }
         PixelSize Size { get; }
         double Scaling { get; }
+        bool IsYFlipped { get; }
     }
 }

+ 1 - 0
src/Avalonia.X11/Glx/GlxGlPlatformSurface.cs

@@ -80,6 +80,7 @@ namespace Avalonia.X11.Glx
                 public IGlDisplay Display => _context.Display;
                 public PixelSize Size => _info.Size;
                 public double Scaling => _info.Scaling;
+                public bool IsYFlipped { get; }
             }
         }
     }

+ 2 - 0
src/Linux/Avalonia.LinuxFramebuffer/Output/DrmOutput.cs

@@ -234,6 +234,8 @@ namespace Avalonia.LinuxFramebuffer.Output
                 public PixelSize Size => _parent._mode.Resolution;
 
                 public double Scaling => _parent.Scaling;
+
+                public bool IsYFlipped { get; }
             }
 
             public IGlPlatformSurfaceRenderingSession BeginDraw()

+ 1 - 1
src/Skia/Avalonia.Skia/GlRenderTarget.cs

@@ -54,7 +54,7 @@ namespace Avalonia.Skia
                         new GRBackendRenderTarget(size.Width, size.Height, disp.SampleCount, disp.StencilSize,
                             new GRGlFramebufferInfo((uint)fb, GRPixelConfig.Rgba8888.ToGlSizedFormat()));
                     var surface = SKSurface.Create(_grContext, renderTarget,
-                        GRSurfaceOrigin.BottomLeft,
+                        session.IsYFlipped ? GRSurfaceOrigin.TopLeft : GRSurfaceOrigin.BottomLeft,
                         GRPixelConfig.Rgba8888.ToColorType());
 
                     var nfo = new DrawingContextImpl.CreateInfo

+ 1 - 0
src/Skia/Avalonia.Skia/PlatformRenderInterface.cs

@@ -43,6 +43,7 @@ namespace Avalonia.Skia
                 {
                     GrContext = GRContext.Create(GRBackend.OpenGL, iface);
                 }
+                display.ClearContext();
             }
         }
 

+ 31 - 0
tests/Avalonia.Controls.UnitTests/ComboBoxTests.cs

@@ -108,5 +108,36 @@ namespace Avalonia.Controls.UnitTests
                 };
             });
         }
+
+        [Fact]
+        public void Detaching_Closed_ComboBox_Keeps_Current_Focus()
+        {
+            using (UnitTestApplication.Start(TestServices.RealFocus))
+            {
+                var target = new ComboBox
+                {
+                    Items = new[] { new Canvas() },
+                    SelectedIndex = 0,
+                    Template = GetTemplate(),
+                };
+
+                var other = new Control { Focusable = true };
+
+                StackPanel panel;
+
+                var root = new TestRoot { Child = panel = new StackPanel { Children = { target, other } } };
+
+                target.ApplyTemplate();
+                target.Presenter.ApplyTemplate();
+
+                other.Focus();
+
+                Assert.True(other.IsFocused);
+
+                panel.Children.Remove(target);
+
+                Assert.True(other.IsFocused);
+            }
+        }
     }
 }

+ 27 - 1
tests/Avalonia.Controls.UnitTests/Primitives/PopupTests.cs

@@ -223,7 +223,33 @@ namespace Avalonia.Controls.UnitTests.Primitives
             }
         }
 
-        
+        [Fact]
+        public void Popup_Close_On_Closed_Popup_Should_Not_Raise_Closed_Event()
+        {
+            using (CreateServices())
+            {
+                var window = PreparedWindow();
+                var target = new Popup() { PlacementMode = PlacementMode.Pointer };
+
+                window.Content = target;
+                window.ApplyTemplate();
+                
+                int closedCount = 0;
+
+                target.Closed += (sender, args) =>
+                {
+                    closedCount++;
+                };
+
+                target.Close();
+                target.Close();
+                target.Close();
+                target.Close();
+
+                Assert.Equal(0, closedCount);
+            }
+        }
+
         [Fact]
         public void Templated_Control_With_Popup_In_Template_Should_Set_TemplatedParent()
         {

+ 24 - 0
tests/Avalonia.Controls.UnitTests/ScrollViewerTests.cs

@@ -65,6 +65,30 @@ namespace Avalonia.Controls.UnitTests
             Assert.Equal(new Vector(10, 10), target.Offset);
         }
 
+        [Fact]
+        public void Test_ScrollToHome()
+        {
+            var target = new ScrollViewer();
+            target.SetValue(ScrollViewer.ExtentProperty, new Size(50, 50));
+            target.SetValue(ScrollViewer.ViewportProperty, new Size(10, 10));
+            target.Offset = new Vector(25, 25);
+            target.ScrollToHome();
+
+            Assert.Equal(new Vector(0, 0), target.Offset);
+        }
+
+        [Fact]
+        public void Test_ScrollToEnd()
+        {
+            var target = new ScrollViewer();
+            target.SetValue(ScrollViewer.ExtentProperty, new Size(50, 50));
+            target.SetValue(ScrollViewer.ViewportProperty, new Size(10, 10));
+            target.Offset = new Vector(25, 25);
+            target.ScrollToEnd();
+
+            Assert.Equal(new Vector(0, 40), target.Offset);
+        }
+
         private Control CreateTemplate(ScrollViewer control, INameScope scope)
         {
             return new Grid