/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qdirectfbwindowsurface.h" #include "qdirectfbscreen.h" #include "qdirectfbpaintengine.h" #include #include #include #include #include #ifndef QT_NO_QWS_DIRECTFB QT_BEGIN_NAMESPACE QDirectFBWindowSurface::QDirectFBWindowSurface(DFBSurfaceFlipFlags flip, QDirectFBScreen *scr) : QDirectFBPaintDevice(scr) #ifndef QT_NO_DIRECTFB_WM , dfbWindow(0) #endif , flipFlags(flip) , boundingRectFlip(scr->directFBFlags() & QDirectFBScreen::BoundingRectFlip) , flushPending(false) { #ifdef QT_NO_DIRECTFB_WM mode = Offscreen; #endif setSurfaceFlags(Opaque | Buffered); #ifdef QT_DIRECTFB_TIMING frames = 0; timer.start(); #endif } QDirectFBWindowSurface::QDirectFBWindowSurface(DFBSurfaceFlipFlags flip, QDirectFBScreen *scr, QWidget *widget) : QWSWindowSurface(widget), QDirectFBPaintDevice(scr) #ifndef QT_NO_DIRECTFB_WM , dfbWindow(0) #endif , flipFlags(flip) , boundingRectFlip(scr->directFBFlags() & QDirectFBScreen::BoundingRectFlip) , flushPending(false) { SurfaceFlags flags = 0; if (!widget || widget->window()->windowOpacity() == 0xff) flags |= Opaque; #ifdef QT_NO_DIRECTFB_WM if (widget && widget->testAttribute(Qt::WA_PaintOnScreen)) { flags = RegionReserved; mode = Primary; } else { mode = Offscreen; flags = Buffered; } #endif setSurfaceFlags(flags); #ifdef QT_DIRECTFB_TIMING frames = 0; timer.start(); #endif } QDirectFBWindowSurface::~QDirectFBWindowSurface() { releaseSurface(); // these are not tracked by QDirectFBScreen so we don't want QDirectFBPaintDevice to release it } bool QDirectFBWindowSurface::isValid() const { return true; } #ifdef QT_DIRECTFB_WM void QDirectFBWindowSurface::raise() { if (IDirectFBWindow *window = directFBWindow()) { window->RaiseToTop(window); } } IDirectFBWindow *QDirectFBWindowSurface::directFBWindow() const { return dfbWindow; } void QDirectFBWindowSurface::createWindow(const QRect &rect) { IDirectFBDisplayLayer *layer = screen->dfbDisplayLayer(); if (!layer) qFatal("QDirectFBWindowSurface: Unable to get primary display layer!"); updateIsOpaque(); DFBWindowDescription description; memset(&description, 0, sizeof(DFBWindowDescription)); description.flags = DWDESC_CAPS|DWDESC_HEIGHT|DWDESC_WIDTH|DWDESC_POSX|DWDESC_POSY|DWDESC_SURFACE_CAPS|DWDESC_PIXELFORMAT; description.caps = DWCAPS_NODECORATION; description.surface_caps = DSCAPS_NONE; imageFormat = screen->pixelFormat(); if (!(surfaceFlags() & Opaque)) { imageFormat = screen->alphaPixmapFormat(); description.caps |= DWCAPS_ALPHACHANNEL; #if (Q_DIRECTFB_VERSION >= 0x010200) description.flags |= DWDESC_OPTIONS; description.options |= DWOP_ALPHACHANNEL; #endif } description.pixelformat = QDirectFBScreen::getSurfacePixelFormat(imageFormat); description.posx = rect.x(); description.posy = rect.y(); description.width = rect.width(); description.height = rect.height(); if (QDirectFBScreen::isPremultiplied(imageFormat)) description.surface_caps = DSCAPS_PREMULTIPLIED; if (screen->directFBFlags() & QDirectFBScreen::VideoOnly) description.surface_caps |= DSCAPS_VIDEOONLY; DFBResult result = layer->CreateWindow(layer, &description, &dfbWindow); if (result != DFB_OK) DirectFBErrorFatal("QDirectFBWindowSurface::createWindow", result); if (window()) { if (window()->windowFlags() & Qt::WindowStaysOnTopHint) { dfbWindow->SetStackingClass(dfbWindow, DWSC_UPPER); } DFBWindowID winid; result = dfbWindow->GetID(dfbWindow, &winid); if (result != DFB_OK) { DirectFBError("QDirectFBWindowSurface::createWindow. Can't get ID", result); } else { window()->setProperty("_q_DirectFBWindowID", winid); } } Q_ASSERT(!dfbSurface); dfbWindow->GetSurface(dfbWindow, &dfbSurface); } static DFBResult setWindowGeometry(IDirectFBWindow *dfbWindow, const QRect &old, const QRect &rect) { DFBResult result = DFB_OK; const bool isMove = old.isEmpty() || rect.topLeft() != old.topLeft(); const bool isResize = rect.size() != old.size(); #if (Q_DIRECTFB_VERSION >= 0x010000) if (isResize && isMove) { result = dfbWindow->SetBounds(dfbWindow, rect.x(), rect.y(), rect.width(), rect.height()); } else if (isResize) { result = dfbWindow->Resize(dfbWindow, rect.width(), rect.height()); } else if (isMove) { result = dfbWindow->MoveTo(dfbWindow, rect.x(), rect.y()); } #else if (isResize) { result = dfbWindow->Resize(dfbWindow, rect.width(), rect.height()); } if (isMove) { result = dfbWindow->MoveTo(dfbWindow, rect.x(), rect.y()); } #endif return result; } #endif // QT_NO_DIRECTFB_WM void QDirectFBWindowSurface::setGeometry(const QRect &rect) { const QRect oldRect = geometry(); if (oldRect == rect) return; IDirectFBSurface *oldSurface = dfbSurface; const bool sizeChanged = oldRect.size() != rect.size(); if (sizeChanged) { delete engine; engine = 0; releaseSurface(); Q_ASSERT(!dfbSurface); } if (rect.isNull()) { #ifndef QT_NO_DIRECTFB_WM if (dfbWindow) { if (window()) window()->setProperty("_q_DirectFBWindowID", QVariant()); dfbWindow->Release(dfbWindow); dfbWindow = 0; } #endif Q_ASSERT(!dfbSurface); #ifdef QT_DIRECTFB_SUBSURFACE Q_ASSERT(!subSurface); #endif } else { #ifdef QT_DIRECTFB_WM if (!dfbWindow) { createWindow(rect); } else { setWindowGeometry(dfbWindow, oldRect, rect); Q_ASSERT(!sizeChanged || !dfbSurface); if (sizeChanged) dfbWindow->GetSurface(dfbWindow, &dfbSurface); } #else IDirectFBSurface *primarySurface = screen->primarySurface(); DFBResult result = DFB_OK; if (mode == Primary) { Q_ASSERT(primarySurface); if (rect == screen->region().boundingRect()) { dfbSurface = primarySurface; } else { const DFBRectangle r = { rect.x(), rect.y(), rect.width(), rect.height() }; result = primarySurface->GetSubSurface(primarySurface, &r, &dfbSurface); } } else { // mode == Offscreen if (!dfbSurface) { dfbSurface = screen->createDFBSurface(rect.size(), surfaceFlags() & Opaque ? screen->pixelFormat() : screen->alphaPixmapFormat(), QDirectFBScreen::DontTrackSurface); } } if (result != DFB_OK) DirectFBErrorFatal("QDirectFBWindowSurface::setGeometry()", result); #endif } if (oldSurface != dfbSurface) { imageFormat = dfbSurface ? QDirectFBScreen::getImageFormat(dfbSurface) : QImage::Format_Invalid; } if (oldRect.size() != rect.size()) { QWSWindowSurface::setGeometry(rect); } else { QWindowSurface::setGeometry(rect); } } QByteArray QDirectFBWindowSurface::permanentState() const { QByteArray state(sizeof(SurfaceFlags) + sizeof(DFBWindowID), 0); char *ptr = state.data(); SurfaceFlags flags = surfaceFlags(); memcpy(ptr, &flags, sizeof(SurfaceFlags)); ptr += sizeof(SurfaceFlags); DFBWindowID did = (DFBWindowID)(-1); if (dfbWindow) dfbWindow->GetID(dfbWindow, &did); memcpy(ptr, &did, sizeof(DFBWindowID)); return state; } void QDirectFBWindowSurface::setPermanentState(const QByteArray &state) { const char *ptr = state.constData(); IDirectFBDisplayLayer *layer = screen->dfbDisplayLayer(); SurfaceFlags flags; memcpy(&flags, ptr, sizeof(SurfaceFlags)); setSurfaceFlags(flags); ptr += sizeof(SurfaceFlags); DFBWindowID id; memcpy(&id, ptr, sizeof(DFBWindowID)); if (dfbSurface) dfbSurface->Release(dfbSurface); if (id != (DFBWindowID)-1) { IDirectFBWindow *dw; layer->GetWindow(layer, id, &dw); if (dw->GetSurface(dw, &dfbSurface) != DFB_OK) dfbSurface = 0; dw->Release(dw); } else { dfbSurface = 0; } } bool QDirectFBWindowSurface::scroll(const QRegion ®ion, int dx, int dy) { if (!dfbSurface || !(flipFlags & DSFLIP_BLIT) || region.rectCount() != 1) return false; if (flushPending) { dfbSurface->Flip(dfbSurface, 0, DSFLIP_BLIT); } else { flushPending = true; } dfbSurface->SetBlittingFlags(dfbSurface, DSBLIT_NOFX); const QRect r = region.boundingRect(); const DFBRectangle rect = { r.x(), r.y(), r.width(), r.height() }; dfbSurface->Blit(dfbSurface, dfbSurface, &rect, r.x() + dx, r.y() + dy); return true; } bool QDirectFBWindowSurface::move(const QPoint &moveBy) { setGeometry(geometry().translated(moveBy)); return true; } void QDirectFBWindowSurface::setOpaque(bool opaque) { SurfaceFlags flags = surfaceFlags(); if (opaque != (flags & Opaque)) { if (opaque) { flags |= Opaque; } else { flags &= ~Opaque; } setSurfaceFlags(flags); } } void QDirectFBWindowSurface::flush(QWidget *widget, const QRegion ®ion, const QPoint &offset) { QWidget *win = window(); if (!win) return; #if !defined(QT_NO_QWS_PROXYSCREEN) && !defined(QT_NO_GRAPHICSVIEW) QWExtra *extra = qt_widget_private(widget)->extraData(); if (extra && extra->proxyWidget) return; #else Q_UNUSED(widget); #endif const quint8 windowOpacity = quint8(win->windowOpacity() * 0xff); const QRect windowGeometry = geometry(); #ifdef QT_DIRECTFB_WM quint8 currentOpacity; Q_ASSERT(dfbWindow); dfbWindow->GetOpacity(dfbWindow, ¤tOpacity); if (currentOpacity != windowOpacity) { dfbWindow->SetOpacity(dfbWindow, windowOpacity); } screen->flipSurface(dfbSurface, flipFlags, region, offset); #else setOpaque(windowOpacity == 0xff); if (mode == Offscreen) { screen->exposeRegion(region.translated(offset + geometry().topLeft()), 0); } else { screen->flipSurface(dfbSurface, flipFlags, region, offset); } #endif #ifdef QT_DIRECTFB_TIMING enum { Secs = 3 }; ++frames; if (timer.elapsed() >= Secs * 1000) { qDebug("%d fps", int(double(frames) / double(Secs))); frames = 0; timer.restart(); } #endif flushPending = false; } void QDirectFBWindowSurface::beginPaint(const QRegion ®ion) { if (!engine) { engine = new QDirectFBPaintEngine(this); } if (dfbSurface) { const QWidget *win = window(); if (win && win->testAttribute(Qt::WA_NoSystemBackground)) { QDirectFBScreen::solidFill(dfbSurface, Qt::transparent, region); } } flushPending = true; } void QDirectFBWindowSurface::endPaint(const QRegion &) { #ifdef QT_NO_DIRECTFB_SUBSURFACE unlockSurface(); #endif } IDirectFBSurface *QDirectFBWindowSurface::directFBSurface() const { return dfbSurface; } IDirectFBSurface *QDirectFBWindowSurface::surfaceForWidget(const QWidget *widget, QRect *rect) const { Q_ASSERT(widget); if (!dfbSurface) return 0; QWidget *win = window(); Q_ASSERT(win); if (rect) { if (win == widget) { *rect = widget->rect(); } else { *rect = QRect(widget->mapTo(win, QPoint(0, 0)), widget->size()); } } Q_ASSERT(win == widget || win->isAncestorOf(widget)); return dfbSurface; } void QDirectFBWindowSurface::releaseSurface() { if (dfbSurface) { #ifdef QT_DIRECTFB_SUBSURFACE releaseSubSurface(); #else unlockSurface(); #endif #ifdef QT_NO_DIRECTFB_WM Q_ASSERT(screen->primarySurface()); if (dfbSurface != screen->primarySurface()) #endif dfbSurface->Release(dfbSurface); dfbSurface = 0; } } void QDirectFBWindowSurface::updateIsOpaque() { const QWidget *win = window(); Q_ASSERT(win); if (win->testAttribute(Qt::WA_OpaquePaintEvent) || win->testAttribute(Qt::WA_PaintOnScreen)) { setOpaque(true); return; } if (qFuzzyCompare(static_cast(win->windowOpacity()), 1.0f)) { const QPalette &pal = win->palette(); if (win->autoFillBackground()) { const QBrush &autoFillBrush = pal.brush(win->backgroundRole()); if (autoFillBrush.style() != Qt::NoBrush && autoFillBrush.isOpaque()) { setOpaque(true); return; } } if (win->isWindow() && !win->testAttribute(Qt::WA_NoSystemBackground)) { const QBrush &windowBrush = win->palette().brush(QPalette::Window); if (windowBrush.style() != Qt::NoBrush && windowBrush.isOpaque()) { setOpaque(true); return; } } } setOpaque(false); } QT_END_NAMESPACE #endif // QT_NO_QWS_DIRECTFB