From 9aec3e348b336cfda48122c83ce75cae008432a6 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Sat, 8 Apr 2023 18:09:38 +0600 Subject: [PATCH] Properly notify the TopLevel before destroying render targets --- src/Avalonia.Controls/TopLevel.cs | 14 ++++++++------ src/Avalonia.X11/X11Window.cs | 19 +++++++++++++++---- .../Avalonia.Win32/WindowImpl.AppWndProc.cs | 15 +++++++++++++++ src/Windows/Avalonia.Win32/WindowImpl.cs | 11 ----------- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs index 2cd7a6f5ef..b78437b559 100644 --- a/src/Avalonia.Controls/TopLevel.cs +++ b/src/Avalonia.Controls/TopLevel.cs @@ -527,6 +527,13 @@ namespace Avalonia.Controls /// protected virtual void HandleClosed() { + Renderer.SceneInvalidated -= SceneInvalidated; + // We need to wait for the renderer to complete any in-flight operations + Renderer.Dispose(); + + // The PlatformImpl is completely invalid at this point + PlatformImpl = null; + if (_globalStyles is object) { _globalStyles.GlobalStylesAdded -= ((IStyleHost)this).StylesAdded; @@ -536,10 +543,7 @@ namespace Avalonia.Controls { _applicationThemeHost.ActualThemeVariantChanged -= GlobalActualThemeVariantChanged; } - - Renderer.SceneInvalidated -= SceneInvalidated; - Renderer.Dispose(); - + _layoutDiagnosticBridge?.Dispose(); _layoutDiagnosticBridge = null; @@ -547,8 +551,6 @@ namespace Avalonia.Controls _pointerOverPreProcessorSubscription?.Dispose(); _backGestureSubscription?.Dispose(); - PlatformImpl = null; - var logicalArgs = new LogicalTreeAttachmentEventArgs(this, this, null); ((ILogical)this).NotifyDetachedFromLogicalTree(logicalArgs); diff --git a/src/Avalonia.X11/X11Window.cs b/src/Avalonia.X11/X11Window.cs index 9665782323..ecd2726e09 100644 --- a/src/Avalonia.X11/X11Window.cs +++ b/src/Avalonia.X11/X11Window.cs @@ -48,6 +48,7 @@ namespace Avalonia.X11 private readonly X11NativeControlHost _nativeControlHost; private PixelPoint? _position; private PixelSize _realSize; + private bool _cleaningUp; private IntPtr _handle; private IntPtr _xic; private IntPtr _renderHandle; @@ -522,7 +523,7 @@ namespace Avalonia.X11 else if (ev.type == XEventName.DestroyNotify && ev.DestroyWindowEvent.window == _handle) { - Cleanup(); + Cleanup(true); } else if (ev.type == XEventName.ClientMessage) { @@ -816,7 +817,7 @@ namespace Avalonia.X11 public void Dispose() { - Cleanup(); + Cleanup(false); } public virtual object? TryGetFeature(Type featureType) @@ -849,8 +850,17 @@ namespace Avalonia.X11 return null; } - private void Cleanup() + private void Cleanup(bool fromDestroyNotification) { + // Prevent reentrancy + if(_cleaningUp) + return; + _cleaningUp = true; + + // Before doing anything else notify the TopLevel that ITopLevelImpl is no longer valid + if (_handle != IntPtr.Zero) + Closed?.Invoke(); + if (_rawEventGrouper != null) { _rawEventGrouper.Dispose(); @@ -891,7 +901,8 @@ namespace Avalonia.X11 Closed?.Invoke(); _mouse.Dispose(); _touch.Dispose(); - XDestroyWindow(_x11.Display, handle); + if (!fromDestroyNotification) + XDestroyWindow(_x11.Display, handle); } if (_useRenderWindow && _renderHandle != IntPtr.Zero) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs index 84f27f080e..2e09a38bad 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs @@ -85,6 +85,9 @@ namespace Avalonia.Win32 case WindowsMessage.WM_DESTROY: { + // The first and foremost thing to do - notify the TopLevel + Closed?.Invoke(); + if (UiaCoreTypesApi.IsNetComInteropAvailable) { UiaCoreProviderApi.UiaReturnRawElementProvider(_hwnd, IntPtr.Zero, IntPtr.Zero, null); @@ -95,6 +98,18 @@ namespace Avalonia.Win32 { Imm32InputMethod.Current.ClearLanguageAndWindow(); } + + // Cleanup render targets + (_gl as IDisposable)?.Dispose(); + + if (_dropTarget != null) + { + OleContext.Current?.UnregisterDragDrop(Handle); + _dropTarget.Dispose(); + _dropTarget = null; + } + + _framebuffer.Dispose(); //Window doesn't exist anymore _hwnd = IntPtr.Zero; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 9dbb7b77d8..545513c732 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -624,15 +624,6 @@ namespace Avalonia.Win32 public void Dispose() { - (_gl as IDisposable)?.Dispose(); - - if (_dropTarget != null) - { - OleContext.Current?.UnregisterDragDrop(Handle); - _dropTarget.Dispose(); - _dropTarget = null; - } - if (_hwnd != IntPtr.Zero) { // Detect if we are being closed programmatically - this would mean that WM_CLOSE was not called @@ -645,8 +636,6 @@ namespace Avalonia.Win32 DestroyWindow(_hwnd); _hwnd = IntPtr.Zero; } - - _framebuffer.Dispose(); } public void Invalidate(Rect rect)