1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/dfb/nonownedwnd.cpp 
   3 // Purpose:     implementation of wxNonOwnedWindowow 
   4 // Author:      Vaclav Slavik 
   7 // Copyright:   (c) 2006 REA Elektronik GmbH 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // For compilers that support precompilation, includes "wx.h". 
  12 #include "wx/wxprec.h" 
  14 #include "wx/toplevel.h" 
  20 #include "wx/hashmap.h" 
  21 #include "wx/evtloop.h" 
  22 #include "wx/dfb/private.h" 
  24 #define TRACE_EVENTS _T("events") 
  25 #define TRACE_PAINT  _T("paint") 
  27 // ============================================================================ 
  29 // ============================================================================ 
  31 // mapping of DirectFB windows to wxTLWs: 
  32 WX_DECLARE_HASH_MAP(DFBWindowID
, wxNonOwnedWindow
*, 
  33                     wxIntegerHash
, wxIntegerEqual
, 
  35 static wxDfbWindowsMap gs_dfbWindowsMap
; 
  37 // ============================================================================ 
  39 // ============================================================================ 
  41 // Queue of paint requests 
  42 class wxDfbQueuedPaintRequests
 
  45     ~wxDfbQueuedPaintRequests() { Clear(); } 
  47     // Adds paint request to the queue 
  48     void Add(const wxRect
& rect
) 
  50         // We use a simple implementation here for now: all refresh requests 
  51         // are merged together into single rectangle that is superset of 
  52         // all the requested rectangles. This wastes some blitting and painting 
  53         // time, but OTOH, EVT_PAINT handler is called only once per window. 
  54         m_invalidated
.Union(rect
); 
  57     // Is the queue empty? 
  58     bool IsEmpty() const { return m_invalidated
.IsEmpty(); } 
  61     void Clear() { m_invalidated 
= wxRect(); } 
  63     // Gets the next request in the queue, returns true if there was one, 
  64     // false if the queue was empty 
  65     bool GetNext(wxRect
& rect
) 
  67         if ( m_invalidated
.IsEmpty() ) 
  71         Clear(); // there's only one item in the queue 
  76     // currently invalidated region 
  80 // ============================================================================ 
  82 // ============================================================================ 
  84 // ---------------------------------------------------------------------------- 
  85 // creation & destruction 
  86 // ---------------------------------------------------------------------------- 
  88 void wxNonOwnedWindow::Init() 
  93     m_toPaint 
= new wxDfbQueuedPaintRequests
; 
  97 bool wxNonOwnedWindow::Create(wxWindow 
*parent
, 
 102                                  const wxString 
&name
) 
 104     wxCHECK_MSG( pos
.x 
>= 0 && pos
.y 
>= 0, false, _T("invalid position") ); 
 105     wxCHECK_MSG( size
.x 
> 0 && size
.y 
> 0, false, _T("invalid size") ); 
 109     // create DirectFB window: 
 110     wxIDirectFBDisplayLayerPtr 
layer(wxIDirectFB::Get()->GetDisplayLayer()); 
 111     wxCHECK_MSG( layer
, false, _T("no display layer") ); 
 113     DFBWindowDescription desc
; 
 114     desc
.flags 
= (DFBWindowDescriptionFlags
) 
 116                     DWDESC_WIDTH 
| DWDESC_HEIGHT 
| DWDESC_POSX 
| DWDESC_POSY
); 
 117     desc
.caps 
= DWCAPS_DOUBLEBUFFER
; 
 121     desc
.height 
= size
.y
; 
 122     m_dfbwin 
= layer
->CreateWindow(&desc
); 
 126     // add the new TLW to DFBWindowID->wxTLW map: 
 128     if ( !m_dfbwin
->GetID(&winid
) ) 
 130     gs_dfbWindowsMap
[winid
] = this; 
 132     // TLWs are created initially hidden: 
 133     if ( !m_dfbwin
->SetOpacity(wxALPHA_TRANSPARENT
) ) 
 136     if ( !wxWindow::Create(NULL
, id
, pos
, size
, style
, name
) ) 
 141         parent
->AddChild(this); 
 143     if ( style 
& (wxSTAY_ON_TOP 
| wxPOPUP_WINDOW
) ) 
 145         m_dfbwin
->SetStackingClass(DWSC_UPPER
); 
 148     // direct events in this window to the global event buffer: 
 149     m_dfbwin
->AttachEventBuffer(wxEventLoop::GetDirectFBEventBuffer()); 
 154 wxNonOwnedWindow::~wxNonOwnedWindow() 
 156     m_isBeingDeleted 
