]> git.saurik.com Git - wxWidgets.git/blame - src/dfb/nonownedwnd.cpp
avoid duplicate move events
[wxWidgets.git] / src / dfb / nonownedwnd.cpp
CommitLineData
42b0d8b9
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/dfb/nonownedwnd.cpp
fd2be5df 3// Purpose: implementation of wxNonOwnedWindow
42b0d8b9
VS
4// Author: Vaclav Slavik
5// Created: 2006-12-24
42b0d8b9
VS
6// Copyright: (c) 2006 REA Elektronik GmbH
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#include "wx/toplevel.h"
14
15#ifndef WX_PRECOMP
16 #include "wx/app.h"
17#endif // WX_PRECOMP
18
19#include "wx/hashmap.h"
20#include "wx/evtloop.h"
21#include "wx/dfb/private.h"
22
a5001e93
VS
23#define TRACE_EVENTS "events"
24#define TRACE_PAINT "paint"
42b0d8b9
VS
25
26// ============================================================================
27// globals
28// ============================================================================
29
30// mapping of DirectFB windows to wxTLWs:
31WX_DECLARE_HASH_MAP(DFBWindowID, wxNonOwnedWindow*,
32 wxIntegerHash, wxIntegerEqual,
33 wxDfbWindowsMap);
34static wxDfbWindowsMap gs_dfbWindowsMap;
35
36// ============================================================================
37// helpers
38// ============================================================================
39
40// Queue of paint requests
41class wxDfbQueuedPaintRequests
42{
43public:
44 ~wxDfbQueuedPaintRequests() { Clear(); }
45
46 // Adds paint request to the queue
47 void Add(const wxRect& rect)
48 {
49 // We use a simple implementation here for now: all refresh requests
50 // are merged together into single rectangle that is superset of
51 // all the requested rectangles. This wastes some blitting and painting
52 // time, but OTOH, EVT_PAINT handler is called only once per window.
53 m_invalidated.Union(rect);
54 }
55
56 // Is the queue empty?
57 bool IsEmpty() const { return m_invalidated.IsEmpty(); }
58
59 // Empties the queue
60 void Clear() { m_invalidated = wxRect(); }
61
62 // Gets the next request in the queue, returns true if there was one,
63 // false if the queue was empty
64 bool GetNext(wxRect& rect)
65 {
66 if ( m_invalidated.IsEmpty() )
67 return false;
68
69 rect = m_invalidated;
70 Clear(); // there's only one item in the queue
71 return true;
72 }
73
74private:
75 // currently invalidated region
76 wxRect m_invalidated;
77};
78
79// ============================================================================
80// wxNonOwnedWindow
81// ============================================================================
82
83// ----------------------------------------------------------------------------
84// creation & destruction
85// ----------------------------------------------------------------------------
86
87void wxNonOwnedWindow::Init()
88{
89 m_isShown = false;
90 m_sizeSet = false;
91 m_opacity = 255;
92 m_toPaint = new wxDfbQueuedPaintRequests;
93 m_isPainting = false;
94}
95
96bool wxNonOwnedWindow::Create(wxWindow *parent,
97 wxWindowID id,
98 const wxPoint& pos,
99 const wxSize& size,
100 long style,
101 const wxString &name)
102{
a5001e93
VS
103 wxCHECK_MSG( pos.x >= 0 && pos.y >= 0, false, "invalid position" );
104 wxCHECK_MSG( size.x > 0 && size.y > 0, false, "invalid size" );
42b0d8b9
VS
105
106 m_tlw = this;
107
108 // create DirectFB window:
109 wxIDirectFBDisplayLayerPtr layer(wxIDirectFB::Get()->GetDisplayLayer());
a5001e93 110 wxCHECK_MSG( layer, false, "no display layer" );
42b0d8b9
VS
111
112 DFBWindowDescription desc;
113 desc.flags = (DFBWindowDescriptionFlags)
114 (DWDESC_CAPS |
115 DWDESC_WIDTH | DWDESC_HEIGHT | DWDESC_POSX | DWDESC_POSY);
116 desc.caps = DWCAPS_DOUBLEBUFFER;
117 desc.posx = pos.x;
118 desc.posy = pos.y;
119 desc.width = size.x;
120 desc.height = size.y;
121 m_dfbwin = layer->CreateWindow(&desc);
122 if ( !m_dfbwin )
123 return false;
124
125 // add the new TLW to DFBWindowID->wxTLW map:
126 DFBWindowID winid;
127 if ( !m_dfbwin->GetID(&winid) )
128 return false;
129 gs_dfbWindowsMap[winid] = this;
130
131 // TLWs are created initially hidden:
132 if ( !m_dfbwin->SetOpacity(wxALPHA_TRANSPARENT) )
133 return false;
134
135 if ( !wxWindow::Create(NULL, id, pos, size, style, name) )
136 return false;
137
138 SetParent(parent);
139 if ( parent )
140 parent->AddChild(this);
141
142 if ( style & (wxSTAY_ON_TOP | wxPOPUP_WINDOW) )
143 {
144 m_dfbwin->SetStackingClass(DWSC_UPPER);
145 }
146
147 // direct events in this window to the global event buffer:
148 m_dfbwin->AttachEventBuffer(wxEventLoop::GetDirectFBEventBuffer());
149
150 return true;
151}
152
153wxNonOwnedWindow::~wxNonOwnedWindow()
154{
c6212a0c 155 SendDestroyEvent();
42b0d8b9
VS
156
157 // destroy all children before we destroy the underlying DirectFB window,
158 // so that if any of them does something with the TLW, it will still work:
159 DestroyChildren();
160
161 // it's safe to delete the underlying DirectFB window now:
162 wxDELETE(m_toPaint);
163
164 if ( !m_dfbwin )
165 return;
166
167 // remove the TLW from DFBWindowID->wxTLW map:
168 DFBWindowID winid;
169 if ( m_dfbwin->GetID(&winid) )
170 gs_dfbWindowsMap.erase(winid);
171
172 m_dfbwin->Destroy();
173 m_dfbwin.Reset();
174}
175
176// ----------------------------------------------------------------------------
177// window size & position
178// ----------------------------------------------------------------------------
179
180void wxNonOwnedWindow::DoGetPosition(int *x, int *y) const
181{
182 m_dfbwin->GetPosition(x, y);
183}
184
185void wxNonOwnedWindow::DoGetSize(int *width, int *height) const
186{
187 m_dfbwin->GetSize(width, height);
188}
189
190void wxNonOwnedWindow::DoMoveWindow(int x, int y, int width, int height)
191{
192 wxPoint curpos = GetPosition();
193 if ( curpos.x != x || curpos.y != y )
194 {
195 m_dfbwin->MoveTo(x, y);
196 }
197
198 wxSize cursize = GetSize();
199 if ( cursize.x != width || cursize.y != height )
200 {
201 // changing window's size changes its surface:
202 InvalidateDfbSurface();
203
204 m_dfbwin->Resize(width, height);
205
206 // we must repaint the window after it changed size:
207 if ( IsShown() )
208 DoRefreshWindow();
209 }
210}
211
212// ----------------------------------------------------------------------------
213// showing and hiding
214// ----------------------------------------------------------------------------
215
216bool wxNonOwnedWindow::Show(bool show)
217{
218 // NB: this calls wxWindow::Show() and so ensures DoRefreshWindow() is
219 // called on the window -- we'll need that below
220 if ( !wxWindow::Show(show) )
221 return false;
222
223 // If this is the first time Show was called, send size event,
224 // so that the frame can adjust itself (think auto layout or single child)
225 if ( !m_sizeSet )
226 {
227 m_sizeSet = true;
228 wxSizeEvent event(GetSize(), GetId());
229 event.SetEventObject(this);
937013e0 230 HandleWindowEvent(event);
42b0d8b9
VS
231 }
232
233 // make sure the window is fully painted, with all pending updates, before
234 // DFB WM shows it, otherwise it would attempt to show either empty (=
235 // black) window surface (if shown for the first time) or it would show
236 // window with outdated content; note that the window was already refreshed
237 // in the wxWindow::Show() call above:
238 if ( show )
239 Update();
240
241 // hide/show the window by setting its opacity to 0/full:
242 m_dfbwin->SetOpacity(show ? m_opacity : 0);
243
244 if ( show )
fd2be5df
VS
245 {
246 wxWindow *focused = FindFocus();
247 if ( focused && focused->GetTLW() == this )
248 {
249 // focus is on this frame or its children, apply it to DirectFB
250 SetDfbFocus();
251 }
252 // else: don't do anything, if this is wxFrame or wxDialog that should
253 // get focus when it's shown,
254 // wxTopLevelWindowDFB::HandleFocusEvent() will do it as soon as
255 // the event loop starts
256 }
42b0d8b9
VS
257
258 return true;
259}
260
84e45580
VS
261void wxNonOwnedWindow::Raise()
262{
263 m_dfbwin->RaiseToTop();
264}
265
266void wxNonOwnedWindow::Lower()
267{
268 m_dfbwin->LowerToBottom();
269}
270
42b0d8b9
VS
271// ----------------------------------------------------------------------------
272// surfaces and painting
273// ----------------------------------------------------------------------------
274
275wxIDirectFBSurfacePtr wxNonOwnedWindow::ObtainDfbSurface() const
276{
277 return m_dfbwin->GetSurface();
278}
279
280void wxNonOwnedWindow::HandleQueuedPaintRequests()
281{
282 if ( m_toPaint->IsEmpty() )
283 return; // nothing to do
284
285 if ( IsFrozen() || !IsShown() )
286 {
287 // nothing to do if the window is frozen or hidden; clear the queue
288 // and return (note that it's OK to clear the queue even if the window
289 // is frozen, because Thaw() calls Refresh()):
290 m_toPaint->Clear();
291 return;
292 }
293
294 // process queued paint requests:
295 wxRect winRect(wxPoint(0, 0), GetSize());
296 wxRect paintedRect;
297
298 // important note: all DCs created from now until m_isPainting is reset to
299 // false will not update the front buffer as this flag indicates that we'll
300 // blit the entire back buffer to front soon
301 m_isPainting = true;
302
42b0d8b9 303 int requestsCount = 0;
42b0d8b9
VS
304
305 wxRect request;
306 while ( m_toPaint->GetNext(request) )
307 {
42b0d8b9 308 requestsCount++;
42b0d8b9
VS
309 wxRect clipped(request);
310 clipped.Intersect(winRect);
311 if ( clipped.IsEmpty() )
312 continue; // nothing to refresh
313
314 wxLogTrace(TRACE_PAINT,
a5001e93 315 "%p ('%s'): processing paint request [%i,%i,%i,%i]",
42b0d8b9
VS
316 this, GetName().c_str(),
317 clipped.x, clipped.y, clipped.GetRight(), clipped.GetBottom());
318
319 PaintWindow(clipped);
320
321 // remember rectangle covering all repainted areas:
322 if ( paintedRect.IsEmpty() )
323 paintedRect = clipped;
324 else
325 paintedRect.Union(clipped);
326 }
327
328 m_isPainting = false;
329
330 m_toPaint->Clear();
331
332 if ( paintedRect.IsEmpty() )
333 return; // no painting occurred, no need to flip
334
335 // Flip the surface to make the changes visible. Note that the rectangle we
336 // flip is *superset* of the union of repainted rectangles (created as
337 // "rectangles union" by wxRect::Union) and so some parts of the back
338 // buffer that we didn't touch in this HandleQueuedPaintRequests call will
339 // be copied to the front buffer as well. This is safe/correct thing to do
340 // *only* because wx always use wxIDirectFBSurface::FlipToFront() and so
341 // the back and front buffers contain the same data.
342 //
343 // Note that we do _not_ split m_toPaint into disjoint rectangles and
344 // do FlipToFront() for each of them, because that could result in visible
345 // updating of the screen; instead, we prefer to flip everything at once.
346
347 DFBRegion r = {paintedRect.GetLeft(), paintedRect.GetTop(),
348 paintedRect.GetRight(), paintedRect.GetBottom()};
349 DFBRegion *rptr = (winRect == paintedRect) ? NULL : &r;
350
351 GetDfbSurface()->FlipToFront(rptr);
352
353 wxLogTrace(TRACE_PAINT,
a5001e93 354 "%p ('%s'): processed %i paint requests, flipped surface: [%i,%i,%i,%i]",
42b0d8b9
VS
355 this, GetName().c_str(),
356 requestsCount,
357 paintedRect.x, paintedRect.y,
358 paintedRect.GetRight(), paintedRect.GetBottom());
359}
360
361void wxNonOwnedWindow::DoRefreshRect(const wxRect& rect)
362{
363 // don't overlap outside of the window (NB: 'rect' is in window coords):
364 wxRect r(rect);
365 r.Intersect(wxRect(GetSize()));
366 if ( r.IsEmpty() )
367 return;
368
369 wxLogTrace(TRACE_PAINT,
a5001e93 370 "%p ('%s'): [TLW] refresh rect [%i,%i,%i,%i]",
42b0d8b9
VS
371 this, GetName().c_str(),
372 rect.x, rect.y, rect.GetRight(), rect.GetBottom());
373
374 // defer painting until idle time or until Update() is called:
375 m_toPaint->Add(rect);
376}
377
378void wxNonOwnedWindow::Update()
379{
380 HandleQueuedPaintRequests();
381}
382
383// ---------------------------------------------------------------------------
384// events handling
385// ---------------------------------------------------------------------------
386
fd2be5df
VS
387namespace
388{
389
390static wxNonOwnedWindow *gs_insideDFBFocusHandlerOf = NULL;
391
392struct InsideDFBFocusHandlerSetter
393{
394 InsideDFBFocusHandlerSetter(wxNonOwnedWindow *win)
395 {
396 wxASSERT( gs_insideDFBFocusHandlerOf == NULL );
397 gs_insideDFBFocusHandlerOf = win;
398 }
399 ~InsideDFBFocusHandlerSetter()
400 {
401 gs_insideDFBFocusHandlerOf = NULL;
402 }
403};
404
405} // anonymous namespace
406
407
42b0d8b9
VS
408void wxNonOwnedWindow::SetDfbFocus()
409{
a5001e93 410 wxCHECK_RET( IsShown(), "cannot set focus to hidden window" );
42b0d8b9 411 wxASSERT_MSG( FindFocus() && FindFocus()->GetTLW() == this,
a5001e93 412 "setting DirectFB focus to unexpected window" );
42b0d8b9 413
fd2be5df
VS
414 // Don't set DirectFB focus if we're called from HandleFocusEvent() on
415 // this window, because we already have the focus in that case. Not only
416 // would it be unnecessary, it would be harmful: RequestFocus() adds
417 // an event to DirectFB event queue and calling it when in
418 // HandleFocusEvent() could result in a window being focused when it
419 // should not be. Consider this example:
420 //
421 // tlw1->SetFocus(); // (1)
422 // tlw2->SetFocus(); // (2)
423 //
424 // This results in adding these events to DFB queue:
425 //
426 // DWET_GOTFOCUS(tlw1)
427 // DWET_LOSTFOCUS(tlw1)
428 // DWET_GOTFOCUS(tlw2)
429 //
430 // Note that the events are processed by event loop, i.e. not between
431 // execution of lines (1) and (2) above. So by the time the first
432 // DWET_GOTFOCUS event is handled, tlw2->SetFocus() was already executed.
433 // If we onconditionally called RequestFocus() from here, handling the
434 // first event would result in this change to the event queue:
435 //
436 // DWET_LOSTFOCUS(tlw1)
437 // DWET_GOTFOCUS(tlw2) // (3)
438 // DWET_LOSTFOCUS(tlw2)
439 // DWET_GOTFOCUS(tlw1)
440 //
441 // And the focus would get back to tlw1 even though that's not what we
442 // wanted.
443
444 if ( gs_insideDFBFocusHandlerOf == this )
445 return;
446
42b0d8b9
VS
447 GetDirectFBWindow()->RequestFocus();
448}
449
450/* static */
451void wxNonOwnedWindow::HandleDFBWindowEvent(const wxDFBWindowEvent& event_)
452{
453 const DFBWindowEvent& event = event_;
454
455 if ( gs_dfbWindowsMap.find(event.window_id) == gs_dfbWindowsMap.end() )
456 {
457 wxLogTrace(TRACE_EVENTS,
a5001e93 458 "received event for unknown DirectFB window, ignoring");
42b0d8b9
VS
459 return;
460 }
461
462 wxNonOwnedWindow *tlw = gs_dfbWindowsMap[event.window_id];
42b0d8b9
VS
463
464 switch ( event.type )
465 {
466 case DWET_KEYDOWN:
467 case DWET_KEYUP:
468 {
6954a1e2
VS
469 wxWindow *recipient = wxWindow::FindFocus();
470 if ( !recipient )
471 {
472 wxLogTrace(TRACE_EVENTS,
a5001e93 473 "ignoring event: no recipient window");
6954a1e2
VS
474 return;
475 }
476
477 wxCHECK_RET( recipient && recipient->GetTLW() == tlw,
a5001e93 478 "event recipient not in TLW which received the event" );
6954a1e2
VS
479
480 recipient->HandleKeyEvent(event_);
42b0d8b9
VS
481 break;
482 }
483
6954a1e2
VS
484 case DWET_GOTFOCUS:
485 case DWET_LOSTFOCUS:
fd2be5df
VS
486 {
487 InsideDFBFocusHandlerSetter inside(tlw);
488 tlw->HandleFocusEvent(event_);
489 }
6954a1e2
VS
490 break;
491
42b0d8b9
VS
492 case DWET_NONE:
493 case DWET_ALL:
a5001e93 494 wxFAIL_MSG( "invalid event type" );
42b0d8b9 495 break;
42b0d8b9
VS
496
497 default:
498 // we're not interested in them here
499 break;
500 }
42b0d8b9
VS
501}
502
503// ---------------------------------------------------------------------------
504// idle events processing
505// ---------------------------------------------------------------------------
506
507void wxNonOwnedWindow::OnInternalIdle()
508{
509 wxWindow::OnInternalIdle();
510 HandleQueuedPaintRequests();
511}