From e2a07cc04d274c98710649090f8abdf90cbe7785 Mon Sep 17 00:00:00 2001 From: Nikita Tsukanov Date: Tue, 24 Sep 2024 14:17:47 +0500 Subject: [PATCH] [AVN] Introduced ComObjectWeakPtr (#17041) --- native/Avalonia.Native/inc/comimpl.h | 63 ++++++++++++ native/Avalonia.Native/src/OSX/AvnView.mm | 81 ++++++++------- native/Avalonia.Native/src/OSX/AvnWindow.mm | 99 ++++++++++--------- .../src/OSX/IWindowStateChanged.h | 2 +- 4 files changed, 165 insertions(+), 80 deletions(-) diff --git a/native/Avalonia.Native/inc/comimpl.h b/native/Avalonia.Native/inc/comimpl.h index fd9093bb419..e16adb1ec25 100644 --- a/native/Avalonia.Native/inc/comimpl.h +++ b/native/Avalonia.Native/inc/comimpl.h @@ -7,6 +7,7 @@ #define COMIMPL_H_INCLUDED #include +#include /** START_COM_CALL causes AddRef to be called at the beginning of a function. @@ -100,6 +101,13 @@ class ComPtr return _obj; } + template ComPtr dynamicCast() + { + if(_obj == nullptr) + return nullptr; + return dynamic_cast(_obj); + } + TInterface** getPPV() { return &_obj; @@ -130,10 +138,18 @@ class ComPtr } }; +class ComObjectWeakRefToken +{ +public: + bool Alive = true; +}; + + class ComObject : public virtual IUnknown { private: unsigned int _refCount; + std::shared_ptr _weakRefs; public: virtual ULONG AddRef() @@ -157,10 +173,22 @@ class ComObject : public virtual IUnknown _refCount = 1; } + virtual ~ComObject() { + if(_weakRefs) + _weakRefs->Alive = false; } + std::shared_ptr __GetWeakRefToken() + { + if(_weakRefs == nullptr) + { + _weakRefs = std::make_shared(); + } + return _weakRefs; + } + virtual ::HRESULT STDMETHODCALLTYPE QueryInterfaceImpl(REFIID riid, void **ppvObject) = 0; @@ -188,6 +216,41 @@ class ComObject : public virtual IUnknown }; +template +class ComObjectWeakPtr +{ +private: + std::shared_ptr _token; + TClass* _rawPtr; +public: + ComPtr tryGet() + { + if(_rawPtr == nullptr) + return nullptr; + if(_token->Alive) + return _rawPtr; + return nullptr; + } + + template ComPtr tryGetWithCast() + { + return tryGet().template dynamicCast(); + } + + ComObjectWeakPtr(TClass* obj) + { + _rawPtr = obj; + if(obj) + _token = obj->__GetWeakRefToken(); + } + + ComObjectWeakPtr() + { + _rawPtr = nullptr; + _token = nullptr; + } +}; + #define FORWARD_IUNKNOWN() \ virtual ULONG Release() override \ { \ diff --git a/native/Avalonia.Native/src/OSX/AvnView.mm b/native/Avalonia.Native/src/OSX/AvnView.mm index dad670c3d1e..b4cd7511966 100644 --- a/native/Avalonia.Native/src/OSX/AvnView.mm +++ b/native/Avalonia.Native/src/OSX/AvnView.mm @@ -11,7 +11,7 @@ @implementation AvnView { - ComPtr _parent; + ComObjectWeakPtr _parent; NSTrackingArea* _area; bool _isLeftPressed, _isMiddlePressed, _isRightPressed, _isXButton1Pressed, _isXButton2Pressed; AvnInputModifiers _modifierState; @@ -132,7 +132,8 @@ -(void)setFrameSize:(NSSize)newSize _area = nullptr; } - if (_parent == nullptr) + auto parent = _parent.tryGet(); + if (parent == nullptr) { return; } @@ -144,7 +145,7 @@ -(void)setFrameSize:(NSSize)newSize _area = [[NSTrackingArea alloc] initWithRect:rect options:options owner:self userInfo:nullptr]; [self addTrackingArea:_area]; - _parent->UpdateCursor(); + parent->UpdateCursor(); auto fsize = [self convertSizeToBacking: [self frame].size]; @@ -156,26 +157,28 @@ -(void)setFrameSize:(NSSize)newSize auto reason = [self inLiveResize] ? ResizeUser : _resizeReason; - _parent->TopLevelEvents->Resized(FromNSSize(newSize), reason); + parent->TopLevelEvents->Resized(FromNSSize(newSize), reason); } } - (void)updateLayer { AvnInsidePotentialDeadlock deadlock; - if (_parent == nullptr) + auto parent = _parent.tryGet(); + if (parent == nullptr) { return; } - _parent->TopLevelEvents->RunRenderPriorityJobs(); + parent->TopLevelEvents->RunRenderPriorityJobs(); - if (_parent == nullptr) + parent = _parent.tryGet(); + if (parent == nullptr) { return; } - _parent->TopLevelEvents->Paint(); + parent->TopLevelEvents->Paint(); } - (void)drawRect:(NSRect)dirtyRect @@ -196,9 +199,10 @@ - (void) viewDidChangeBackingProperties _lastPixelSize.Height = (int)fsize.height; [self updateRenderTarget]; - if(_parent != nullptr) + auto parent = _parent.tryGet(); + if(parent != nullptr) { - _parent->TopLevelEvents->ScalingChanged([[self window] backingScaleFactor]); + parent->TopLevelEvents->ScalingChanged([[self window] backingScaleFactor]); } [super viewDidChangeBackingProperties]; @@ -206,7 +210,8 @@ - (void) viewDidChangeBackingProperties - (bool) ignoreUserInput:(bool)trigerInputWhenDisabled { - if(_parent == nullptr) + auto parent = _parent.tryGet(); + if(parent == nullptr) { return TRUE; } @@ -221,7 +226,7 @@ - (bool) ignoreUserInput:(bool)trigerInputWhenDisabled { if(trigerInputWhenDisabled) { - WindowImpl* windowImpl = dynamic_cast(_parent.getRaw()); + auto windowImpl = _parent.tryGetWithCast(); if(windowImpl == nullptr){ return FALSE; @@ -298,10 +303,10 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type ) ) { - WindowBaseImpl* windowBase = dynamic_cast(_parent.getRaw()); + auto windowBase = _parent.tryGetWithCast(); if(windowBase != nullptr){ - WindowBaseImpl* parent = windowBase->Parent; + auto parent = windowBase->Parent; if(parent != nullptr){ auto parentWindow = parent->Window; @@ -313,10 +318,10 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type } } - - if(_parent != nullptr) + auto parent = _parent.tryGet(); + if(parent != nullptr) { - _parent->TopLevelEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); + parent->TopLevelEvents->RawMouseEvent(type, timestamp, modifiers, point, delta); } [super mouseMoved:event]; @@ -324,7 +329,9 @@ - (void)mouseEvent:(NSEvent *)event withType:(AvnRawMouseEventType) type - (BOOL) resignFirstResponder { - _parent->TopLevelEvents->LostFocus(); + auto parent = _parent.tryGet(); + if(parent) + parent->TopLevelEvents->LostFocus(); return YES; } @@ -463,7 +470,8 @@ - (void)mouseExited:(NSEvent *)event - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type { - if([self ignoreUserInput: false] || _parent == nullptr) + auto parent = _parent.tryGet(); + if([self ignoreUserInput: false] || parent == nullptr) { return; } @@ -477,7 +485,7 @@ - (void) keyboardEvent: (NSEvent *) event withType: (AvnRawKeyEventType)type auto timestamp = static_cast([event timestamp] * 1000); auto modifiers = [self getModifiers:[event modifierFlags]]; - _parent->TopLevelEvents->RawKeyEvent(type, timestamp, modifiers, key, physicalKey, keySymbolUtf8); + parent->TopLevelEvents->RawKeyEvent(type, timestamp, modifiers, key, physicalKey, keySymbolUtf8); } - (void)setModifiers:(NSEventModifierFlags)modifierFlags @@ -551,12 +559,14 @@ - (void)flagsChanged:(NSEvent *)event } - (bool) handleKeyDown: (NSTimeInterval) timestamp withKey:(AvnKey)key withPhysicalKey:(AvnPhysicalKey)physicalKey withModifiers:(AvnInputModifiers)modifiers withKeySymbol:(NSString*)keySymbol { - return _parent->TopLevelEvents->RawKeyEvent(KeyDown, timestamp, modifiers, key, physicalKey, [keySymbol UTF8String]); + auto parent = _parent.tryGet(); + return parent->TopLevelEvents->RawKeyEvent(KeyDown, timestamp, modifiers, key, physicalKey, [keySymbol UTF8String]); } - (void)keyDown:(NSEvent *)event { - if([self ignoreUserInput: false] || _parent == nullptr) + auto parent = _parent.tryGet(); + if([self ignoreUserInput: false] || parent == nullptr) { return; } @@ -573,7 +583,7 @@ - (void)keyDown:(NSEvent *)event auto modifiers = [self getModifiers:[event modifierFlags]]; //InputMethod is active - if(_parent->InputMethod->IsActive()){ + if(parent->InputMethod->IsActive()){ auto hasInputModifier = modifiers != AvnInputModifiersNone; //Handle keyDown first if an input modifier is present @@ -605,7 +615,7 @@ - (void)keyDown:(NSEvent *)event if(keySymbol != nullptr && key != AvnKeyEnter){ auto timestamp = static_cast([event timestamp] * 1000); - _parent->TopLevelEvents->RawTextInputEvent(timestamp, [keySymbol UTF8String]); + parent->TopLevelEvents->RawTextInputEvent(timestamp, [keySymbol UTF8String]); } } } @@ -681,16 +691,18 @@ - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacemen } _markedRange = NSMakeRange(_selectedRange.location, [markedText length]); - - if(_parent->InputMethod->IsActive()){ - _parent->InputMethod->Client->SetPreeditText((char*)[markedText UTF8String]); + auto parent = _parent.tryGet(); + + if(parent->InputMethod->IsActive()){ + parent->InputMethod->Client->SetPreeditText((char*)[markedText UTF8String]); } } - (void)unmarkText { - if(_parent->InputMethod->IsActive()){ - _parent->InputMethod->Client->SetPreeditText(nullptr); + auto parent = _parent.tryGet(); + if(parent->InputMethod->IsActive()){ + parent->InputMethod->Client->SetPreeditText(nullptr); } _markedRange = NSMakeRange(_selectedRange.location, 0); @@ -718,7 +730,8 @@ - (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actua - (void)insertText:(id)string replacementRange:(NSRange)replacementRange { - if(_parent == nullptr){ + auto parent = _parent.tryGet(); + if(parent == nullptr){ return; } @@ -737,7 +750,7 @@ - (void)insertText:(id)string replacementRange:(NSRange)replacementRange uint64_t timestamp = static_cast([NSDate timeIntervalSinceReferenceDate] * 1000); - _parent->TopLevelEvents->RawTextInputEvent(timestamp, [text UTF8String]); + parent->TopLevelEvents->RawTextInputEvent(timestamp, [text UTF8String]); } - (NSUInteger)characterIndexForPoint:(NSPoint)point @@ -747,7 +760,8 @@ - (NSUInteger)characterIndexForPoint:(NSPoint)point - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(NSRangePointer)actualRange { - if(!_parent->InputMethod->IsActive()){ + auto parent = _parent.tryGet(); + if(!parent->InputMethod->IsActive()){ return NSZeroRect; } @@ -763,7 +777,8 @@ - (NSDragOperation)triggerAvnDragEvent: (AvnDragEventType) type info: (id TopLevelEvents + auto parent = _parent.tryGet(); + int reffects = (int)parent->TopLevelEvents ->DragEvent(type, point, modifiers, effects, CreateClipboard([info draggingPasteboard], nil), GetAvnDataObjectHandleFromDraggingInfo(info)); diff --git a/native/Avalonia.Native/src/OSX/AvnWindow.mm b/native/Avalonia.Native/src/OSX/AvnWindow.mm index 2f603f0cade..e6d451d0bf4 100644 --- a/native/Avalonia.Native/src/OSX/AvnWindow.mm +++ b/native/Avalonia.Native/src/OSX/AvnWindow.mm @@ -29,7 +29,7 @@ @implementation CLASS_NAME { - ComPtr _parent; + ComObjectWeakPtr _parent; bool _closed; bool _isEnabled; bool _canBecomeKeyWindow; @@ -42,7 +42,8 @@ @implementation CLASS_NAME -(AvnView* _Nullable) view { - return _parent->View; + auto parent = _parent.tryGet(); + return parent ? parent->View : nullptr; } -(void) setIsExtended:(bool)value; @@ -52,7 +53,8 @@ -(void) setIsExtended:(bool)value; -(bool) isDialog { - return _parent->IsModal(); + auto parent = _parent.tryGet(); + return parent ? parent->IsModal() : false; } -(double) getExtendedTitleBarHeight @@ -194,7 +196,7 @@ -(CLASS_NAME*_Nonnull) initWithParent: (WindowBaseImpl*_Nonnull) parent content - (BOOL)windowShouldClose:(NSWindow *_Nonnull)sender { - auto window = dynamic_cast(_parent.getRaw()); + auto window = _parent.tryGet().dynamicCast(); if(window != nullptr) { @@ -212,20 +214,17 @@ - (void)windowDidChangeBackingProperties:(NSNotification *_Nonnull)notification - (void)windowWillClose:(NSNotification *_Nonnull)notification { _closed = true; - if(_parent) + auto window = _parent.tryGetWithCast(); + if(window) { - ComPtr parent = _parent; - _parent = NULL; - - auto window = dynamic_cast(parent.getRaw()); if(window != nullptr) { window->SetParent(nullptr); } - parent->BaseEvents->Closed(); - [parent->View onClosed]; + window->BaseEvents->Closed(); + [window->View onClosed]; } } @@ -247,7 +246,7 @@ -(BOOL)canBecomeKeyWindow if(_canBecomeKeyWindow && !_closed) { // If the window has a child window being shown as a dialog then don't allow it to become the key window. - auto parent = dynamic_cast(_parent.getRaw()); + auto parent = _parent.tryGet().dynamicCast(); if(parent != nullptr) { @@ -286,9 +285,10 @@ -(void)becomeKeyWindow { [self showWindowMenuWithAppMenu]; - if(_parent != nullptr) + auto parent = _parent.tryGet(); + if(parent != nullptr) { - _parent->BaseEvents->Activated(); + parent->BaseEvents->Activated(); } [super becomeKeyWindow]; @@ -296,19 +296,21 @@ -(void)becomeKeyWindow - (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification { - if (_parent == nullptr) + auto parent = _parent.tryGet(); + if (parent == nullptr) return; - if (_parent->View != nullptr) - [_parent->View setModifiers:NSEvent.modifierFlags]; + if (parent->View != nullptr) + [parent->View setModifiers:NSEvent.modifierFlags]; - _parent->BringToFront(); + parent->BringToFront(); dispatch_async(dispatch_get_main_queue(), ^{ @try { [self invalidateShadow]; - if (self->_parent != nullptr) - self->_parent->BringToFront(); + auto parent = self->_parent.tryGet(); + if (parent != nullptr) + parent->BringToFront(); } @finally{ } @@ -317,7 +319,7 @@ - (void)windowDidBecomeKey:(NSNotification *_Nonnull)notification - (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification { - auto parent = dynamic_cast(_parent.operator->()); + auto parent = _parent.tryGetWithCast(); if(parent != nullptr) { @@ -327,7 +329,7 @@ - (void)windowDidMiniaturize:(NSNotification *_Nonnull)notification - (void)windowDidDeminiaturize:(NSNotification *_Nonnull)notification { - auto parent = dynamic_cast(_parent.operator->()); + auto parent = _parent.tryGetWithCast(); if(parent != nullptr) { @@ -337,7 +339,7 @@ - (void)windowDidDeminiaturize:(NSNotification *_Nonnull)notification - (void)windowDidResize:(NSNotification *_Nonnull)notification { - auto parent = dynamic_cast(_parent.operator->()); + auto parent = _parent.tryGetWithCast(); if(parent != nullptr) { @@ -347,7 +349,7 @@ - (void)windowDidResize:(NSNotification *_Nonnull)notification - (void)windowWillExitFullScreen:(NSNotification *_Nonnull)notification { - auto parent = dynamic_cast(_parent.operator->()); + auto parent = _parent.tryGetWithCast(); if(parent != nullptr) { @@ -357,7 +359,7 @@ - (void)windowWillExitFullScreen:(NSNotification *_Nonnull)notification - (void)windowDidExitFullScreen:(NSNotification *_Nonnull)notification { - auto parent = dynamic_cast(_parent.operator->()); + auto parent = _parent.tryGetWithCast(); if(parent != nullptr) { @@ -381,7 +383,7 @@ - (void)windowDidExitFullScreen:(NSNotification *_Nonnull)notification - (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification { _isTransitioningToFullScreen = true; - auto parent = dynamic_cast(_parent.operator->()); + auto parent = _parent.tryGetWithCast(); if(parent != nullptr) { @@ -392,7 +394,7 @@ - (void)windowWillEnterFullScreen:(NSNotification *_Nonnull)notification - (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification { _isTransitioningToFullScreen = false; - auto parent = dynamic_cast(_parent.operator->()); + auto parent = _parent.tryGetWithCast(); if(parent != nullptr) { @@ -403,13 +405,15 @@ - (void)windowDidEnterFullScreen:(NSNotification *_Nonnull)notification - (BOOL)windowShouldZoom:(NSWindow *_Nonnull)window toFrame:(NSRect)newFrame { - return _parent->CanZoom(); + auto parent = _parent.tryGet(); + return parent ? parent->CanZoom() : false; } -(void)windowDidResignKey:(NSNotification* _Nonnull)notification { - if(_parent) - _parent->BaseEvents->Deactivated(); + auto parent = _parent.tryGet(); + if(parent) + parent->BaseEvents->Deactivated(); [self showAppMenuOnly]; @@ -420,25 +424,25 @@ - (void)windowDidMove:(NSNotification *_Nonnull)notification { AvnPoint position; - if(_parent != nullptr) + auto parent = _parent.tryGet(); + if(parent != nullptr) { - auto cparent = dynamic_cast(_parent.getRaw()); - - if(cparent != nullptr) + auto window = parent.dynamicCast(); + if(window != nullptr) { - if(!cparent->IsShown()) + if(!window->IsShown()) { return; } - if(cparent->WindowState() == Maximized) + if(window->WindowState() == Maximized) { - cparent->SetWindowState(Normal); + window->SetWindowState(Normal); } } - _parent->GetPosition(&position); - _parent->BaseEvents->PositionChanged(position); + parent->GetPosition(&position); + parent->BaseEvents->PositionChanged(position); } } @@ -452,14 +456,15 @@ - (void)sendEvent:(NSEvent *_Nonnull)event { [super sendEvent:event]; + auto parent = _parent.tryGetWithCast(); /// This is to detect non-client clicks. This can only be done on Windows... not popups, hence the dynamic_cast. - if(_parent != nullptr && dynamic_cast(_parent.getRaw()) != nullptr) + if(parent) { switch(event.type) { case NSEventTypeLeftMouseDown: { - AvnView* view = _parent->View; + AvnView* view = parent->View; NSPoint windowPoint = [event locationInWindow]; NSPoint viewPoint = [view convertPoint:windowPoint fromView:nil]; @@ -469,19 +474,19 @@ - (void)sendEvent:(NSEvent *_Nonnull)event auto point = [self translateLocalPoint:avnPoint]; AvnVector delta = { 0, 0 }; - _parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); + parent->BaseEvents->RawMouseEvent(NonClientLeftButtonDown, static_cast([event timestamp] * 1000), AvnInputModifiersNone, point, delta); } if(!_isTransitioningToFullScreen) { - _parent->BringToFront(); + parent->BringToFront(); } } break; case NSEventTypeMouseEntered: { - _parent->UpdateCursor(); + parent->UpdateCursor(); } break; @@ -516,9 +521,10 @@ - (NSString * _Nullable) accessibilityIdentifier - (IAvnAutomationPeer* _Nonnull) automationPeer { + auto parent = _parent.tryGet(); if (_automationPeer == nullptr) { - _automationPeer = _parent->BaseEvents->GetAutomationPeer(); + _automationPeer = parent->BaseEvents->GetAutomationPeer(); _automationNode = new AvnAutomationNode(self); _automationPeer->SetNode(_automationNode); } @@ -528,7 +534,8 @@ - (IAvnAutomationPeer* _Nonnull) automationPeer - (void)raiseChildrenChanged { - [_parent->View raiseAccessibilityChildrenChanged]; + auto parent = _parent.tryGet(); + [parent->View raiseAccessibilityChildrenChanged]; } - (void)raiseFocusChanged diff --git a/native/Avalonia.Native/src/OSX/IWindowStateChanged.h b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h index f0905da3ac4..11478724bfc 100644 --- a/native/Avalonia.Native/src/OSX/IWindowStateChanged.h +++ b/native/Avalonia.Native/src/OSX/IWindowStateChanged.h @@ -6,7 +6,7 @@ #ifndef AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H #define AVALONIA_NATIVE_OSX_IWINDOWSTATECHANGED_H -struct IWindowStateChanged +struct IWindowStateChanged: public IUnknown { virtual void WindowStateChanged () = 0; virtual void StartStateTransition () = 0;