class WXDLLIMPEXP_CORE wxWindowDC : public wxDC
{
public:
- wxWindowDC() {}
+ wxWindowDC() : m_win(NULL) {}
wxWindowDC(wxWindow *win);
+ virtual ~wxWindowDC();
protected:
// initializes the DC for painting on given window; if rect!=NULL, then
// for painting only on the given region of the window
void InitForWin(wxWindow *win, const wxRect *rect);
+private:
+ wxWindow *m_win; // the window the DC paints on
+
DECLARE_DYNAMIC_CLASS(wxWindowDC)
DECLARE_NO_COPY_CLASS(wxWindowDC)
};
-//-----------------------------------------------------------------------------
-// base class for wxClientDC and wxPaintDC
-//-----------------------------------------------------------------------------
-
-class WXDLLIMPEXP_CORE wxClientDCBase : public wxWindowDC
-{
-public:
- wxClientDCBase() {}
- wxClientDCBase(wxWindow *win);
-};
-
//-----------------------------------------------------------------------------
// wxClientDC
//-----------------------------------------------------------------------------
-class WXDLLIMPEXP_CORE wxClientDC : public wxClientDCBase
+class WXDLLIMPEXP_CORE wxClientDC : public wxWindowDC
{
public:
wxClientDC() {}
- wxClientDC(wxWindow *win) : wxClientDCBase(win) {}
- ~wxClientDC();
+ wxClientDC(wxWindow *win);
DECLARE_DYNAMIC_CLASS(wxClientDC)
DECLARE_NO_COPY_CLASS(wxClientDC)
// wxPaintDC
//-----------------------------------------------------------------------------
-class WXDLLIMPEXP_CORE wxPaintDC : public wxClientDCBase
+class WXDLLIMPEXP_CORE wxPaintDC : public wxClientDC
{
public:
wxPaintDC() {}
- wxPaintDC(wxWindow *win) : wxClientDCBase(win) {}
- ~wxPaintDC();
+ wxPaintDC(wxWindow *win) : wxClientDC(win) {}
DECLARE_DYNAMIC_CLASS(wxPaintDC)
DECLARE_NO_COPY_CLASS(wxPaintDC)
wxIDirectFBWindowPtr GetDirectFBWindow() const { return m_dfbwin; }
+ // Returns true if some invalidated area of the TLW is currently being
+ // painted
+ bool IsPainting() const { return m_isPainting; }
+
protected:
// common part of all ctors
void Init();
virtual void DoGetSize(int *width, int *height) const;
virtual void DoMoveWindow(int x, int y, int width, int height);
- virtual void DoRefreshRect(const wxRect& rect, bool eraseBack = true);
+ virtual void DoRefreshRect(const wxRect& rect);
private:
// do queued painting in idle time
wxIDirectFBWindowPtr m_dfbwin;
private:
+ // invalidated areas of the TLW that need repainting
wxDfbQueuedPaintRequests *m_toPaint;
+ // are we currently painting some area of this TLW?
+ bool m_isPainting;
friend class wxEventLoop; // for HandleDFBWindowEvent
};
void InvalidateDfbSurface();
// called by parent to render (part of) the window
- void PaintWindow(const wxRect& rect, bool eraseBackground);
+ void PaintWindow(const wxRect& rect);
- // implementation of Refresh()
- void DoRefreshWindow(bool eraseBack = true);
- virtual void DoRefreshRect(const wxRect& rect, bool eraseBack = true);
+ // refreshes the entire window (including non-client areas)
+ void DoRefreshWindow();
+ // refreshes given rectangle of the window (in window, _not_ client coords)
+ virtual void DoRefreshRect(const wxRect& rect);
// DirectFB events handling
void HandleKeyEvent(const wxDFBWindowEvent& event_);
DECLARE_EVENT_TABLE()
};
-
#endif // _WX_DFB_WINDOW_H_
(DFBSurfaceTextFlags)flags));
}
- bool Flip(const DFBRegion *region, int flags)
- {
- return Check(m_ptr->Flip(m_ptr, region, (DFBSurfaceFlipFlags)flags));
- }
+ /**
+ Updates the front buffer from the back buffer. If @a region is not
+ NULL, only given rectangle is updated.
+ */
+ bool FlipToFront(const DFBRegion *region = NULL);
wxIDirectFBSurfacePtr GetSubSurface(const DFBRectangle *rect)
{
size of this surface.
*/
wxIDirectFBSurfacePtr CreateCompatible(const wxSize& size = wxDefaultSize);
+
+private:
+ // this is private because we want user code to use FlipToFront()
+ bool Flip(const DFBRegion *region, int flags);
};
wxColour clr = m_backgroundBrush.GetColour();
m_surface->Clear(clr.Red(), clr.Green(), clr.Blue(), clr.Alpha());
+
+ wxSize size(GetSize());
+ CalcBoundingBox(XDEV2LOG(0), YDEV2LOG(0));
+ CalcBoundingBox(XDEV2LOG(size.x), YDEV2LOG(size.y));
}
extern bool wxDoFloodFill(wxDC *dc, wxCoord x, wxCoord y,
#include "wx/dfb/private.h"
+#define TRACE_PAINT _T("paint")
+
// ===========================================================================
// implementation
// ===========================================================================
void wxWindowDC::InitForWin(wxWindow *win, const wxRect *rect)
{
- wxCHECK_RET( win, _T("invalid window") );
+ m_win = win;
- // check if the rectangle covers full window and so is not needed:
- if ( rect && *rect == wxRect(win->GetSize()) )
- rect = NULL;
+ wxCHECK_RET( win, _T("invalid window") );
// obtain the surface used for painting:
+ wxPoint origin;
wxIDirectFBSurfacePtr surface;
if ( !win->IsVisible() )
// we still need a valid DC so that e.g. text extents can be measured,
// so let's create a dummy surface that has the same format as the real
// one would have and let the code paint on it:
+ wxLogTrace(TRACE_PAINT, _T("%p ('%s'): creating dummy DC surface"),
+ win, win->GetName().c_str());
wxSize size(rect ? rect->GetSize() : win->GetSize());
surface = win->GetDfbSurface()->CreateCompatible(size);
}
- else if ( !rect )
- {
- wxCHECK_RET( win->GetSize().x > 0 && win->GetSize().y > 0,
- _T("window has invalid size") );
-
- surface = win->GetDfbSurface();
- }
else
{
- wxCHECK_RET( !rect || !rect->IsEmpty(), _T("invalid rectangle") );
-
- DFBRectangle dfbrect = { rect->x, rect->y, rect->width, rect->height };
+ wxRect rectOrig(rect ? *rect : wxRect(win->GetSize()));
+
+ // compute painting rectangle after clipping if we're in PaintWindow
+ // code, otherwise paint on the entire window:
+ wxRect r(rectOrig);
+ if ( win->GetTLW()->IsPainting() )
+ r.Intersect(win->GetUpdateRegion().AsRect());
+
+ wxCHECK_RET( !r.IsEmpty(), _T("invalid painting rectangle") );
+
+ // if the DC was clipped thanks to rectPaint, we must adjust the origin
+ // accordingly; but we do *not* adjust for 'rect', because
+ // rect.GetPosition() has coordinates (0,0) in the DC:
+ origin.x = rectOrig.x - r.x;
+ origin.y = rectOrig.y - r.y;
+
+ wxLogTrace(TRACE_PAINT,
+ _T("%p ('%s'): creating DC for area [%i,%i,%i,%i], clipped to [%i,%i,%i,%i], origin [%i,%i]"),
+ win, win->GetName().c_str(),
+ rectOrig.x, rectOrig.y, rectOrig.GetRight(), rectOrig.GetBottom(),
+ r.x, r.y, r.GetRight(), r.GetBottom(),
+ origin.x, origin.y);
+
+ DFBRectangle dfbrect = { r.x, r.y, r.width, r.height };
surface = win->GetDfbSurface()->GetSubSurface(&dfbrect);
}
SetFont(win->GetFont());
// offset coordinates to account for subsurface's origin coordinates:
- if ( rect )
- SetDeviceOrigin(rect->x, rect->y);
+ SetDeviceOrigin(origin.x, origin.y);
}
-//-----------------------------------------------------------------------------
-// base class for wxClientDC and wxPaintDC
-//-----------------------------------------------------------------------------
-
-wxClientDCBase::wxClientDCBase(wxWindow *win)
+wxWindowDC::~wxWindowDC()
{
- wxCHECK_RET( win, _T("invalid window") );
+ wxIDirectFBSurfacePtr surface(GetDirectFBSurface());
+ if ( !surface || !m_win )
+ return;
- wxRect rect = win->GetClientRect();
- InitForWin(win, &rect);
+ // painting on hidden window has no effect on TLW's surface, don't
+ // waste time flipping the dummy surface:
+ if ( !m_win->IsVisible() )
+ return;
+
+ // if no painting was done on the DC, we don't have to flip the surface:
+ if ( !m_isBBoxValid )
+ return;
+
+ if ( !m_win->GetTLW()->IsPainting() )
+ {
+ // FIXME: flip only modified parts of the surface
+ surface->FlipToFront();
+ }
+ // else: don't flip the surface, wxTLW will do it when it finishes
+ // painting of its invalidated areas
}
//-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxClientDC, wxWindowDC)
-wxClientDC::~wxClientDC()
+wxClientDC::wxClientDC(wxWindow *win)
{
- // flip to surface so that the changes become visible
- wxIDirectFBSurfacePtr surface(GetDirectFBSurface());
+ wxCHECK_RET( win, _T("invalid window") );
- // FIXME: do this only if the surface was modified (as opposed to e.g.
- // used only to obtain text metrics)
- if ( surface )
- surface->Flip(NULL, DSFLIP_NONE);
+ wxRect rect = win->GetClientRect();
+ InitForWin(win, &rect);
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxPaintDC, wxWindowDC)
-
-#warning "wxPaintDC ctor must respect m_updateRegion"
-
-wxPaintDC::~wxPaintDC()
-{
- // NB: do *not* flip the surface: wxPaintDC is used with EVT_PAINT and the
- // surface will be flipped for the entire TLW once all children are
- // repainted
-}
struct wxDfbPaintRequest
{
- wxDfbPaintRequest(const wxRect& rect, bool eraseBackground)
- : m_rect(rect), m_eraseBackground(eraseBackground) {}
- wxDfbPaintRequest(const wxDfbPaintRequest& r)
- : m_rect(r.m_rect), m_eraseBackground(r.m_eraseBackground) {}
+ wxDfbPaintRequest(const wxRect& rect) : m_rect(rect) {}
+ wxDfbPaintRequest(const wxDfbPaintRequest& r) : m_rect(r.m_rect) {}
wxRect m_rect;
- bool m_eraseBackground;
};
WX_DEFINE_ARRAY_PTR(wxDfbPaintRequest*, wxDfbQueuedPaintRequestsList);
~wxDfbQueuedPaintRequests() { Clear(); }
// Adds paint request to the queue
- void Add(const wxRect& rect, bool eraseBack)
- { m_queue.push_back(new wxDfbPaintRequest(rect, eraseBack)); }
+ void Add(const wxRect& rect)
+ { m_queue.push_back(new wxDfbPaintRequest(rect)); }
// Is the queue empty?
bool IsEmpty() const { return m_queue.empty(); }
m_sizeSet = false;
m_opacity = 255;
m_toPaint = new wxDfbQueuedPaintRequests;
+ m_isPainting = false;
}
bool wxTopLevelWindowDFB::Create(wxWindow *parent,
if ( !m_dfbwin->SetOpacity(wxALPHA_TRANSPARENT) )
return false;
- wxWindow::Create(NULL, id, pos, size, style, name);
+ if ( !wxWindow::Create(NULL, id, pos, size, style, name) )
+ return false;
SetParent(parent);
if ( parent )
{
m_dfbwin->Resize(width, height);
// we must repaint the window after it changed size:
- Refresh();
+ if ( IsShown() )
+ DoRefreshWindow();
}
}
wxRect winRect(wxPoint(0, 0), GetSize());
wxRect paintedRect;
+ // important note: all DCs created from now until m_isPainting is reset to
+ // false will not update the front buffer as this flag indicates that we'll
+ // blit the entire back buffer to front soon
+ m_isPainting = true;
+
size_t cnt = requests.size();
+ wxLogTrace(TRACE_PAINT, _T("%p ('%s'): processing %i paint requests"),
+ this, GetName().c_str(), cnt);
+
for ( size_t i = 0; i < cnt; ++i )
{
const wxDfbPaintRequest& request = *requests[i];
wxRect clipped(request.m_rect);
-
- wxLogTrace(TRACE_PAINT,
- _T("%p ('%s'): processing paint request [x=%i,y=%i,w=%i,h=%i]"),
- this, GetName().c_str(),
- clipped.x, clipped.y, clipped.width, clipped.height);
-
clipped.Intersect(winRect);
if ( clipped.IsEmpty() )
continue; // nothing to refresh
- PaintWindow(clipped, request.m_eraseBackground);
+ wxLogTrace(TRACE_PAINT,
+ _T("%p ('%s'): processing paint request [%i,%i,%i,%i]"),
+ this, GetName().c_str(),
+ clipped.x, clipped.y, clipped.GetRight(), clipped.GetBottom());
+
+ PaintWindow(clipped);
// remember rectangle covering all repainted areas:
if ( paintedRect.IsEmpty() )
paintedRect.Union(clipped);
}
+ m_isPainting = false;
+
m_toPaint->Clear();
if ( paintedRect.IsEmpty() )
return; // no painting occurred, no need to flip
- // flip the surface to make the changes visible:
+ // Flip the surface to make the changes visible. Note that the rectangle we
+ // flip is *superset* of the union of repainted rectangles (created as
+ // "rectangles union" by wxRect::Union) and so some parts of the back
+ // buffer that we didn't touch in this HandleQueuedPaintRequests call will
+ // be copied to the front buffer as well. This is safe/correct thing to do
+ // *only* because wx always use wxIDirectFBSurface::FlipToFront() and so
+ // the back and front buffers contain the same data.
+ //
+ // Note that we do _not_ split m_toPaint into disjoint rectangles and
+ // do FlipToFront() for each of them, because that could result in visible
+ // updating of the screen; instead, we prefer to flip everything at once.
+
DFBRegion r = {paintedRect.GetLeft(), paintedRect.GetTop(),
paintedRect.GetRight(), paintedRect.GetBottom()};
DFBRegion *rptr = (winRect == paintedRect) ? NULL : &r;
- GetDfbSurface()->Flip(rptr, DSFLIP_NONE);
+ GetDfbSurface()->FlipToFront(rptr);
+
+ wxLogTrace(TRACE_PAINT,
+ _T("%p ('%s'): flipped surface: [%i,%i,%i,%i]"),
+ this, GetName().c_str(),
+ paintedRect.x, paintedRect.y,
+ paintedRect.GetRight(), paintedRect.GetBottom());
}
-void wxTopLevelWindowDFB::DoRefreshRect(const wxRect& rect, bool eraseBack)
+void wxTopLevelWindowDFB::DoRefreshRect(const wxRect& rect)
{
+ wxASSERT_MSG( rect.width > 0 && rect.height > 0, _T("invalid rect") );
+
+ wxLogTrace(TRACE_PAINT,
+ _T("%p ('%s'): [TLW] refresh rect [%i,%i,%i,%i]"),
+ this, GetName().c_str(),
+ rect.x, rect.y, rect.GetRight(), rect.GetBottom());
+
// defer painting until idle time or until Update() is called:
- m_toPaint->Add(rect, eraseBack);
+ m_toPaint->Add(rect);
}
void wxTopLevelWindowDFB::Update()
dc.Clear();
}
-void wxWindowDFB::Refresh(bool eraseBack, const wxRect *rect)
+void wxWindowDFB::Refresh(bool WXUNUSED(eraseBack), const wxRect *rect)
{
if ( !IsShown() || IsFrozen() )
return;
+ // NB[1]: We intentionally ignore the eraseBack argument here. This is
+ // because of the way wxDFB's painting is implemented: the refresh
+ // request is probagated up to wxTLW, which is then painted in
+ // top-down order. This means that this window's area is first
+ // painted by its parent and this window is then painted over it, so
+ // it's not safe to not paint this window's background even if
+ // eraseBack=false.
+ // NB[2]: wxWindow::Refresh() takes the rectangle in client coords, but
+ // wxUniv translates it to window coords before passing it to
+ // wxWindowDFB::Refresh(), so we can directly pass the rect to
+ // DoRefreshRect (which takes window, not client, coords) here.
if ( rect )
- DoRefreshRect(*rect, eraseBack);
+ DoRefreshRect(*rect);
else
- DoRefreshWindow(eraseBack);
+ DoRefreshWindow();
}
-void wxWindowDFB::DoRefreshWindow(bool eraseBack)
+void wxWindowDFB::DoRefreshWindow()
{
- DoRefreshRect(wxRect(wxPoint(0, 0), GetSize()), eraseBack);
+ // NB: DoRefreshRect() takes window coords, not client, so this is correct
+ DoRefreshRect(wxRect(GetSize()));
}
-void wxWindowDFB::DoRefreshRect(const wxRect& rect, bool eraseBack)
+void wxWindowDFB::DoRefreshRect(const wxRect& rect)
{
wxWindow *parent = GetParent();
wxCHECK_RET( parent, _T("no parent") );
+ // don't overlap outside of the window (NB: 'rect' is in window coords):
+ wxRect r(rect);
+ r.Intersect(wxRect(GetSize()));
+ if ( r.IsEmpty() )
+ return;
+
+ wxLogTrace(TRACE_PAINT,
+ _T("%p ('%s'): refresh rect [%i,%i,%i,%i]"),
+ this, GetName().c_str(),
+ rect.x, rect.y, rect.GetRight(), rect.GetBottom());
+
// convert the refresh rectangle to parent's coordinates and
// recursively refresh the parent:
- wxRect r(rect);
r.Offset(GetPosition());
r.Offset(parent->GetClientAreaOrigin());
- parent->DoRefreshRect(r, eraseBack);
+ parent->DoRefreshRect(r);
}
void wxWindowDFB::Update()
if ( --m_frozenness == 0 )
{
if ( IsShown() )
- Refresh();
+ DoRefreshWindow();
}
}
-void wxWindowDFB::PaintWindow(const wxRect& rect, bool eraseBackground)
+void wxWindowDFB::PaintWindow(const wxRect& rect)
{
wxCHECK_RET( !IsFrozen() && IsShown(), _T("shouldn't be called") );
wxLogTrace(TRACE_PAINT,
- _T("%p ('%s'): painting region [x=%i,y=%i,w=%i,h=%i]"),
+ _T("%p ('%s'): painting region [%i,%i,%i,%i]"),
this, GetName().c_str(),
- rect.x, rect.y, rect.width, rect.height);
+ rect.x, rect.y, rect.GetRight(), rect.GetBottom());
m_updateRegion = rect;
- // FIXME_DFB: don't waste time rendering the area if it's fully covered
- // by some children, go directly to rendering the children
-
#if wxUSE_CARET
// must hide caret temporarily, otherwise we'd get rendering artifacts
wxCaret *caret = GetCaret();
caret->Hide();
#endif // wxUSE_CARET
- if ( eraseBackground )
+ // FIXME_DFB: don't waste time rendering the area if it's fully covered
+ // by some children, go directly to rendering the children
+
+ // NB: unconditionally send wxEraseEvent, because our implementation of
+ // wxWindow::Refresh() ignores the eraseBack argument
+ wxWindowDC dc((wxWindow*)this);
+ wxEraseEvent eventEr(m_windowId, &dc);
+ eventEr.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(eventEr);
+
+ wxRect clientRect(GetClientRect());
+
+ // only send wxNcPaintEvent if drawing at least part of nonclient area:
+ if ( !clientRect.Inside(rect) )
{
- wxWindowDC dc((wxWindow*)this);
- wxEraseEvent eventEr(m_windowId, &dc);
- eventEr.SetEventObject(this);
- GetEventHandler()->ProcessEvent(eventEr);
+ wxNcPaintEvent eventNc(GetId());
+ eventNc.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(eventNc);
+ }
+ else
+ {
+ wxLogTrace(TRACE_PAINT, _T("%p ('%s'): not sending wxNcPaintEvent"),
+ this, GetName().c_str());
}
- wxNcPaintEvent eventNc(GetId());
- eventNc.SetEventObject(this);
- GetEventHandler()->ProcessEvent(eventNc);
-
- wxPaintEvent eventPt(GetId());
- eventPt.SetEventObject(this);
- GetEventHandler()->ProcessEvent(eventPt);
+ // only send wxPaintEvent if drawing at least part of client area:
+ if ( rect.Intersects(clientRect) )
+ {
+ wxPaintEvent eventPt(GetId());
+ eventPt.SetEventObject(this);
+ GetEventHandler()->ProcessEvent(eventPt);
+ }
+ else
+ {
+ wxLogTrace(TRACE_PAINT, _T("%p ('%s'): not sending wxPaintEvent"),
+ this, GetName().c_str());
+ }
#if wxUSE_CARET
if ( caret )
caret->Show();
#endif // wxUSE_CARET
+ m_updateRegion.Clear();
+
+ // paint the children:
wxPoint origin = GetClientAreaOrigin();
wxWindowList& children = GetChildren();
for ( wxWindowList::iterator i = children.begin();
continue;
// and repaint it:
- wxPoint childpos(child->GetPosition());
- childrect.Offset(-childpos.x, -childpos.y);
- childrect.Offset(-origin.x, -origin.y);
- child->PaintWindow(childrect, eraseBackground);
+ childrect.Offset(-child->GetPosition());
+ childrect.Offset(-origin);
+ child->PaintWindow(childrect);
}
-
- m_updateRegion.Clear();
}
return snew;
}
+
+bool wxIDirectFBSurface::Flip(const DFBRegion *region, int flags)
+{
+ return Check(m_ptr->Flip(m_ptr, region, (DFBSurfaceFlipFlags)flags));
+}
+
+bool wxIDirectFBSurface::FlipToFront(const DFBRegion *region)
+{
+ // Blit to the front buffer instead of exchanging front and back ones.
+ // Always doing this ensures that back and front buffer have same content
+ // and so painting to the back buffer will never lose any previous
+ // drawings:
+ return Flip(region, DSFLIP_BLIT);
+}