This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
512 lines
15 KiB
C++
512 lines
15 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/dfb/nonownedwnd.cpp
|
|
// Purpose: implementation of wxNonOwnedWindow
|
|
// Author: Vaclav Slavik
|
|
// Created: 2006-12-24
|
|
// Copyright: (c) 2006 REA Elektronik GmbH
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#include "wx/toplevel.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/app.h"
|
|
#endif // WX_PRECOMP
|
|
|
|
#include "wx/hashmap.h"
|
|
#include "wx/evtloop.h"
|
|
#include "wx/dfb/private.h"
|
|
|
|
#define TRACE_EVENTS "events"
|
|
#define TRACE_PAINT "paint"
|
|
|
|
// ============================================================================
|
|
// globals
|
|
// ============================================================================
|
|
|
|
// mapping of DirectFB windows to wxTLWs:
|
|
WX_DECLARE_HASH_MAP(DFBWindowID, wxNonOwnedWindow*,
|
|
wxIntegerHash, wxIntegerEqual,
|
|
wxDfbWindowsMap);
|
|
static wxDfbWindowsMap gs_dfbWindowsMap;
|
|
|
|
// ============================================================================
|
|
// helpers
|
|
// ============================================================================
|
|
|
|
// Queue of paint requests
|
|
class wxDfbQueuedPaintRequests
|
|
{
|
|
public:
|
|
~wxDfbQueuedPaintRequests() { Clear(); }
|
|
|
|
// Adds paint request to the queue
|
|
void Add(const wxRect& rect)
|
|
{
|
|
// We use a simple implementation here for now: all refresh requests
|
|
// are merged together into single rectangle that is superset of
|
|
// all the requested rectangles. This wastes some blitting and painting
|
|
// time, but OTOH, EVT_PAINT handler is called only once per window.
|
|
m_invalidated.Union(rect);
|
|
}
|
|
|
|
// Is the queue empty?
|
|
bool IsEmpty() const { return m_invalidated.IsEmpty(); }
|
|
|
|
// Empties the queue
|
|
void Clear() { m_invalidated = wxRect(); }
|
|
|
|
// Gets the next request in the queue, returns true if there was one,
|
|
// false if the queue was empty
|
|
bool GetNext(wxRect& rect)
|
|
{
|
|
if ( m_invalidated.IsEmpty() )
|
|
return false;
|
|
|
|
rect = m_invalidated;
|
|
Clear(); // there's only one item in the queue
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
// currently invalidated region
|
|
wxRect m_invalidated;
|
|
};
|
|
|
|
// ============================================================================
|
|
// wxNonOwnedWindow
|
|
// ============================================================================
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// creation & destruction
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxNonOwnedWindow::Init()
|
|
{
|
|
m_isShown = false;
|
|
m_sizeSet = false;
|
|
m_opacity = 255;
|
|
m_toPaint = new wxDfbQueuedPaintRequests;
|
|
m_isPainting = false;
|
|
}
|
|
|
|
bool wxNonOwnedWindow::Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxString &name)
|
|
{
|
|
wxCHECK_MSG( pos.x >= 0 && pos.y >= 0, false, "invalid position" );
|
|
wxCHECK_MSG( size.x > 0 && size.y > 0, false, "invalid size" );
|
|
|
|
m_tlw = this;
|
|
|
|
// create DirectFB window:
|
|
wxIDirectFBDisplayLayerPtr layer(wxIDirectFB::Get()->GetDisplayLayer());
|
|
wxCHECK_MSG( layer, false, "no display layer" );
|
|
|
|
DFBWindowDescription desc;
|
|
desc.flags = (DFBWindowDescriptionFlags)
|
|
(DWDESC_CAPS |
|
|
DWDESC_WIDTH | DWDESC_HEIGHT | DWDESC_POSX | DWDESC_POSY);
|
|
desc.caps = DWCAPS_DOUBLEBUFFER;
|
|
desc.posx = pos.x;
|
|
desc.posy = pos.y;
|
|
desc.width = size.x;
|
|
desc.height = size.y;
|
|
m_dfbwin = layer->CreateWindow(&desc);
|
|
if ( !m_dfbwin )
|
|
return false;
|
|
|
|
// add the new TLW to DFBWindowID->wxTLW map:
|
|
DFBWindowID winid;
|
|
if ( !m_dfbwin->GetID(&winid) )
|
|
return false;
|
|
gs_dfbWindowsMap[winid] = this;
|
|
|
|
// TLWs are created initially hidden:
|
|
if ( !m_dfbwin->SetOpacity(wxALPHA_TRANSPARENT) )
|
|
return false;
|
|
|
|
if ( !wxWindow::Create(NULL, id, pos, size, style, name) )
|
|
return false;
|
|
|
|
SetParent(parent);
|
|
if ( parent )
|
|
parent->AddChild(this);
|
|
|
|
if ( style & (wxSTAY_ON_TOP | wxPOPUP_WINDOW) )
|
|
{
|
|
m_dfbwin->SetStackingClass(DWSC_UPPER);
|
|
}
|
|
|
|
// direct events in this window to the global event buffer:
|
|
m_dfbwin->AttachEventBuffer(wxEventLoop::GetDirectFBEventBuffer());
|
|
|
|
return true;
|
|
}
|
|
|
|
wxNonOwnedWindow::~wxNonOwnedWindow()
|
|
{
|
|
SendDestroyEvent();
|
|
|
|
// destroy all children before we destroy the underlying DirectFB window,
|
|
// so that if any of them does something with the TLW, it will still work:
|
|
DestroyChildren();
|
|
|
|
// it's safe to delete the underlying DirectFB window now:
|
|
wxDELETE(m_toPaint);
|
|
|
|
if ( !m_dfbwin )
|
|
return;
|
|
|
|
// remove the TLW from DFBWindowID->wxTLW map:
|
|
DFBWindowID winid;
|
|
if ( m_dfbwin->GetID(&winid) )
|
|
gs_dfbWindowsMap.erase(winid);
|
|
|
|
m_dfbwin->Destroy();
|
|
m_dfbwin.Reset();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// window size & position
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void wxNonOwnedWindow::DoGetPosition(int *x, int *y) const
|
|
{
|
|
m_dfbwin->GetPosition(x, y);
|
|
}
|
|
|
|
void wxNonOwnedWindow::DoGetSize(int *width, int *height) const
|
|
{
|
|
m_dfbwin->GetSize(width, height);
|
|
}
|
|
|
|
void wxNonOwnedWindow::DoMoveWindow(int x, int y, int width, int height)
|
|
{
|
|
wxPoint curpos = GetPosition();
|
|
if ( curpos.x != x || curpos.y != y )
|
|
{
|
|
m_dfbwin->MoveTo(x, y);
|
|
}
|
|
|
|
wxSize cursize = GetSize();
|
|
if ( cursize.x != width || cursize.y != height )
|
|
{
|
|
// changing window's size changes its surface:
|
|
InvalidateDfbSurface();
|
|
|
|
m_dfbwin->Resize(width, height);
|
|
|
|
// we must repaint the window after it changed size:
|
|
if ( IsShown() )
|
|
DoRefreshWindow();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// showing and hiding
|
|
// ----------------------------------------------------------------------------
|
|
|
|
bool wxNonOwnedWindow::Show(bool show)
|
|
{
|
|
// NB: this calls wxWindow::Show() and so ensures DoRefreshWindow() is
|
|
// called on the window -- we'll need that below
|
|
if ( !wxWindow::Show(show) )
|
|
return false;
|
|
|
|
// If this is the first time Show was called, send size event,
|
|
// so that the frame can adjust itself (think auto layout or single child)
|
|
if ( !m_sizeSet )
|
|
{
|
|
m_sizeSet = true;
|
|
wxSizeEvent event(GetSize(), GetId());
|
|
event.SetEventObject(this);
|
|
HandleWindowEvent(event);
|
|
}
|
|
|
|
// make sure the window is fully painted, with all pending updates, before
|
|
// DFB WM shows it, otherwise it would attempt to show either empty (=
|
|
// black) window surface (if shown for the first time) or it would show
|
|
// window with outdated content; note that the window was already refreshed
|
|
// in the wxWindow::Show() call above:
|
|
if ( show )
|
|
Update();
|
|
|
|
// hide/show the window by setting its opacity to 0/full:
|
|
m_dfbwin->SetOpacity(show ? m_opacity : 0);
|
|
|
|
if ( show )
|
|
{
|
|
wxWindow *focused = FindFocus();
|
|
if ( focused && focused->GetTLW() == this )
|
|
{
|
|
// focus is on this frame or its children, apply it to DirectFB
|
|
SetDfbFocus();
|
|
}
|
|
// else: don't do anything, if this is wxFrame or wxDialog that should
|
|
// get focus when it's shown,
|
|
// wxTopLevelWindowDFB::HandleFocusEvent() will do it as soon as
|
|
// the event loop starts
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxNonOwnedWindow::Raise()
|
|
{
|
|
m_dfbwin->RaiseToTop();
|
|
}
|
|
|
|
void wxNonOwnedWindow::Lower()
|
|
{
|
|
m_dfbwin->LowerToBottom();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// surfaces and painting
|
|
// ----------------------------------------------------------------------------
|
|
|
|
wxIDirectFBSurfacePtr wxNonOwnedWindow::ObtainDfbSurface() const
|
|
{
|
|
return m_dfbwin->GetSurface();
|
|
}
|
|
|
|
void wxNonOwnedWindow::HandleQueuedPaintRequests()
|
|
{
|
|
if ( m_toPaint->IsEmpty() )
|
|
return; // nothing to do
|
|
|
|
if ( IsFrozen() || !IsShown() )
|
|
{
|
|
// nothing to do if the window is frozen or hidden; clear the queue
|
|
// and return (note that it's OK to clear the queue even if the window
|
|
// is frozen, because Thaw() calls Refresh()):
|
|
m_toPaint->Clear();
|
|
return;
|
|
}
|
|
|
|
// process queued paint requests:
|
|
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;
|
|
|
|
int requestsCount = 0;
|
|
|
|
wxRect request;
|
|
while ( m_toPaint->GetNext(request) )
|
|
{
|
|
requestsCount++;
|
|
wxRect clipped(request);
|
|
clipped.Intersect(winRect);
|
|
if ( clipped.IsEmpty() )
|
|
continue; // nothing to refresh
|
|
|
|
wxLogTrace(TRACE_PAINT,
|
|
"%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 = clipped;
|
|
else
|
|
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. 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()->FlipToFront(rptr);
|
|
|
|
wxLogTrace(TRACE_PAINT,
|
|
"%p ('%s'): processed %i paint requests, flipped surface: [%i,%i,%i,%i]",
|
|
this, GetName().c_str(),
|
|
requestsCount,
|
|
paintedRect.x, paintedRect.y,
|
|
paintedRect.GetRight(), paintedRect.GetBottom());
|
|
}
|
|
|
|
void wxNonOwnedWindow::DoRefreshRect(const wxRect& rect)
|
|
{
|
|
// 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,
|
|
"%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);
|
|
}
|
|
|
|
void wxNonOwnedWindow::Update()
|
|
{
|
|
HandleQueuedPaintRequests();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// events handling
|
|
// ---------------------------------------------------------------------------
|
|
|
|
namespace
|
|
{
|
|
|
|
static wxNonOwnedWindow *gs_insideDFBFocusHandlerOf = NULL;
|
|
|
|
struct InsideDFBFocusHandlerSetter
|
|
{
|
|
InsideDFBFocusHandlerSetter(wxNonOwnedWindow *win)
|
|
{
|
|
wxASSERT( gs_insideDFBFocusHandlerOf == NULL );
|
|
gs_insideDFBFocusHandlerOf = win;
|
|
}
|
|
~InsideDFBFocusHandlerSetter()
|
|
{
|
|
gs_insideDFBFocusHandlerOf = NULL;
|
|
}
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
void wxNonOwnedWindow::SetDfbFocus()
|
|
{
|
|
wxCHECK_RET( IsShown(), "cannot set focus to hidden window" );
|
|
wxASSERT_MSG( FindFocus() && FindFocus()->GetTLW() == this,
|
|
"setting DirectFB focus to unexpected window" );
|
|
|
|
// Don't set DirectFB focus if we're called from HandleFocusEvent() on
|
|
// this window, because we already have the focus in that case. Not only
|
|
// would it be unnecessary, it would be harmful: RequestFocus() adds
|
|
// an event to DirectFB event queue and calling it when in
|
|
// HandleFocusEvent() could result in a window being focused when it
|
|
// should not be. Consider this example:
|
|
//
|
|
// tlw1->SetFocus(); // (1)
|
|
// tlw2->SetFocus(); // (2)
|
|
//
|
|
// This results in adding these events to DFB queue:
|
|
//
|
|
// DWET_GOTFOCUS(tlw1)
|
|
// DWET_LOSTFOCUS(tlw1)
|
|
// DWET_GOTFOCUS(tlw2)
|
|
//
|
|
// Note that the events are processed by event loop, i.e. not between
|
|
// execution of lines (1) and (2) above. So by the time the first
|
|
// DWET_GOTFOCUS event is handled, tlw2->SetFocus() was already executed.
|
|
// If we onconditionally called RequestFocus() from here, handling the
|
|
// first event would result in this change to the event queue:
|
|
//
|
|
// DWET_LOSTFOCUS(tlw1)
|
|
// DWET_GOTFOCUS(tlw2) // (3)
|
|
// DWET_LOSTFOCUS(tlw2)
|
|
// DWET_GOTFOCUS(tlw1)
|
|
//
|
|
// And the focus would get back to tlw1 even though that's not what we
|
|
// wanted.
|
|
|
|
if ( gs_insideDFBFocusHandlerOf == this )
|
|
return;
|
|
|
|
GetDirectFBWindow()->RequestFocus();
|
|
}
|
|
|
|
/* static */
|
|
void wxNonOwnedWindow::HandleDFBWindowEvent(const wxDFBWindowEvent& event_)
|
|
{
|
|
const DFBWindowEvent& event = event_;
|
|
|
|
if ( gs_dfbWindowsMap.find(event.window_id) == gs_dfbWindowsMap.end() )
|
|
{
|
|
wxLogTrace(TRACE_EVENTS,
|
|
"received event for unknown DirectFB window, ignoring");
|
|
return;
|
|
}
|
|
|
|
wxNonOwnedWindow *tlw = gs_dfbWindowsMap[event.window_id];
|
|
|
|
switch ( event.type )
|
|
{
|
|
case DWET_KEYDOWN:
|
|
case DWET_KEYUP:
|
|
{
|
|
wxWindow *recipient = wxWindow::FindFocus();
|
|
if ( !recipient )
|
|
{
|
|
wxLogTrace(TRACE_EVENTS,
|
|
"ignoring event: no recipient window");
|
|
return;
|
|
}
|
|
|
|
wxCHECK_RET( recipient && recipient->GetTLW() == tlw,
|
|
"event recipient not in TLW which received the event" );
|
|
|
|
recipient->HandleKeyEvent(event_);
|
|
break;
|
|
}
|
|
|
|
case DWET_GOTFOCUS:
|
|
case DWET_LOSTFOCUS:
|
|
{
|
|
InsideDFBFocusHandlerSetter inside(tlw);
|
|
tlw->HandleFocusEvent(event_);
|
|
}
|
|
break;
|
|
|
|
case DWET_NONE:
|
|
case DWET_ALL:
|
|
wxFAIL_MSG( "invalid event type" );
|
|
break;
|
|
|
|
default:
|
|
// we're not interested in them here
|
|
break;
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// idle events processing
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void wxNonOwnedWindow::OnInternalIdle()
|
|
{
|
|
wxWindow::OnInternalIdle();
|
|
HandleQueuedPaintRequests();
|
|
}
|