1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/dfb/dcclient.cpp
3 // Purpose:     wxWindowDCImpl, wxClientDCImpl and wxPaintDC
4 // Author:      Vaclav Slavik
5 // Created:     2006-08-10
6 // Copyright:   (c) 2006 REA Elektronik GmbH
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // ===========================================================================
11 // declarations
12 // ===========================================================================
13 
14 // ---------------------------------------------------------------------------
15 // headers
16 // ---------------------------------------------------------------------------
17 
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #ifdef __BORLANDC__
22     #pragma hdrstop
23 #endif
24 
25 #ifndef WX_PRECOMP
26     #include "wx/window.h"
27     #include "wx/nonownedwnd.h"
28 #endif
29 
30 #include "wx/dfb/dcclient.h"
31 #include "wx/dfb/private.h"
32 
33 #define TRACE_PAINT  "paint"
34 
35 // ===========================================================================
36 // implementation
37 // ===========================================================================
38 
39 //-----------------------------------------------------------------------------
40 // helpers
41 //-----------------------------------------------------------------------------
42 
43 // Returns subrect of the window that is not outside of its parent's
44 // boundaries ("hidden behind its borders"), recursively:
GetUncoveredWindowArea(wxWindow * win)45 static wxRect GetUncoveredWindowArea(wxWindow *win)
46 {
47     wxRect r(win->GetSize());
48 
49     if ( win->IsTopLevel() )
50         return r;
51 
52     wxWindow *parent = win->GetParent();
53     if ( !parent )
54         return r;
55 
56     // intersect with parent's uncovered area, after offsetting it into win's
57     // coordinates; this will remove parts of 'r' that are outside of the
58     // parent's area:
59     wxRect rp(GetUncoveredWindowArea(parent));
60 
61     // normal windows cannot extend out of its parent's client area:
62     if ( !win->CanBeOutsideClientArea() )
63         rp.Intersect(parent->GetClientRect());
64 
65     rp.Offset(-win->GetPosition());
66     rp.Offset(-parent->GetClientAreaOrigin());
67     r.Intersect(rp);
68 
69     return r;
70 }
71 
72 // creates a dummy surface that has the same format as the real window's
73 // surface, but is not visible and so can be painted on even if the window
74 // is hidden
75 static
CreateDummySurface(wxWindow * win,const wxRect * rect)76 wxIDirectFBSurfacePtr CreateDummySurface(wxWindow *win, const wxRect *rect)
77 {
78     wxLogTrace(TRACE_PAINT, "%p ('%s'): creating dummy DC surface",
79                win, win->GetName().c_str());
80     wxSize size(rect ? rect->GetSize() : win->GetSize());
81 
82     // we can't create a surface of 0 size but the size of the window may be 0,
83     // so ensure that we have at least a single pixel to draw on
84     size.IncTo(wxSize(1, 1));
85 
86     return win->GetDfbSurface()->CreateCompatible
87            (
88              size,
89              wxIDirectFBSurface::CreateCompatible_NoBackBuffer
90            );
91 }
92 
93 //-----------------------------------------------------------------------------
94 // wxWindowDCImpl
95 //-----------------------------------------------------------------------------
96 
IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl,wxDFBDCImpl)97 IMPLEMENT_ABSTRACT_CLASS(wxWindowDCImpl, wxDFBDCImpl)
98 
99 wxWindowDCImpl::wxWindowDCImpl(wxDC *owner, wxWindow *win)
100               : wxDFBDCImpl(owner)
101 {
102     InitForWin(win, NULL);
103 }
104 
InitForWin(wxWindow * win,const wxRect * rect)105 void wxWindowDCImpl::InitForWin(wxWindow *win, const wxRect *rect)
106 {
107     wxCHECK_RET( win, "invalid window" );
108 
109     m_window = win;
110 
111     // obtain the surface used for painting:
112     wxPoint origin;
113     wxIDirectFBSurfacePtr surface;
114 
115     wxRect rectOrig(rect ? *rect : wxRect(win->GetSize()));
116     wxRect r;
117 
118     if ( !win->IsShownOnScreen() )
119     {
120         // leave 'r' rectangle empty to indicate the window is not visible,
121         // see below (below "create the surface:") for how is this case handled
122     }
123     else
124     {
125         // compute painting rectangle after clipping if we're in PaintWindow
126         // code, otherwise paint on the entire window:
127         r = rectOrig;
128 
129         const wxRegion& updateRegion = win->GetUpdateRegion();
130         if ( win->GetTLW()->IsPainting() && !updateRegion.IsEmpty() )
131         {
132             r.Intersect(updateRegion.AsRect());
133             wxCHECK_RET( !r.IsEmpty(), "invalid painting rectangle" );
134 
135             // parent TLW will flip the entire surface when painting is done
136             m_shouldFlip = false;
137         }
138         else
139         {
140             // One of two things happened:
141             // (1) the TLW is not being painted by PaintWindow() now; or
142             // (2) we're drawing on some window other than the one that is
143             //     currently painted on by PaintWindow()
144             // In either case, we need to flip the surface when we're done
145             // painting and we don't have to use updateRegion for clipping.
146             // OTOH, if the window is (partially) hidden by being
147             // out of its parent's area, we must clip the surface accordingly.
148             r.Intersect(GetUncoveredWindowArea(win));
149             m_shouldFlip = true; // paint the results immediately
150         }
151     }
152 
153     // create the surface:
154     if ( r.IsEmpty() )
155     {
156         // we're painting on invisible window: the changes won't have any
157         // effect, as the window will be repainted anyhow when it is shown,
158         // but we still need a valid DC so that e.g. text extents can be
159         // measured, so let's create a dummy surface that has the same
160         // format as the real one would have and let the code paint on it:
161         surface = CreateDummySurface(win, rect);
162 
163         // painting on hidden window has no effect on TLW's surface, don't
164         // waste time flipping the dummy surface:
165         m_shouldFlip = false;
166     }
167     else
168     {
169         m_winRect = r;
170         DFBRectangle dfbrect = { r.x, r.y, r.width, r.height };
171         surface = win->GetDfbSurface()->GetSubSurface(&dfbrect);
172 
173         // if the DC was clipped thanks to rectPaint, we must adjust the
174         // origin accordingly; but we do *not* adjust for 'rect', because
175         // rect.GetPosition() has coordinates (0,0) in the DC:
176         origin.x = rectOrig.x - r.x;
177         origin.y = rectOrig.y - r.y;
178 
179         // m_shouldFlip was set in the "if" block above this one
180     }
181 
182     if ( !surface )
183         return;
184 
185     wxLogTrace(TRACE_PAINT,
186                "%p ('%s'): creating DC for area [%i,%i,%i,%i], clipped to [%i,%i,%i,%i], origin [%i,%i]",
187                win, win->GetName().c_str(),
188                rectOrig.x, rectOrig.y, rectOrig.GetRight(), rectOrig.GetBottom(),
189                r.x, r.y, r.GetRight(), r.GetBottom(),
190                origin.x, origin.y);
191 
192     DFBInit(surface);
193     SetFont(win->GetFont());
194 
195     // offset coordinates to account for subsurface's origin coordinates:
196     SetDeviceOrigin(origin.x, origin.y);
197 }
198 
~wxWindowDCImpl()199 wxWindowDCImpl::~wxWindowDCImpl()
200 {
201     wxIDirectFBSurfacePtr surface(GetDirectFBSurface());
202     if ( !surface )
203         return;
204 
205     // if no painting was done on the DC, we don't have to flip the surface:
206     if ( !m_isBBoxValid )
207         return;
208 
209     if ( m_shouldFlip )
210     {
211         // paint overlays on top of the surface being drawn to by this DC
212         // before showing anything on the screen:
213         GetWindow()->PaintOverlays(m_winRect);
214 
215         DFBSurfaceCapabilities caps = DSCAPS_NONE;
216         surface->GetCapabilities(&caps);
217         if ( caps & DSCAPS_DOUBLE )
218         {
219             // FIXME: flip only modified parts of the surface
220             surface->FlipToFront();
221         }
222         // else: the surface is not double-buffered and so cannot be flipped
223     }
224     // else: don't flip the surface, wxTLW will do it when it finishes
225     //       painting of its invalidated areas
226 }
227 
228 //-----------------------------------------------------------------------------
229 // wxClientDCImpl
230 //-----------------------------------------------------------------------------
231 
IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl,wxWindowDCImpl)232 IMPLEMENT_ABSTRACT_CLASS(wxClientDCImpl, wxWindowDCImpl)
233 
234 wxClientDCImpl::wxClientDCImpl(wxDC *owner, wxWindow *win)
235               : wxWindowDCImpl(owner, win)
236 {
237     wxCHECK_RET( win, "invalid window" );
238 
239     wxRect rect = win->GetClientRect();
240     InitForWin(win, &rect);
241 }
242 
243 //-----------------------------------------------------------------------------
244 // wxPaintDC
245 //-----------------------------------------------------------------------------
246 
247 IMPLEMENT_ABSTRACT_CLASS(wxPaintDCImpl, wxWindowDCImpl)
248