/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Copyright (C) 2010 netlabs.org. OS/2 parts. ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** 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, Nokia gives you certain additional ** rights. These rights are described in the Nokia 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. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qt_os2.h" #include "qlibrary.h" #include "qwindowsurface_pm_p.h" #include "private/qnativeimage_p.h" #include "qdebug.h" //////////////////////////////////////////////////////////////////////////////// // The below definitions are stolen from the OS/2 Toolkit 4.5 headers (dive.h) // to avoid the requirement of having the Toolkit installed when building Qt // and let it dynamically link to dive.dll. #define mmioFOURCC( ch0, ch1, ch2, ch3 ) \ ( (ULONG)(BYTE)(ch0) | ( (ULONG)(BYTE)(ch1) << 8 ) | \ ( (ULONG)(BYTE)(ch2) << 16 ) | ( (ULONG)(BYTE)(ch3) << 24 ) ) #define FOURCC_SCRN 0 #define FOURCC_BGR4 mmioFOURCC( 'B', 'G', 'R', '4' ) #define FOURCC_RGB4 mmioFOURCC( 'R', 'G', 'B', '4' ) #define FOURCC_BGR3 mmioFOURCC( 'B', 'G', 'R', '3' ) #define FOURCC_RGB3 mmioFOURCC( 'R', 'G', 'B', '3' ) #define FOURCC_R565 mmioFOURCC( 'R', '5', '6', '5' ) #define FOURCC_R555 mmioFOURCC( 'R', '5', '5', '5' ) #define FOURCC_R664 mmioFOURCC( 'R', '6', '6', '4' ) #define FOURCC ULONG #define HDIVE ULONG #define DIVE_SUCCESS 0x00000000 #define DIVE_BUFFER_SCREEN 0x00000000 #define DIVE_BUFFER_GRAPHICS_PLANE 0x00000001 #define DIVE_BUFFER_ALTERNATE_PLANE 0x00000002 typedef struct _SETUP_BLITTER { /* Set the ulStructLen field equal to the amount of the structure used. */ /* allowable: blank lines below mark sizes of 8, 28, 32, 52, 60, or 68. */ ULONG ulStructLen; /* Set the ulInvert flags based on the following: */ /* b0001 = d01 = h01 = flip the image in the horizontal direction. */ /* b0010 = d02 = h02 = flip the image in the vertical direction. */ /* All other bits ignored. */ ULONG fInvert; /* This is the color format of the source data. See "FOURCC.H" */ FOURCC fccSrcColorFormat; /* This is the width of the source image in pixels. */ ULONG ulSrcWidth; /* This is the height of the source image in pixels. */ ULONG ulSrcHeight; /* This is the horizontal offset from which to start displaying for */ /* use in displaying a sub-portion of the source image. */ ULONG ulSrcPosX; /* This is the vertical offset from which to start displaying. */ ULONG ulSrcPosY; /* This is the dither type to use. 0 defines no dither and 1 */ /* defines 2x2 dither (all others ignored). Note: dithering is only */ /* supported in direct color to LUT8 conversions. */ ULONG ulDitherType; /* This is the color format of the destinaion data. See "FOURCC.H" */ FOURCC fccDstColorFormat; /* This is the width of the destination image in pixels. */ ULONG ulDstWidth; /* This is the height of the destination image in pixels. */ ULONG ulDstHeight; /* This is the horizontal offset from which to start displaying for */ /* use in displaying to sub-portion of the destination image. */ LONG lDstPosX; /* This is the vertical offset from which to start displaying. */ LONG lDstPosY; /* This is the world screen horizontal position, where 0 is left. */ /* These are ignored if the destination is not the screen. */ LONG lScreenPosX; /* This is the world screen vertical position, where 0 is bottom. */ LONG lScreenPosY; /* This is the number of visible rectangular regions being passed in. */ /* These are ignored if the destination is not the screen. */ /* Also, if you application *KNOWS* that the region is fully visible */ /* (like not going to the screen), the you can use DIVE_FULLY_VISIBLE */ /* instead of making up a bogus visible region structure. */ ULONG ulNumDstRects; /* This points to an array of visible regions which defines what */ /* portions of the source image are to be displayed. */ PRECTL pVisDstRects; /* Pointer to array of visible rectangles. */ } SETUP_BLITTER; typedef SETUP_BLITTER *PSETUP_BLITTER; // functions resolved by dive.dll static ULONG (APIENTRY *DiveOpen) ( HDIVE *phDiveInst, BOOL fNonScreenInstance, PVOID ppFrameBuffer ) = 0; static ULONG (APIENTRY *DiveSetupBlitter) ( HDIVE hDiveInst, PSETUP_BLITTER pSetupBlitter ) = 0; static ULONG (APIENTRY *DiveBlitImage) ( HDIVE hDiveInst, ULONG ulSrcBufNumber, ULONG ulDstBufNumber ) = 0; static ULONG (APIENTRY *DiveClose) ( HDIVE hDiveInst ); static ULONG (APIENTRY *DiveAllocImageBuffer) ( HDIVE hDiveInst, PULONG pulBufferNumber, FOURCC fccColorSpace, ULONG ulWidth, ULONG ulHeight, ULONG ulLineSizeBytes, PBYTE pbImageBuffer ) = 0; static ULONG (APIENTRY *DiveFreeImageBuffer) ( HDIVE hDiveInst, ULONG ulBufferNumber ) = 0; static ULONG (APIENTRY *DiveBeginImageBufferAccess) ( HDIVE hDiveInst, ULONG ulBufferNumber, PBYTE *ppbImageBuffer, PULONG pulBufferScanLineBytes, PULONG pulBufferScanLines ) = 0; static ULONG (APIENTRY *DiveEndImageBufferAccess) ( HDIVE hDiveInst, ULONG ulBufferNumber ) = 0; // function table #define FUNC_ENTRY(name) { #name, (void **)&name } static struct { const char *name; void **entry; } diveDllFuncs[] = { FUNC_ENTRY(DiveOpen), FUNC_ENTRY(DiveSetupBlitter), FUNC_ENTRY(DiveBlitImage), FUNC_ENTRY(DiveClose), FUNC_ENTRY(DiveAllocImageBuffer), FUNC_ENTRY(DiveFreeImageBuffer), FUNC_ENTRY(DiveBeginImageBufferAccess), FUNC_ENTRY(DiveEndImageBufferAccess), }; static QLibrary diveDll(QLatin1String("dive")); static bool diveDllResolved = false; static bool diveDllOK = false; //////////////////////////////////////////////////////////////////////////////// QT_BEGIN_NAMESPACE struct QPMDiveWindowSurfacePrivate { QImage *image; HDIVE hDive; ULONG bufNum; bool posDirty; bool vrnDirty; bool vrnDisabled; SETUP_BLITTER setup; QVector rcls; struct FlushArgs { QRect from; QPoint to; }; QList pending; }; QPMDiveWindowSurface::QPMDiveWindowSurface(QWidget* widget) : QWindowSurface(widget), d(new QPMDiveWindowSurfacePrivate) { d->image = 0; d->hDive = NULLHANDLE; d->bufNum = 0; d->posDirty = true; d->vrnDirty = true; d->vrnDisabled = false; memset(&d->setup, 0, sizeof(SETUP_BLITTER)); // Note that DiveBlitImage() seems to ignore fccSrcColorFormat and // fccSrcColorFormat which is not a big surprise because both values are // known (the source color format is specified at buffer creation time // and the screen color format is always known). Also note that specifying // values other than FOURCC_LUT8/R565/BGR3 causes a crash in DiveBlitImage() // no matter what the actual source format is. FOURCC_SCRN seems to work. d->setup.fccSrcColorFormat = FOURCC_SCRN; d->setup.fccDstColorFormat = FOURCC_SCRN; window()->addPmEventFilter(this); WinSetVisibleRegionNotify(window()->winId(), TRUE); setStaticContentsSupport(true); } QPMDiveWindowSurface::~QPMDiveWindowSurface() { WinSetVisibleRegionNotify(window()->winId(), FALSE); window()->removePmEventFilter(this); if (d->bufNum) DiveFreeImageBuffer(d->hDive, d->bufNum); if (d->hDive != NULLHANDLE) DiveClose(d->hDive); if (d->image) delete d->image; } QPaintDevice *QPMDiveWindowSurface::paintDevice() { return d->image; } void QPMDiveWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset) { // Not ready for painting yet, bail out. This can happen in // QWidget::create_sys() if (!d->image || rgn.rectCount() == 0) return; QRect br = rgn.boundingRect(); QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft(); QRect wbr = br.translated(-wOffset); br.translate(offset); wbr.setBottom(widget->height() - wbr.bottom() - 1); // flip y coordinate if (d->vrnDisabled) { // defer the flush QPMDiveWindowSurfacePrivate::FlushArgs args = { br, wbr.bottomLeft() }; d->pending.append(args); return; } doFlush(br, wbr.bottomLeft()); } void QPMDiveWindowSurface::doFlush(const QRect &from, const QPoint &to) { #if 0 qDebug() << "QPMDiveWindowSurface::doFlush:" << window() << "from" << from << "to" << to; #endif // make sure from doesn't exceed the backing storage size (it may happen // during resize & move due to the different event order) QRect src = from.intersected(QRect(0, 0, d->image->width(), d->image->height())); QPoint dst = to - (src.bottomLeft() - from.bottomLeft()); HWND hwnd = window()->winId(); // don't include lScreenPosX and the rest by default d->setup.ulStructLen = offsetof(SETUP_BLITTER, lScreenPosX); bool setupDirty = false; if (d->posDirty || d->vrnDirty) { setupDirty = true; d->posDirty = false; POINTL ptl = { 0, 0 }; WinMapWindowPoints(window()->winId(), HWND_DESKTOP, &ptl, 1); d->setup.lScreenPosX = ptl.x; d->setup.lScreenPosY = ptl.y; d->setup.ulStructLen = offsetof(SETUP_BLITTER, ulNumDstRects); } if (d->vrnDirty) { setupDirty = true; d->vrnDirty = false; HPS hps = window()->getPS(); HRGN hrgn = GpiCreateRegion(hps, 0L, NULL); RGNRECT rgnCtl; rgnCtl.ircStart = 1; rgnCtl.crc = rgnCtl.crcReturned = 0; rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT; ULONG rc = WinQueryVisibleRegion(hwnd, hrgn); if (rc == RGN_RECT || rc == RGN_COMPLEX) { if (GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, NULL) && rgnCtl.crcReturned) { rgnCtl.ircStart = 1; rgnCtl.crc = rgnCtl.crcReturned; rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT; if (d->rcls.size() < (int)rgnCtl.crc) d->rcls.resize((int)rgnCtl.crc); GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, d->rcls.data()); } } d->setup.ulNumDstRects = rgnCtl.crcReturned; d->setup.pVisDstRects = rgnCtl.crcReturned ? d->rcls.data() : NULL; d->setup.ulStructLen = sizeof(SETUP_BLITTER); GpiDestroyRegion(hps, hrgn); window()->releasePS(hps); } // note that the source image is expected to be top-left oriented // by DiveSetupBlitter() so we don't perform y coordinate flip SETUP_BLITTER setupTmp = d->setup; setupTmp.ulSrcWidth = src.width(); setupTmp.ulSrcHeight = src.height(); setupTmp.ulSrcPosX = src.left(); setupTmp.ulSrcPosY = src.top(); setupTmp.ulDstWidth = setupTmp.ulSrcWidth; setupTmp.ulDstHeight = setupTmp.ulSrcHeight; setupTmp.lDstPosX = dst.x(); setupTmp.lDstPosY = dst.y(); if (memcmp(&d->setup, &setupTmp, d->setup.ulStructLen)) { setupDirty = true; memcpy(&d->setup, &setupTmp, d->setup.ulStructLen); } if (setupDirty) { DiveSetupBlitter(d->hDive, &d->setup); } DiveBlitImage(d->hDive, d->bufNum, DIVE_BUFFER_SCREEN); } bool QPMDiveWindowSurface::pmEventFilter(QMSG *msg, MRESULT *result) { switch (msg->msg) { case WM_VRNDISABLED: { DiveSetupBlitter(d->hDive, NULL); d->vrnDisabled = true; *result = 0; return true; } case WM_VRNENABLED: { d->vrnDisabled = false; if (LONGFROMMP(msg->mp1)) // window's visible region changed d->vrnDirty = true; d->posDirty = true; // process pending flush events foreach(QPMDiveWindowSurfacePrivate::FlushArgs args, d->pending) { doFlush(args.from, args.to); } d->pending.clear(); *result = 0; return true; } default: break; } return false; } void QPMDiveWindowSurface::setGeometry(const QRect &rect) { // this method is mostly like QRasterWindowSurface::prepareBuffer() QWindowSurface::setGeometry(rect); if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) { int width = window()->width(); int height = window()->height(); if (d->image) { width = qMax(d->image->width(), width); height = qMax(d->image->height(), height); } if (width == 0 || height == 0) { delete d->image; d->image = 0; return; } QImage *oldImage = d->image; d->image = new QImage(width, height, QImage::Format_RGB32); // associate the image data pointer with the buffer number DiveFreeImageBuffer(d->hDive, d->bufNum); d->bufNum = 0; ULONG rc = DiveAllocImageBuffer(d->hDive, &d->bufNum, FOURCC_BGR4, width, height, d->image->bytesPerLine(), (PBYTE)const_cast(d->image)->bits()); if (rc != DIVE_SUCCESS) { qWarning("QPMDiveWindowSurface::setGeometry: DiveAllocImageBuffer " "returned 0x%08lX", rc); delete d->image; delete oldImage; return; } if (oldImage && hasStaticContents()) { // Make sure we use the const version of bits() (no detach). const uchar *src = const_cast(oldImage)->bits(); uchar *dst = d->image->bits(); const int srcBytesPerLine = oldImage->bytesPerLine(); const int dstBytesPerLine = d->image->bytesPerLine(); const int bytesPerPixel = oldImage->depth() >> 3; QRegion staticRegion(staticContents()); // Make sure we're inside the boundaries of the old image. staticRegion &= QRect(0, 0, oldImage->width(), oldImage->height()); const QVector &rects = staticRegion.rects(); const QRect *srcRect = rects.constData(); // Copy the static content of the old image into the new one. int numRectsLeft = rects.size(); do { const int bytesOffset = srcRect->x() * bytesPerPixel; const int dy = srcRect->y(); // Adjust src and dst to point to the right offset. const uchar *s = src + dy * srcBytesPerLine + bytesOffset; uchar *d = dst + dy * dstBytesPerLine + bytesOffset; const int numBytes = srcRect->width() * bytesPerPixel; int numScanLinesLeft = srcRect->height(); do { ::memcpy(d, s, numBytes); d += dstBytesPerLine; s += srcBytesPerLine; } while (--numScanLinesLeft); ++srcRect; } while (--numRectsLeft); } delete oldImage; } } // from qwindowsurface.cpp extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); bool QPMDiveWindowSurface::scroll(const QRegion &area, int dx, int dy) { if (!d->image || d->image->isNull()) return false; const QVector rects = area.rects(); for (int i = 0; i < rects.size(); ++i) qt_scrollRectInImage(*d->image, rects.at(i), QPoint(dx, dy)); return true; } // static QPMDiveWindowSurface *QPMDiveWindowSurface::create(QWidget *widget) { if (!diveDllResolved) { diveDllResolved = true; diveDllOK = true; for (size_t i = 0; i < sizeof(diveDllFuncs) / sizeof(diveDllFuncs[0]); ++i) { *diveDllFuncs[i].entry = diveDll.resolve(diveDllFuncs[i].name); if (!*diveDllFuncs[i].entry) { diveDllOK = false; break; } } } // Note: returning 0 from this method will cause using QRasterWindowSurface // as a fallback if (!diveDllOK) return 0; // Attempt to create a new DIVE instance for this widget HDIVE hDive = NULLHANDLE; ULONG rc = DiveOpen(&hDive, FALSE, 0); if (rc != DIVE_SUCCESS) { qWarning("QPMDiveWindowSurface::create: DiveOpen returned 0x%08lX", rc); return 0; } QPMDiveWindowSurface *surface = new QPMDiveWindowSurface(widget); if (surface) surface->d->hDive = hDive; else DiveClose(hDive); return surface; } QT_END_NAMESPACE