X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0b822969a677939811d9fb966177031fb5fc625b..43c42c18d36c703a88b1b7b697bac27fe5608eca:/src/msw/dcclient.cpp diff --git a/src/msw/dcclient.cpp b/src/msw/dcclient.cpp index e97fe01af2..1da0e01531 100644 --- a/src/msw/dcclient.cpp +++ b/src/msw/dcclient.cpp @@ -29,6 +29,7 @@ #ifndef WX_PRECOMP #include "wx/string.h" + #include "wx/hashmap.h" #include "wx/log.h" #include "wx/window.h" #endif @@ -36,44 +37,117 @@ #include "wx/msw/private.h" // ---------------------------------------------------------------------------- -// array/list types +// local data structures // ---------------------------------------------------------------------------- -struct WXDLLEXPORT wxPaintDCInfo +// This is a base class for two concrete subclasses below and contains HDC +// cached for the duration of the WM_PAINT processing together with some +// bookkeeping information. +class wxPaintDCInfo { - wxPaintDCInfo(wxWindow *win, wxPaintDCImpl *dc) +public: + wxPaintDCInfo(HDC hdc) + : m_hdc(hdc) { - hwnd = win->GetHWND(); - hdc = dc->GetHDC(); - count = 1; } - WXHWND hwnd; // window for this DC - WXHDC hdc; // the DC handle - size_t count; // usage count + // The derived class must perform some cleanup. + virtual ~wxPaintDCInfo() = 0; + + WXHDC GetHDC() const { return (WXHDC)m_hdc; } + +protected: + const HDC m_hdc; + + wxDECLARE_NO_COPY_CLASS(wxPaintDCInfo); }; -#include "wx/arrimpl.cpp" +namespace +{ + +// This subclass contains information for the HDCs we create ourselves, i.e. +// those for which we call BeginPaint() -- and hence need to call EndPaint(). +class wxPaintDCInfoOur : public wxPaintDCInfo +{ +public: + wxPaintDCInfoOur(wxWindow* win) + : wxPaintDCInfo(::BeginPaint(GetHwndOf(win), GetPaintStructPtr(m_ps))), + m_hwnd(GetHwndOf(win)) + { + } + + virtual ~wxPaintDCInfoOur() + { + ::EndPaint(m_hwnd, &m_ps); + } -WX_DEFINE_OBJARRAY(wxArrayDCInfo) +private: + // This helper is only needed in order to call it from the ctor initializer + // list. + static PAINTSTRUCT* GetPaintStructPtr(PAINTSTRUCT& ps) + { + wxZeroMemory(ps); + return &ps; + } -// ---------------------------------------------------------------------------- -// macros -// ---------------------------------------------------------------------------- + const HWND m_hwnd; + PAINTSTRUCT m_ps; + + wxDECLARE_NO_COPY_CLASS(wxPaintDCInfoOur); +}; + +// This subclass contains information for the HDCs we receive from outside, as +// WPARAM of WM_PAINT itself. +class wxPaintDCInfoExternal : public wxPaintDCInfo +{ +public: + wxPaintDCInfoExternal(HDC hdc) + : wxPaintDCInfo(hdc), + m_state(::SaveDC(hdc)) + { + } + + virtual ~wxPaintDCInfoExternal() + { + ::RestoreDC(m_hdc, m_state); + } + +private: + const int m_state; + + wxDECLARE_NO_COPY_CLASS(wxPaintDCInfoExternal); +}; + +// The global map containing HDC to use for the given window. The entries in +// this map only exist during WM_PAINT processing and are destroyed when it is +// over. +// +// It is needed because in some circumstances it can happen that more than one +// wxPaintDC is created for the same window during its WM_PAINT handling (and +// as this can happen implicitly, e.g. by calling a function in some library, +// this can be quite difficult to find) but we need to reuse the same HDC for +// all of them because we can't call BeginPaint() more than once. So we cache +// the first HDC created for the window in this map and then reuse it later if +// needed. And, of course, remove it from the map when the painting is done. +WX_DECLARE_HASH_MAP(wxWindow *, wxPaintDCInfo *, + wxPointerHash, wxPointerEqual, + PaintDCInfos); + +PaintDCInfos gs_PaintDCInfos; + +} // anonymous namespace // ---------------------------------------------------------------------------- // global variables // ---------------------------------------------------------------------------- -static PAINTSTRUCT g_paintStruct; - -#ifdef __WXDEBUG__ +#ifdef wxHAS_PAINT_DEBUG // a global variable which we check to verify that wxPaintDC are only // created in response to WM_PAINT message - doing this from elsewhere is a // common programming error among wxWidgets programmers and might lead to // very subtle and difficult to debug refresh/repaint bugs. int g_isPainting = 0; -#endif // __WXDEBUG__ +#endif // wxHAS_PAINT_DEBUG // =========================================================================== // implementation @@ -93,7 +167,7 @@ wxWindowDCImpl::wxWindowDCImpl( wxDC *owner ) : wxWindowDCImpl::wxWindowDCImpl( wxDC *owner, wxWindow *window ) : wxMSWDCImpl( owner ) { - wxCHECK_RET( window, _T("invalid window in wxWindowDCImpl") ); + wxCHECK_RET( window, wxT("invalid window in wxWindowDCImpl") ); m_window = window; m_hDC = (WXHDC) ::GetWindowDC(GetHwndOf(m_window)); @@ -109,9 +183,6 @@ void wxWindowDCImpl::InitDC() // DrawText() to OPAQUE as required, otherwise always TRANSPARENT, ::SetBkMode(GetHdc(), TRANSPARENT); - // default bg colour is pne of the window - SetBackground(wxBrush(m_window->GetBackgroundColour(), wxBRUSHSTYLE_SOLID)); - // since we are a window dc we need to grab the palette from the window #if wxUSE_PALETTE InitializePalette(); @@ -120,7 +191,7 @@ void wxWindowDCImpl::InitDC() void wxWindowDCImpl::DoGetSize(int *width, int *height) const { - wxCHECK_RET( m_window, _T("wxWindowDCImpl without a window?") ); + wxCHECK_RET( m_window, wxT("wxWindowDCImpl without a window?") ); m_window->GetSize(width, height); } @@ -139,7 +210,7 @@ wxClientDCImpl::wxClientDCImpl( wxDC *owner ) : wxClientDCImpl::wxClientDCImpl( wxDC *owner, wxWindow *window ) : wxWindowDCImpl( owner ) { - wxCHECK_RET( window, _T("invalid window in wxClientDCImpl") ); + wxCHECK_RET( window, wxT("invalid window in wxClientDCImpl") ); m_window = window; m_hDC = (WXHDC)::GetDC(GetHwndOf(window)); @@ -180,7 +251,7 @@ wxClientDCImpl::~wxClientDCImpl() void wxClientDCImpl::DoGetSize(int *width, int *height) const { - wxCHECK_RET( m_window, _T("wxClientDCImpl without a window?") ); + wxCHECK_RET( m_window, wxT("wxClientDCImpl without a window?") ); m_window->GetClientSize(width, height); } @@ -189,26 +260,8 @@ void wxClientDCImpl::DoGetSize(int *width, int *height) const // wxPaintDCImpl // ---------------------------------------------------------------------------- -// VZ: initial implementation (by JACS) only remembered the last wxPaintDC -// created and tried to reuse it - this was supposed to take care of a -// situation when a derived class OnPaint() calls base class OnPaint() -// because in this case ::BeginPaint() shouldn't be called second time. -// -// I'm not sure how useful this is, however we must remember the HWND -// associated with the last HDC as well - otherwise we may (and will!) try -// to reuse the HDC for another HWND which is a nice recipe for disaster. -// -// So we store a list of windows for which we already have the DC and not -// just one single hDC. This seems to work, but I'm really not sure about -// the usefullness of the whole idea - IMHO it's much better to not call -// base class OnPaint() at all, or, if we really want to allow it, add a -// "wxPaintDC *" parameter to wxPaintEvent which should be used if it's -// !NULL instead of creating a new DC. - IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxClientDCImpl) -wxArrayDCInfo wxPaintDCImpl::ms_cache; - wxPaintDCImpl::wxPaintDCImpl( wxDC *owner ) : wxClientDCImpl( owner ) { @@ -219,29 +272,31 @@ wxPaintDCImpl::wxPaintDCImpl( wxDC *owner, wxWindow *window ) : { wxCHECK_RET( window, wxT("NULL canvas in wxPaintDCImpl ctor") ); -#ifdef __WXDEBUG__ +#ifdef wxHAS_PAINT_DEBUG if ( g_isPainting <= 0 ) { wxFAIL_MSG( wxT("wxPaintDCImpl may be created only in EVT_PAINT handler!") ); return; } -#endif // __WXDEBUG__ +#endif // wxHAS_PAINT_DEBUG + + // see comments in src/msw/window.cpp where this is defined + extern bool wxDidCreatePaintDC; + + wxDidCreatePaintDC = true; + m_window = window; // do we have a DC for this window in the cache? - wxPaintDCInfo *info = FindInCache(); - if ( info ) - { - m_hDC = info->hdc; - info->count++; - } - else // not in cache, create a new one + m_hDC = FindDCInCache(m_window); + if ( !m_hDC ) { - m_hDC = (WXHDC)::BeginPaint(GetHwndOf(m_window), &g_paintStruct); - if (m_hDC) - ms_cache.Add(new wxPaintDCInfo(m_window, this)); + // not in cache, create a new one + wxPaintDCInfoOur* const info = new wxPaintDCInfoOur(m_window); + gs_PaintDCInfos[m_window] = info; + m_hDC = info->GetHDC(); } // Note: at this point m_hDC can be NULL under MicroWindows, when dragging. @@ -261,77 +316,51 @@ wxPaintDCImpl::~wxPaintDCImpl() if ( m_hDC ) { SelectOldObjects(m_hDC); + m_hDC = 0; + } +} - size_t index; - wxPaintDCInfo *info = FindInCache(&index); - - wxCHECK_RET( info, wxT("existing DC should have a cache entry") ); - if ( --info->count == 0 ) - { - ::EndPaint(GetHwndOf(m_window), &g_paintStruct); +/* static */ +wxPaintDCInfo *wxPaintDCImpl::FindInCache(wxWindow *win) +{ + PaintDCInfos::const_iterator it = gs_PaintDCInfos.find( win ); - ms_cache.RemoveAt(index); + return it != gs_PaintDCInfos.end() ? it->second : NULL; +} - // Reduce the number of bogus reports of non-freed memory - // at app exit - if (ms_cache.IsEmpty()) - ms_cache.Clear(); - } - //else: cached DC entry is still in use +/* static */ +WXHDC wxPaintDCImpl::FindDCInCache(wxWindow* win) +{ + wxPaintDCInfo* const info = FindInCache(win); - // prevent the base class dtor from ReleaseDC()ing it again - m_hDC = 0; - } + return info ? info->GetHDC() : 0; } -wxPaintDCInfo *wxPaintDCImpl::FindInCache(size_t *index) const +/* static */ +void wxPaintDCImpl::EndPaint(wxWindow *win) { - wxPaintDCInfo *info = NULL; - size_t nCache = ms_cache.GetCount(); - for ( size_t n = 0; n < nCache; n++ ) + wxPaintDCInfo *info = FindInCache(win); + if ( info ) { - wxPaintDCInfo *info1 = &ms_cache[n]; - if ( info1->hwnd == m_window->GetHWND() ) - { - info = info1; - if ( index ) - *index = n; - break; - } + gs_PaintDCInfos.erase(win); + delete info; } - - return info; } -// find the entry for this DC in the cache (keyed by the window) -WXHDC wxPaintDCImpl::FindDCInCache(wxWindow* win) +wxPaintDCInfo::~wxPaintDCInfo() { - size_t nCache = ms_cache.GetCount(); - for ( size_t n = 0; n < nCache; n++ ) - { - wxPaintDCInfo *info = &ms_cache[n]; - if ( info->hwnd == win->GetHWND() ) - { - return info->hdc; - } - } - return 0; } /* * wxPaintDCEx */ -// TODO: don't duplicate wxPaintDCImpl code here!! - class wxPaintDCExImpl: public wxPaintDCImpl { public: wxPaintDCExImpl( wxDC *owner, wxWindow *window, WXHDC dc ); ~wxPaintDCExImpl(); - - int m_saveState; }; @@ -345,47 +374,14 @@ wxPaintDCEx::wxPaintDCEx(wxWindow *window, WXHDC dc) wxPaintDCExImpl::wxPaintDCExImpl(wxDC *owner, wxWindow *window, WXHDC dc) : wxPaintDCImpl( owner ) { - wxCHECK_RET( dc, wxT("wxPaintDCEx requires an existing device context") ); - - m_saveState = 0; - - m_window = window; - - wxPaintDCInfo *info = FindInCache(); - if ( info ) - { - m_hDC = info->hdc; - info->count++; - } - else // not in cache, create a new one - { - m_hDC = dc; - ms_cache.Add(new wxPaintDCInfo(m_window, this)); - m_saveState = SaveDC((HDC) dc); - } + wxCHECK_RET( dc, wxT("wxPaintDCEx requires an existing device context") ); + + m_window = window; + m_hDC = dc; } wxPaintDCExImpl::~wxPaintDCExImpl() { - size_t index; - wxPaintDCInfo *info = FindInCache(&index); - - wxCHECK_RET( info, wxT("existing DC should have a cache entry") ); - - if ( --info->count == 0 ) - { - RestoreDC((HDC) m_hDC, m_saveState); - ms_cache.RemoveAt(index); - - // Reduce the number of bogus reports of non-freed memory - // at app exit - if (ms_cache.IsEmpty()) - ms_cache.Clear(); - } - //else: cached DC entry is still in use - - // prevent the base class dtor from ReleaseDC()ing it again - m_hDC = 0; + // prevent the base class dtor from ReleaseDC()ing it again + m_hDC = 0; } - -