= true; 
 158     // destroy all children before we destroy the underlying DirectFB window, 
 159     // so that if any of them does something with the TLW, it will still work: 
 162     // it's safe to delete the underlying DirectFB window now: 
 168     // remove the TLW from DFBWindowID->wxTLW map: 
 170     if ( m_dfbwin
->GetID(&winid
) ) 
 171         gs_dfbWindowsMap
.erase(winid
); 
 177 // ---------------------------------------------------------------------------- 
 178 // window size & position 
 179 // ---------------------------------------------------------------------------- 
 181 void wxNonOwnedWindow::DoGetPosition(int *x
, int *y
) const 
 183     m_dfbwin
->GetPosition(x
, y
); 
 186 void wxNonOwnedWindow::DoGetSize(int *width
, int *height
) const 
 188     m_dfbwin
->GetSize(width
, height
); 
 191 void wxNonOwnedWindow::DoMoveWindow(int x
, int y
, int width
, int height
) 
 193     wxPoint curpos 
= GetPosition(); 
 194     if ( curpos
.x 
!= x 
|| curpos
.y 
!= y 
) 
 196         m_dfbwin
->MoveTo(x
, y
); 
 199     wxSize cursize 
= GetSize(); 
 200     if ( cursize
.x 
!= width 
|| cursize
.y 
!= height 
) 
 202         // changing window's size changes its surface: 
 203         InvalidateDfbSurface(); 
 205         m_dfbwin
->Resize(width
, height
); 
 207         // we must repaint the window after it changed size: 
 213 // ---------------------------------------------------------------------------- 
 214 // showing and hiding 
 215 // ---------------------------------------------------------------------------- 
 217 bool wxNonOwnedWindow::Show(bool show
) 
 219     // NB: this calls wxWindow::Show() and so ensures DoRefreshWindow() is 
 220     //     called on the window -- we'll need that below 
 221     if ( !wxWindow::Show(show
) ) 
 224     // If this is the first time Show was called, send size event, 
 225     // so that the frame can adjust itself (think auto layout or single child) 
 229         wxSizeEvent 
event(GetSize(), GetId()); 
 230         event
.SetEventObject(this); 
 231         GetEventHandler()->ProcessEvent(event
); 
 234     // make sure the window is fully painted, with all pending updates, before 
 235     // DFB WM shows it, otherwise it would attempt to show either empty (= 
 236     // black) window surface (if shown for the first time) or it would show 
 237     // window with outdated content; note that the window was already refreshed 
 238     // in the wxWindow::Show() call above: 
 242     // hide/show the window by setting its opacity to 0/full: 
 243     m_dfbwin
->SetOpacity(show 
? m_opacity 
: 0); 
 247         wxWindow 
*focused 
= wxWindow::FindFocus(); 
 248         if ( focused 
&& focused
->GetTLW() == this ) 
 252         else if ( CanAcceptFocus() ) 
 254             // FIXME: we should probably always call SetDfbFocus instead 
 255             // and call SetFocus() from wxActivateEvent/DWET_GOTFOCUS 
 264 // ---------------------------------------------------------------------------- 
 265 // surfaces and painting 
 266 // ---------------------------------------------------------------------------- 
 268 wxIDirectFBSurfacePtr 
wxNonOwnedWindow::ObtainDfbSurface() const 
 270     return m_dfbwin
->GetSurface(); 
 273 void wxNonOwnedWindow::HandleQueuedPaintRequests() 
 275     if ( m_toPaint
->IsEmpty() ) 
 276         return; // nothing to do 
 278     if ( IsFrozen() || !IsShown() ) 
 280         // nothing to do if the window is frozen or hidden; clear the queue 
 281         // and return (note that it's OK to clear the queue even if the window 
 282         // is frozen, because Thaw() calls Refresh()): 
 287     // process queued paint requests: 
 288     wxRect 
winRect(wxPoint(0, 0), GetSize()); 
 291     // important note: all DCs created from now until m_isPainting is reset to 
 292     // false will not update the front buffer as this flag indicates that we'll 
 293     // blit the entire back buffer to front soon 
 297     int requestsCount 
= 0; 
 301     while ( m_toPaint
->GetNext(request
) ) 
 306         wxRect 
clipped(request
); 
 307         clipped
.Intersect(winRect
); 
 308         if ( clipped
.IsEmpty() ) 
 309             continue; // nothing to refresh 
 311         wxLogTrace(TRACE_PAINT
, 
 312                    _T("%p ('%s'): processing paint request [%i,%i,%i,%i]"), 
 313                    this, GetName().c_str(), 
 314                    clipped
.x
, clipped
.y
, clipped
.GetRight(), clipped
.GetBottom()); 
 316         PaintWindow(clipped
); 
 318         // remember rectangle covering all repainted areas: 
 319         if ( paintedRect
.IsEmpty() ) 
 320             paintedRect 
