source: trunk/src/gui/painting/qwindowsurface_pm.cpp@ 707

Last change on this file since 707 was 707, checked in by Dmitry A. Kuminov, 16 years ago

gui: Process deferred window flush events in Dive mode to make sure the window is properly updated after resizing etc.

File size: 19.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** Copyright (C) 2010 netlabs.org. OS/2 parts.
8**
9** This file is part of the QtGui module of the Qt Toolkit.
10**
11** $QT_BEGIN_LICENSE:LGPL$
12** Commercial Usage
13** Licensees holding valid Qt Commercial licenses may use this file in
14** accordance with the Qt Commercial License Agreement provided with the
15** Software or, alternatively, in accordance with the terms contained in
16** a written agreement between you and Nokia.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 2.1 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 2.1 requirements
24** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25**
26** In addition, as a special exception, Nokia gives you certain additional
27** rights. These rights are described in the Nokia Qt LGPL Exception
28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29**
30** GNU General Public License Usage
31** Alternatively, this file may be used under the terms of the GNU
32** General Public License version 3.0 as published by the Free Software
33** Foundation and appearing in the file LICENSE.GPL included in the
34** packaging of this file. Please review the following information to
35** ensure the GNU General Public License version 3.0 requirements will be
36** met: http://www.gnu.org/copyleft/gpl.html.
37**
38** If you have questions regarding the use of this file, please contact
39** Nokia at [email protected].
40** $QT_END_LICENSE$
41**
42****************************************************************************/
43
44#include "qt_os2.h"
45#include "qlibrary.h"
46
47#include "qwindowsurface_pm_p.h"
48#include "private/qnativeimage_p.h"
49
50#include "qdebug.h"
51
52////////////////////////////////////////////////////////////////////////////////
53
54// The below definitions are stolen from the OS/2 Toolkit 4.5 headers (dive.h)
55// to avoid the requirement of having the Toolkit installed when building Qt
56// and let it dynamically link to dive.dll.
57
58#define mmioFOURCC( ch0, ch1, ch2, ch3 ) \
59 ( (ULONG)(BYTE)(ch0) | ( (ULONG)(BYTE)(ch1) << 8 ) | \
60 ( (ULONG)(BYTE)(ch2) << 16 ) | ( (ULONG)(BYTE)(ch3) << 24 ) )
61
62#define FOURCC_SCRN 0
63#define FOURCC_BGR4 mmioFOURCC( 'B', 'G', 'R', '4' )
64#define FOURCC_RGB4 mmioFOURCC( 'R', 'G', 'B', '4' )
65#define FOURCC_BGR3 mmioFOURCC( 'B', 'G', 'R', '3' )
66#define FOURCC_RGB3 mmioFOURCC( 'R', 'G', 'B', '3' )
67#define FOURCC_R565 mmioFOURCC( 'R', '5', '6', '5' )
68#define FOURCC_R555 mmioFOURCC( 'R', '5', '5', '5' )
69#define FOURCC_R664 mmioFOURCC( 'R', '6', '6', '4' )
70
71#define FOURCC ULONG
72#define HDIVE ULONG
73
74#define DIVE_SUCCESS 0x00000000
75
76#define DIVE_BUFFER_SCREEN 0x00000000
77#define DIVE_BUFFER_GRAPHICS_PLANE 0x00000001
78#define DIVE_BUFFER_ALTERNATE_PLANE 0x00000002
79
80typedef struct _SETUP_BLITTER {
81
82 /* Set the ulStructLen field equal to the amount of the structure used. */
83 /* allowable: blank lines below mark sizes of 8, 28, 32, 52, 60, or 68. */
84 ULONG ulStructLen;
85 /* Set the ulInvert flags based on the following: */
86 /* b0001 = d01 = h01 = flip the image in the horizontal direction. */
87 /* b0010 = d02 = h02 = flip the image in the vertical direction. */
88 /* All other bits ignored. */
89 ULONG fInvert;
90
91 /* This is the color format of the source data. See "FOURCC.H" */
92 FOURCC fccSrcColorFormat;
93 /* This is the width of the source image in pixels. */
94 ULONG ulSrcWidth;
95 /* This is the height of the source image in pixels. */
96 ULONG ulSrcHeight;
97 /* This is the horizontal offset from which to start displaying for */
98 /* use in displaying a sub-portion of the source image. */
99 ULONG ulSrcPosX;
100 /* This is the vertical offset from which to start displaying. */
101 ULONG ulSrcPosY;
102
103 /* This is the dither type to use. 0 defines no dither and 1 */
104 /* defines 2x2 dither (all others ignored). Note: dithering is only */
105 /* supported in direct color to LUT8 conversions. */
106 ULONG ulDitherType;
107
108 /* This is the color format of the destinaion data. See "FOURCC.H" */
109 FOURCC fccDstColorFormat;
110 /* This is the width of the destination image in pixels. */
111 ULONG ulDstWidth;
112 /* This is the height of the destination image in pixels. */
113 ULONG ulDstHeight;
114 /* This is the horizontal offset from which to start displaying for */
115 /* use in displaying to sub-portion of the destination image. */
116 LONG lDstPosX;
117 /* This is the vertical offset from which to start displaying. */
118 LONG lDstPosY;
119
120 /* This is the world screen horizontal position, where 0 is left. */
121 /* These are ignored if the destination is not the screen. */
122 LONG lScreenPosX;
123 /* This is the world screen vertical position, where 0 is bottom. */
124 LONG lScreenPosY;
125
126 /* This is the number of visible rectangular regions being passed in. */
127 /* These are ignored if the destination is not the screen. */
128 /* Also, if you application *KNOWS* that the region is fully visible */
129 /* (like not going to the screen), the you can use DIVE_FULLY_VISIBLE */
130 /* instead of making up a bogus visible region structure. */
131 ULONG ulNumDstRects;
132 /* This points to an array of visible regions which defines what */
133 /* portions of the source image are to be displayed. */
134 PRECTL pVisDstRects; /* Pointer to array of visible rectangles. */
135
136 } SETUP_BLITTER;
137typedef SETUP_BLITTER *PSETUP_BLITTER;
138
139// functions resolved by dive.dll
140
141static
142ULONG (APIENTRY *DiveOpen) ( HDIVE *phDiveInst,
143 BOOL fNonScreenInstance,
144 PVOID ppFrameBuffer ) = 0;
145
146static
147ULONG (APIENTRY *DiveSetupBlitter) ( HDIVE hDiveInst,
148 PSETUP_BLITTER pSetupBlitter ) = 0;
149
150static
151ULONG (APIENTRY *DiveBlitImage) ( HDIVE hDiveInst,
152 ULONG ulSrcBufNumber,
153 ULONG ulDstBufNumber ) = 0;
154
155static
156ULONG (APIENTRY *DiveClose) ( HDIVE hDiveInst );
157
158static
159ULONG (APIENTRY *DiveAllocImageBuffer) ( HDIVE hDiveInst,
160 PULONG pulBufferNumber,
161 FOURCC fccColorSpace,
162 ULONG ulWidth,
163 ULONG ulHeight,
164 ULONG ulLineSizeBytes,
165 PBYTE pbImageBuffer ) = 0;
166
167static
168ULONG (APIENTRY *DiveFreeImageBuffer) ( HDIVE hDiveInst,
169 ULONG ulBufferNumber ) = 0;
170
171static
172ULONG (APIENTRY *DiveBeginImageBufferAccess) ( HDIVE hDiveInst,
173 ULONG ulBufferNumber,
174 PBYTE *ppbImageBuffer,
175 PULONG pulBufferScanLineBytes,
176 PULONG pulBufferScanLines ) = 0;
177
178static
179ULONG (APIENTRY *DiveEndImageBufferAccess) ( HDIVE hDiveInst,
180 ULONG ulBufferNumber ) = 0;
181
182// function table
183
184#define FUNC_ENTRY(name) { #name, (void **)&name }
185
186static struct { const char *name; void **entry; } diveDllFuncs[] =
187{
188 FUNC_ENTRY(DiveOpen),
189 FUNC_ENTRY(DiveSetupBlitter),
190 FUNC_ENTRY(DiveBlitImage),
191 FUNC_ENTRY(DiveClose),
192 FUNC_ENTRY(DiveAllocImageBuffer),
193 FUNC_ENTRY(DiveFreeImageBuffer),
194 FUNC_ENTRY(DiveBeginImageBufferAccess),
195 FUNC_ENTRY(DiveEndImageBufferAccess),
196};
197
198static QLibrary diveDll(QLatin1String("dive"));
199static bool diveDllResolved = false;
200static bool diveDllOK = false;
201
202////////////////////////////////////////////////////////////////////////////////
203
204QT_BEGIN_NAMESPACE
205
206struct QPMDiveWindowSurfacePrivate
207{
208 QImage *image;
209 HDIVE hDive;
210 ULONG bufNum;
211 bool posDirty;
212 bool vrnDirty;
213 bool vrnDisabled;
214 SETUP_BLITTER setup;
215 QVector<RECTL> rcls;
216
217 struct FlushArgs
218 {
219 QRect from;
220 QPoint to;
221 };
222 QList<FlushArgs> pending;
223};
224
225QPMDiveWindowSurface::QPMDiveWindowSurface(QWidget* widget)
226 : QWindowSurface(widget), d(new QPMDiveWindowSurfacePrivate)
227{
228 d->image = 0;
229 d->hDive = NULLHANDLE;
230 d->bufNum = 0;
231 d->posDirty = true;
232 d->vrnDirty = true;
233 d->vrnDisabled = false;
234
235 memset(&d->setup, 0, sizeof(SETUP_BLITTER));
236
237 // Note that DiveBlitImage() seems to ignore fccSrcColorFormat and
238 // fccSrcColorFormat which is not a big surprise because both values are
239 // known (the source color format is specified at buffer creation time
240 // and the screen color format is always known). Also note that specifying
241 // values other than FOURCC_LUT8/R565/BGR3 causes a crash in DiveBlitImage()
242 // no matter what the actual source format is. FOURCC_SCRN seems to work.
243 d->setup.fccSrcColorFormat = FOURCC_SCRN;
244 d->setup.fccDstColorFormat = FOURCC_SCRN;
245
246 window()->addPmEventFilter(this);
247 WinSetVisibleRegionNotify(window()->winId(), TRUE);
248
249 setStaticContentsSupport(true);
250}
251
252QPMDiveWindowSurface::~QPMDiveWindowSurface()
253{
254 WinSetVisibleRegionNotify(window()->winId(), FALSE);
255 window()->removePmEventFilter(this);
256
257 if (d->bufNum)
258 DiveFreeImageBuffer(d->hDive, d->bufNum);
259 if (d->hDive != NULLHANDLE)
260 DiveClose(d->hDive);
261 if (d->image)
262 delete d->image;
263}
264
265QPaintDevice *QPMDiveWindowSurface::paintDevice()
266{
267 return d->image;
268}
269
270void QPMDiveWindowSurface::flush(QWidget *widget, const QRegion &rgn,
271 const QPoint &offset)
272{
273 // Not ready for painting yet, bail out. This can happen in
274 // QWidget::create_sys()
275 if (!d->image || rgn.rectCount() == 0)
276 return;
277
278 QRect br = rgn.boundingRect();
279
280 QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
281 QRect wbr = br.translated(-wOffset);
282 br.translate(offset);
283
284 wbr.setBottom(widget->height() - wbr.bottom() - 1); // flip y coordinate
285
286 if (d->vrnDisabled) {
287 // defer the flush
288 QPMDiveWindowSurfacePrivate::FlushArgs args = { br, wbr.bottomLeft() };
289 d->pending.append(args);
290 return;
291 }
292
293 doFlush(br, wbr.bottomLeft());
294}
295
296void QPMDiveWindowSurface::doFlush(const QRect &from, const QPoint &to)
297{
298#if 0
299 qDebug() << "QPMDiveWindowSurface::doFlush:" << window()
300 << "from" << from << "to" << to;
301#endif
302
303 // make sure from doesn't exceed the backing storage size (it may happen
304 // during resize & move due to the different event order)
305 QRect src = from.intersected(QRect(0, 0, d->image->width(), d->image->height()));
306 QPoint dst = to - (src.bottomLeft() - from.bottomLeft());
307
308 HWND hwnd = window()->winId();
309
310 // don't include lScreenPosX and the rest by default
311 d->setup.ulStructLen = offsetof(SETUP_BLITTER, lScreenPosX);
312 bool setupDirty = false;
313
314 if (d->posDirty || d->vrnDirty) {
315 setupDirty = true;
316 d->posDirty = false;
317 POINTL ptl = { 0, 0 };
318 WinMapWindowPoints(window()->winId(), HWND_DESKTOP, &ptl, 1);
319 d->setup.lScreenPosX = ptl.x;
320 d->setup.lScreenPosY = ptl.y;
321 d->setup.ulStructLen = offsetof(SETUP_BLITTER, ulNumDstRects);
322 }
323
324 if (d->vrnDirty) {
325 setupDirty = true;
326 d->vrnDirty = false;
327
328 HPS hps = window()->getPS();
329 HRGN hrgn = GpiCreateRegion(hps, 0L, NULL);
330
331 RGNRECT rgnCtl;
332 rgnCtl.ircStart = 1;
333 rgnCtl.crc = rgnCtl.crcReturned = 0;
334 rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
335
336 ULONG rc = WinQueryVisibleRegion(hwnd, hrgn);
337 if (rc == RGN_RECT || rc == RGN_COMPLEX) {
338 if (GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, NULL) &&
339 rgnCtl.crcReturned) {
340 rgnCtl.ircStart = 1;
341 rgnCtl.crc = rgnCtl.crcReturned;
342 rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
343 if (d->rcls.size() < (int)rgnCtl.crc)
344 d->rcls.resize((int)rgnCtl.crc);
345 GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, d->rcls.data());
346 }
347 }
348
349 d->setup.ulNumDstRects = rgnCtl.crcReturned;
350 d->setup.pVisDstRects = rgnCtl.crcReturned ? d->rcls.data() : NULL;
351 d->setup.ulStructLen = sizeof(SETUP_BLITTER);
352
353 GpiDestroyRegion(hps, hrgn);
354 window()->releasePS(hps);
355 }
356
357 // note that the source image is expected to be top-left oriented
358 // by DiveSetupBlitter() so we don't perform y coordinate flip
359
360 SETUP_BLITTER setupTmp = d->setup;
361 setupTmp.ulSrcWidth = src.width();
362 setupTmp.ulSrcHeight = src.height();
363 setupTmp.ulSrcPosX = src.left();
364 setupTmp.ulSrcPosY = src.top();
365 setupTmp.ulDstWidth = setupTmp.ulSrcWidth;
366 setupTmp.ulDstHeight = setupTmp.ulSrcHeight;
367 setupTmp.lDstPosX = dst.x();
368 setupTmp.lDstPosY = dst.y();
369
370 if (memcmp(&d->setup, &setupTmp, d->setup.ulStructLen)) {
371 setupDirty = true;
372 memcpy(&d->setup, &setupTmp, d->setup.ulStructLen);
373 }
374
375 if (setupDirty) {
376 DiveSetupBlitter(d->hDive, &d->setup);
377 }
378
379 DiveBlitImage(d->hDive, d->bufNum, DIVE_BUFFER_SCREEN);
380}
381
382bool QPMDiveWindowSurface::pmEventFilter(QMSG *msg, MRESULT *result)
383{
384 switch (msg->msg) {
385 case WM_VRNDISABLED: {
386 DiveSetupBlitter(d->hDive, NULL);
387 d->vrnDisabled = true;
388 *result = 0;
389 return true;
390 }
391 case WM_VRNENABLED: {
392 d->vrnDisabled = false;
393 if (LONGFROMMP(msg->mp1)) // window's visible region changed
394 d->vrnDirty = true;
395 d->posDirty = true;
396
397 // process pending flush events
398 foreach(QPMDiveWindowSurfacePrivate::FlushArgs args, d->pending) {
399 doFlush(args.from, args.to);
400 }
401 d->pending.clear();
402
403 *result = 0;
404 return true;
405 }
406 default:
407 break;
408 }
409
410 return false;
411}
412
413void QPMDiveWindowSurface::setGeometry(const QRect &rect)
414{
415 // this method is mostly like QRasterWindowSurface::prepareBuffer()
416
417 QWindowSurface::setGeometry(rect);
418
419 if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) {
420 int width = window()->width();
421 int height = window()->height();
422 if (d->image) {
423 width = qMax(d->image->width(), width);
424 height = qMax(d->image->height(), height);
425 }
426
427 if (width == 0 || height == 0) {
428 delete d->image;
429 d->image = 0;
430 return;
431 }
432
433 QImage *oldImage = d->image;
434
435 d->image = new QImage(width, height, QImage::Format_RGB32);
436
437 // associate the image data pointer with the buffer number
438 DiveFreeImageBuffer(d->hDive, d->bufNum);
439 d->bufNum = 0;
440 ULONG rc = DiveAllocImageBuffer(d->hDive, &d->bufNum, FOURCC_BGR4,
441 width, height,
442 d->image->bytesPerLine(),
443 (PBYTE)const_cast<const QImage *>(d->image)->bits());
444
445 if (rc != DIVE_SUCCESS) {
446 qWarning("QPMDiveWindowSurface::setGeometry: DiveAllocImageBuffer "
447 "returned 0x%08lX", rc);
448 delete d->image;
449 delete oldImage;
450 return;
451 }
452
453 if (oldImage && hasStaticContents()) {
454 // Make sure we use the const version of bits() (no detach).
455 const uchar *src = const_cast<const QImage *>(oldImage)->bits();
456 uchar *dst = d->image->bits();
457
458 const int srcBytesPerLine = oldImage->bytesPerLine();
459 const int dstBytesPerLine = d->image->bytesPerLine();
460 const int bytesPerPixel = oldImage->depth() >> 3;
461
462 QRegion staticRegion(staticContents());
463 // Make sure we're inside the boundaries of the old image.
464 staticRegion &= QRect(0, 0, oldImage->width(), oldImage->height());
465 const QVector<QRect> &rects = staticRegion.rects();
466 const QRect *srcRect = rects.constData();
467
468 // Copy the static content of the old image into the new one.
469 int numRectsLeft = rects.size();
470 do {
471 const int bytesOffset = srcRect->x() * bytesPerPixel;
472 const int dy = srcRect->y();
473
474 // Adjust src and dst to point to the right offset.
475 const uchar *s = src + dy * srcBytesPerLine + bytesOffset;
476 uchar *d = dst + dy * dstBytesPerLine + bytesOffset;
477 const int numBytes = srcRect->width() * bytesPerPixel;
478
479 int numScanLinesLeft = srcRect->height();
480 do {
481 ::memcpy(d, s, numBytes);
482 d += dstBytesPerLine;
483 s += srcBytesPerLine;
484 } while (--numScanLinesLeft);
485
486 ++srcRect;
487 } while (--numRectsLeft);
488 }
489
490 delete oldImage;
491 }
492}
493
494// from qwindowsurface.cpp
495extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
496
497bool QPMDiveWindowSurface::scroll(const QRegion &area, int dx, int dy)
498{
499 if (!d->image || d->image->isNull())
500 return false;
501
502 const QVector<QRect> rects = area.rects();
503 for (int i = 0; i < rects.size(); ++i)
504 qt_scrollRectInImage(*d->image, rects.at(i), QPoint(dx, dy));
505
506 return true;
507}
508
509// static
510QPMDiveWindowSurface *QPMDiveWindowSurface::create(QWidget *widget)
511{
512 if (!diveDllResolved) {
513 diveDllResolved = true;
514 diveDllOK = true;
515 for (size_t i = 0; i < sizeof(diveDllFuncs) / sizeof(diveDllFuncs[0]); ++i) {
516 *diveDllFuncs[i].entry = diveDll.resolve(diveDllFuncs[i].name);
517 if (!*diveDllFuncs[i].entry) {
518 diveDllOK = false;
519 break;
520 }
521 }
522 }
523
524 // Note: returning 0 from this method will cause using QRasterWindowSurface
525 // as a fallback
526
527 if (!diveDllOK)
528 return 0;
529
530 // Attempt to create a new DIVE instance for this widget
531 HDIVE hDive = NULLHANDLE;
532 ULONG rc = DiveOpen(&hDive, FALSE, 0);
533 if (rc != DIVE_SUCCESS) {
534 qWarning("QPMDiveWindowSurface::create: DiveOpen returned 0x%08lX", rc);
535 return 0;
536 }
537
538 QPMDiveWindowSurface *surface = new QPMDiveWindowSurface(widget);
539 if (surface)
540 surface->d->hDive = hDive;
541 else
542 DiveClose(hDive);
543
544 return surface;
545}
546
547QT_END_NAMESPACE
548
Note: See TracBrowser for help on using the repository browser.