From 2d145378f68347bef1dc25356c0a367bed304cd6 Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 18 Aug 2013 13:28:16 +0000 Subject: [PATCH] Make mouse capture checking asserts stronger and more detailed. Verify not only that we don't destroy the window having the capture now but also that this window is not in the mouse capture stack at all, not necessarily on top. This is important as keeping a dangling pointer in the capture stack would result in difficult to diagnose bugs later. Also check that we don't recapture the mouse in the same window as this should never be necessary. Finally, give more details in the assert checking that the window does have capture in ReleaseMouse(). git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74677 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- src/common/wincmn.cpp | 58 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/src/common/wincmn.cpp b/src/common/wincmn.cpp index 8fd56980d5..5d50b212b2 100644 --- a/src/common/wincmn.cpp +++ b/src/common/wincmn.cpp @@ -89,6 +89,14 @@ wxMenu *wxCurrentPopupMenu = NULL; extern WXDLLEXPORT_DATA(const char) wxPanelNameStr[] = "panel"; +namespace wxMouseCapture +{ + +// Check if the given window is in the capture stack. +bool IsInCaptureStack(wxWindowBase* win); + +} // wxMouseCapture + // ---------------------------------------------------------------------------- // static data // ---------------------------------------------------------------------------- @@ -448,7 +456,9 @@ bool wxWindowBase::ToggleWindowStyle(int flag) // common clean up wxWindowBase::~wxWindowBase() { - wxASSERT_MSG( GetCapture() != this, wxT("attempt to destroy window with mouse capture") ); + wxASSERT_MSG( !wxMouseCapture::IsInCaptureStack(this), + "Destroying window before releasing mouse capture: this " + "will result in a crash later." ); // FIXME if these 2 cases result from programming errors in the user code // we should probably assert here instead of silently fixing them @@ -3218,6 +3228,19 @@ wxVector stack; // Flag preventing reentrancy in {Capture,Release}Mouse(). wxRecursionGuardFlag changing; +bool IsInCaptureStack(wxWindowBase* win) +{ + for ( wxVector::const_iterator it = stack.begin(); + it != stack.end(); + ++it ) + { + if ( *it == win ) + return true; + } + + return false; +} + } // wxMouseCapture void wxWindowBase::CaptureMouse() @@ -3227,6 +3250,9 @@ void wxWindowBase::CaptureMouse() wxRecursionGuard guard(wxMouseCapture::changing); wxASSERT_MSG( !guard.IsInside(), wxT("recursive CaptureMouse call?") ); + wxASSERT_MSG( !wxMouseCapture::IsInCaptureStack(this), + "Recapturing the mouse in the same window?" ); + wxWindow *winOld = GetCapture(); if ( winOld ) ((wxWindowBase*) winOld)->DoReleaseMouse(); @@ -3243,8 +3269,32 @@ void wxWindowBase::ReleaseMouse() wxRecursionGuard guard(wxMouseCapture::changing); wxASSERT_MSG( !guard.IsInside(), wxT("recursive ReleaseMouse call?") ); - wxASSERT_MSG( GetCapture() == this, - "attempt to release mouse, but this window hasn't captured it" ); +#if wxDEBUG_LEVEL + wxWindow* const winCapture = GetCapture(); + if ( !winCapture ) + { + wxFAIL_MSG + ( + wxString::Format + ( + "Releasing mouse in %p(%s) but it is not captured", + this, GetClassInfo()->GetClassName() + ) + ); + } + else if ( winCapture != this ) + { + wxFAIL_MSG + ( + wxString::Format + ( + "Releasing mouse in %p(%s) but it is captured by %p(%s)", + this, GetClassInfo()->GetClassName(), + winCapture, winCapture->GetClassInfo()->GetClassName() + ) + ); + } +#endif // wxDEBUG_LEVEL DoReleaseMouse(); @@ -3262,7 +3312,7 @@ void wxWindowBase::ReleaseMouse() } wxLogTrace(wxT("mousecapture"), - (const wxChar *) wxT("After ReleaseMouse() mouse is captured by %p"), + wxT("After ReleaseMouse() mouse is captured by %p"), static_cast(GetCapture())); } -- 2.45.2