= clipped
; 
 322             paintedRect
.Union(clipped
); 
 325     m_isPainting 
= false; 
 329     if ( paintedRect
.IsEmpty() ) 
 330         return; // no painting occurred, no need to flip 
 332     // Flip the surface to make the changes visible. Note that the rectangle we 
 333     // flip is *superset* of the union of repainted rectangles (created as 
 334     // "rectangles union" by wxRect::Union) and so some parts of the back 
 335     // buffer that we didn't touch in this HandleQueuedPaintRequests call will 
 336     // be copied to the front buffer as well. This is safe/correct thing to do 
 337     // *only* because wx always use wxIDirectFBSurface::FlipToFront() and so 
 338     // the back and front buffers contain the same data. 
 340     // Note that we do _not_ split m_toPaint into disjoint rectangles and 
 341     // do FlipToFront() for each of them, because that could result in visible 
 342     // updating of the screen; instead, we prefer to flip everything at once. 
 344     DFBRegion r 
= {paintedRect
.GetLeft(), paintedRect
.GetTop(), 
 345                    paintedRect
.GetRight(), paintedRect
.GetBottom()}; 
 346     DFBRegion 
*rptr 
= (winRect 
== paintedRect
) ? NULL 
: &r
; 
 348     GetDfbSurface()->FlipToFront(rptr
); 
 350     wxLogTrace(TRACE_PAINT
, 
 351                _T("%p ('%s'): processed %i paint requests, flipped surface: [%i,%i,%i,%i]"), 
 352                this, GetName().c_str(), 
 354                paintedRect
.x
, paintedRect
.y
, 
 355                paintedRect
.GetRight(), paintedRect
.GetBottom()); 
 358 void wxNonOwnedWindow::DoRefreshRect(const wxRect
& rect
) 
 360     // don't overlap outside of the window (NB: 'rect' is in window coords): 
 362     r
.Intersect(wxRect(GetSize())); 
 366     wxLogTrace(TRACE_PAINT
, 
 367                _T("%p ('%s'): [TLW] refresh rect [%i,%i,%i,%i]"), 
 368                this, GetName().c_str(), 
 369                rect
.x
, rect
.y
, rect
.GetRight(), rect
.GetBottom()); 
 371     // defer painting until idle time or until Update() is called: 
 372     m_toPaint
->Add(rect
); 
 375 void wxNonOwnedWindow::Update() 
 377     HandleQueuedPaintRequests(); 
 380 // --------------------------------------------------------------------------- 
 382 // --------------------------------------------------------------------------- 
 384 void wxNonOwnedWindow::SetDfbFocus() 
 386     wxCHECK_RET( IsShown(), _T("cannot set focus to hidden window") ); 
 387     wxASSERT_MSG( FindFocus() && FindFocus()->GetTLW() == this, 
 388                   _T("setting DirectFB focus to unexpected window") ); 
 390     GetDirectFBWindow()->RequestFocus(); 
 394 void wxNonOwnedWindow::HandleDFBWindowEvent(const wxDFBWindowEvent
& event_
) 
 396     const DFBWindowEvent
& event 
= event_
; 
 398     if ( gs_dfbWindowsMap
.find(event
.window_id
) == gs_dfbWindowsMap
.end() ) 
 400         wxLogTrace(TRACE_EVENTS
, 
 401                    _T("received event for unknown DirectFB window, ignoring")); 
 405     wxNonOwnedWindow 
*tlw 
= gs_dfbWindowsMap
[event
.window_id
]; 
 406     wxWindow 
*recipient 
= NULL
; 
 407     void (wxWindow::*handlerFunc
)(const wxDFBWindowEvent
&) = NULL
; 
 409     switch ( event
.type 
) 
 414             recipient 
= wxWindow::FindFocus(); 
 415             handlerFunc 
= &wxWindowDFB::HandleKeyEvent
; 
 422             wxFAIL_MSG( _T("invalid event type") ); 
 427             // we're not interested in them here 
 433         wxLogTrace(TRACE_EVENTS
, _T("ignoring event: no recipient window")); 
 437     wxCHECK_RET( recipient 
&& recipient
->GetTLW() == tlw
, 
 438                  _T("event recipient not in TLW which received the event") ); 
 440     // process the event: 
 441     (recipient
->*handlerFunc
)(event_
); 
 444 // --------------------------------------------------------------------------- 
 445 // idle events processing 
 446 // --------------------------------------------------------------------------- 
 448 void wxNonOwnedWindow::OnInternalIdle() 
 450     wxWindow::OnInternalIdle(); 
 451     HandleQueuedPaintRequests();