Check that wxPaintDC is created correctly in code using wxMSW
Creating wxPaintDC for a window outside of any wxEVT_PAINT handler already resulted in assert failures and crash due to using the empty wxDidCreatePaintDC stack, but the assert message was not really clear, so improve it by stating explicitly that wxPaintDC can only be created from wxEVT_PAINT handlers. Also check that wxPaintDC is being created for the correct window: this wasn't detected at all before, but could still result in a lot of grief, so check for this too. Finally, create a new private header with the paint data stack variable declaration instead of using "extern" to declare it manually in wxDC code.
This commit is contained in:
39
include/wx/msw/private/paint.h
Normal file
39
include/wx/msw/private/paint.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Name: wx/msw/private/paint.h
|
||||||
|
// Purpose: Helpers for handling repainting
|
||||||
|
// Author: Vadim Zeitlin
|
||||||
|
// Created: 2020-02-10
|
||||||
|
// Copyright: (c) 2020 Vadim Zeitlin <vadim@wxwidgets.org>
|
||||||
|
// Licence: wxWindows licence
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#ifndef _WX_MSW_PRIVATE_PAINT_H_
|
||||||
|
#define _WX_MSW_PRIVATE_PAINT_H_
|
||||||
|
|
||||||
|
#include "wx/stack.h"
|
||||||
|
|
||||||
|
namespace wxMSWImpl
|
||||||
|
{
|
||||||
|
|
||||||
|
// Data used by WM_PAINT handler
|
||||||
|
struct PaintData
|
||||||
|
{
|
||||||
|
explicit PaintData(wxWindowMSW* window_)
|
||||||
|
: window(window_),
|
||||||
|
createdPaintDC(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// The window being repainted (never null).
|
||||||
|
wxWindowMSW* const window;
|
||||||
|
|
||||||
|
// True if the user-defined paint handler created wxPaintDC.
|
||||||
|
bool createdPaintDC;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Stack storing data for the possibly nested WM_PAINT handlers.
|
||||||
|
extern wxStack<PaintData> paintStack;
|
||||||
|
|
||||||
|
} // namespace wxMSWImpl
|
||||||
|
|
||||||
|
#endif // _WX_MSW_PRIVATE_PAINT_H_
|
@@ -36,6 +36,7 @@
|
|||||||
#include "wx/stack.h"
|
#include "wx/stack.h"
|
||||||
|
|
||||||
#include "wx/msw/private.h"
|
#include "wx/msw/private.h"
|
||||||
|
#include "wx/msw/private/paint.h"
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// local data structures
|
// local data structures
|
||||||
@@ -257,11 +258,13 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) :
|
|||||||
{
|
{
|
||||||
wxCHECK_RET( window, wxT("NULL canvas in wxPaintDCImpl ctor") );
|
wxCHECK_RET( window, wxT("NULL canvas in wxPaintDCImpl ctor") );
|
||||||
|
|
||||||
// see comments in src/msw/window.cpp where this is defined
|
using namespace wxMSWImpl;
|
||||||
extern wxStack<bool> wxDidCreatePaintDC;
|
wxCHECK_RET( !paintStack.empty(),
|
||||||
|
"wxPaintDC can't be created outside wxEVT_PAINT handler" );
|
||||||
wxDidCreatePaintDC.top() = true;
|
wxCHECK_RET( paintStack.top().window == window,
|
||||||
|
"wxPaintDC must be associated with the window being repainted" );
|
||||||
|
|
||||||
|
paintStack.top().createdPaintDC = true;
|
||||||
|
|
||||||
m_window = window;
|
m_window = window;
|
||||||
|
|
||||||
|
@@ -63,7 +63,6 @@
|
|||||||
#include "wx/popupwin.h"
|
#include "wx/popupwin.h"
|
||||||
#include "wx/power.h"
|
#include "wx/power.h"
|
||||||
#include "wx/scopeguard.h"
|
#include "wx/scopeguard.h"
|
||||||
#include "wx/stack.h"
|
|
||||||
#include "wx/sysopt.h"
|
#include "wx/sysopt.h"
|
||||||
|
|
||||||
#if wxUSE_DRAG_AND_DROP
|
#if wxUSE_DRAG_AND_DROP
|
||||||
@@ -81,6 +80,7 @@
|
|||||||
|
|
||||||
#include "wx/msw/private.h"
|
#include "wx/msw/private.h"
|
||||||
#include "wx/msw/private/keyboard.h"
|
#include "wx/msw/private/keyboard.h"
|
||||||
|
#include "wx/msw/private/paint.h"
|
||||||
#include "wx/msw/private/winstyle.h"
|
#include "wx/msw/private/winstyle.h"
|
||||||
#include "wx/msw/dcclient.h"
|
#include "wx/msw/dcclient.h"
|
||||||
#include "wx/msw/seh.h"
|
#include "wx/msw/seh.h"
|
||||||
@@ -5240,8 +5240,7 @@ wxColour wxWindowMSW::MSWGetThemeColour(const wchar_t *themeName,
|
|||||||
// endless stream of WM_PAINT messages for this window resulting in a lot of
|
// endless stream of WM_PAINT messages for this window resulting in a lot of
|
||||||
// difficult to debug problems (e.g. impossibility to repaint other windows,
|
// difficult to debug problems (e.g. impossibility to repaint other windows,
|
||||||
// lack of timer and idle events and so on)
|
// lack of timer and idle events and so on)
|
||||||
extern wxStack<bool> wxDidCreatePaintDC;
|
wxStack<wxMSWImpl::PaintData> wxMSWImpl::paintStack;
|
||||||
wxStack<bool> wxDidCreatePaintDC;
|
|
||||||
|
|
||||||
bool wxWindowMSW::HandlePaint()
|
bool wxWindowMSW::HandlePaint()
|
||||||
{
|
{
|
||||||
@@ -5257,14 +5256,17 @@ bool wxWindowMSW::HandlePaint()
|
|||||||
|
|
||||||
m_updateRegion = wxRegion((WXHRGN) hRegion);
|
m_updateRegion = wxRegion((WXHRGN) hRegion);
|
||||||
|
|
||||||
wxDidCreatePaintDC.push(false);
|
using namespace wxMSWImpl;
|
||||||
|
|
||||||
|
paintStack.push(PaintData(this));
|
||||||
|
|
||||||
wxPaintEvent event(m_windowId);
|
wxPaintEvent event(m_windowId);
|
||||||
event.SetEventObject(this);
|
event.SetEventObject(this);
|
||||||
|
|
||||||
bool processed = HandleWindowEvent(event);
|
bool processed = HandleWindowEvent(event);
|
||||||
|
|
||||||
if ( wxDidCreatePaintDC.top() && !processed )
|
const bool createdPaintDC = paintStack.top().createdPaintDC;
|
||||||
|
if ( createdPaintDC && !processed )
|
||||||
{
|
{
|
||||||
// Event handler did paint something as wxPaintDC object was created
|
// Event handler did paint something as wxPaintDC object was created
|
||||||
// but then it must have skipped the event to indicate that default
|
// but then it must have skipped the event to indicate that default
|
||||||
@@ -5288,16 +5290,16 @@ bool wxWindowMSW::HandlePaint()
|
|||||||
|
|
||||||
wxPaintDCImpl::EndPaint((wxWindow *)this);
|
wxPaintDCImpl::EndPaint((wxWindow *)this);
|
||||||
|
|
||||||
|
paintStack.pop();
|
||||||
|
|
||||||
// It doesn't matter whether the event was actually processed or not here,
|
// It doesn't matter whether the event was actually processed or not here,
|
||||||
// what matters is whether we already painted, and hence validated, the
|
// what matters is whether we already painted, and hence validated, the
|
||||||
// window or not. If we did, either the event was processed or we called
|
// window or not. If we did, either the event was processed or we called
|
||||||
// OnPaint() above, so we should return true. If we did not, even the event
|
// OnPaint() above, so we should return true. If we did not, even the event
|
||||||
// was processed, we must still call MSWDefWindowProc() to ensure that the
|
// was processed, we must still call MSWDefWindowProc() to ensure that the
|
||||||
// window is validated, i.e. to avoid the problem described in the comment
|
// window is validated, i.e. to avoid the problem described in the comment
|
||||||
// before wxDidCreatePaintDC definition above.
|
// before paintStack definition above.
|
||||||
const bool ret = wxDidCreatePaintDC.top();
|
return createdPaintDC;
|
||||||
wxDidCreatePaintDC.pop();
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can be called from an application's OnPaint handler
|
// Can be called from an application's OnPaint handler
|
||||||
|
Reference in New Issue
Block a user