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

Last change on this file since 1058 was 1035, checked in by Dmitry A. Kuminov, 14 years ago

OS/2: Add proper processing of WM_VRNENABLED for child widgets.

This became possible after r1034 and fixes some repainting issues
in applications (like video players) that paint on Qt widgets externally
using native window IDs.

File size: 51.8 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 <qdebug.h>
45
46//#define QDIVE_DEBUG
47
48#if defined QDIVE_DEBUG
49#define DEBUG(a) qDebug a
50#define DEBUG_VAR(v) DEBUG(() << #v << v)
51#define DEBUG_VAR_HEX(v) DEBUG(() << #v << qDebugFmtHex(v))
52#else
53#define DEBUG(a) do {} while(0)
54#define DEBUG_VAR(v) do {} while(0)
55#define DEBUG_VAR_HEX(v) do {} while(0)
56#endif
57
58#include <qt_os2.h>
59#include <qlibrary.h>
60#include <qobject.h>
61#include <qevent.h>
62
63#include "qwindowsurface_pm_p.h"
64#include "private/qnativeimage_p.h"
65#include "private/qdnd_p.h"
66
67////////////////////////////////////////////////////////////////////////////////
68
69// The below definitions are stolen from the OS/2 Toolkit 4.5 headers (dive.h)
70// to avoid the requirement of having the Toolkit installed when building Qt
71// and let it dynamically link to dive.dll.
72
73#define mmioFOURCC( ch0, ch1, ch2, ch3 ) \
74 ( (ULONG)(BYTE)(ch0) | ( (ULONG)(BYTE)(ch1) << 8 ) | \
75 ( (ULONG)(BYTE)(ch2) << 16 ) | ( (ULONG)(BYTE)(ch3) << 24 ) )
76
77#define FOURCC_SCRN 0
78#define FOURCC_BGR4 mmioFOURCC( 'B', 'G', 'R', '4' )
79#define FOURCC_RGB4 mmioFOURCC( 'R', 'G', 'B', '4' )
80#define FOURCC_BGR3 mmioFOURCC( 'B', 'G', 'R', '3' )
81#define FOURCC_RGB3 mmioFOURCC( 'R', 'G', 'B', '3' )
82#define FOURCC_R565 mmioFOURCC( 'R', '5', '6', '5' )
83#define FOURCC_R555 mmioFOURCC( 'R', '5', '5', '5' )
84#define FOURCC_R664 mmioFOURCC( 'R', '6', '6', '4' )
85
86#define FOURCC ULONG
87#define HDIVE ULONG
88
89#define DIVE_SUCCESS 0x00000000
90
91#define DIVE_BUFFER_SCREEN 0x00000000
92#define DIVE_BUFFER_GRAPHICS_PLANE 0x00000001
93#define DIVE_BUFFER_ALTERNATE_PLANE 0x00000002
94
95typedef struct _DIVE_CAPS {
96
97 ULONG ulStructLen; /* Set equal to sizeof(DIVE_CAPS) */
98 ULONG ulPlaneCount; /* Number of defined planes. */
99
100 /* Info returned in the following fields pertains to ulPlaneID. */
101 BOOL fScreenDirect; /* TRUE if can get addressability to vram. */
102 BOOL fBankSwitched; /* TRUE if vram is bank-switched. */
103 ULONG ulDepth; /* Number of bits per pixel. */
104 ULONG ulHorizontalResolution; /* Screen width in pixels. */
105 ULONG ulVerticalResolution; /* Screen height in pixels. */
106 ULONG ulScanLineBytes; /* Screen scan line size in bytes. */
107 FOURCC fccColorEncoding; /* Colorspace encoding of the screen. */
108 ULONG ulApertureSize; /* Size of vram aperture in bytes. */
109
110 ULONG ulInputFormats; /* Number of input color formats. */
111 ULONG ulOutputFormats; /* Number of output color formats. */
112 ULONG ulFormatLength; /* Length of format buffer. */
113 PVOID pFormatData; /* Pointer to color format buffer FOURCC's.*/
114
115 } DIVE_CAPS;
116typedef DIVE_CAPS *PDIVE_CAPS;
117
118typedef struct _SETUP_BLITTER {
119
120 /* Set the ulStructLen field equal to the amount of the structure used. */
121 /* allowable: blank lines below mark sizes of 8, 28, 32, 52, 60, or 68. */
122 ULONG ulStructLen;
123 /* Set the ulInvert flags based on the following: */
124 /* b0001 = d01 = h01 = flip the image in the horizontal direction. */
125 /* b0010 = d02 = h02 = flip the image in the vertical direction. */
126 /* All other bits ignored. */
127 ULONG fInvert;
128
129 /* This is the color format of the source data. See "FOURCC.H" */
130 FOURCC fccSrcColorFormat;
131 /* This is the width of the source image in pixels. */
132 ULONG ulSrcWidth;
133 /* This is the height of the source image in pixels. */
134 ULONG ulSrcHeight;
135 /* This is the horizontal offset from which to start displaying for */
136 /* use in displaying a sub-portion of the source image. */
137 ULONG ulSrcPosX;
138 /* This is the vertical offset from which to start displaying. */
139 ULONG ulSrcPosY;
140
141 /* This is the dither type to use. 0 defines no dither and 1 */
142 /* defines 2x2 dither (all others ignored). Note: dithering is only */
143 /* supported in direct color to LUT8 conversions. */
144 ULONG ulDitherType;
145
146 /* This is the color format of the destinaion data. See "FOURCC.H" */
147 FOURCC fccDstColorFormat;
148 /* This is the width of the destination image in pixels. */
149 ULONG ulDstWidth;
150 /* This is the height of the destination image in pixels. */
151 ULONG ulDstHeight;
152 /* This is the horizontal offset from which to start displaying for */
153 /* use in displaying to sub-portion of the destination image. */
154 LONG lDstPosX;
155 /* This is the vertical offset from which to start displaying. */
156 LONG lDstPosY;
157
158 /* This is the world screen horizontal position, where 0 is left. */
159 /* These are ignored if the destination is not the screen. */
160 LONG lScreenPosX;
161 /* This is the world screen vertical position, where 0 is bottom. */
162 LONG lScreenPosY;
163
164 /* This is the number of visible rectangular regions being passed in. */
165 /* These are ignored if the destination is not the screen. */
166 /* Also, if you application *KNOWS* that the region is fully visible */
167 /* (like not going to the screen), the you can use DIVE_FULLY_VISIBLE */
168 /* instead of making up a bogus visible region structure. */
169 ULONG ulNumDstRects;
170 /* This points to an array of visible regions which defines what */
171 /* portions of the source image are to be displayed. */
172 PRECTL pVisDstRects; /* Pointer to array of visible rectangles. */
173
174 } SETUP_BLITTER;
175typedef SETUP_BLITTER *PSETUP_BLITTER;
176
177// functions resolved by dive.dll
178
179static
180ULONG (APIENTRY *DiveQueryCaps) ( PDIVE_CAPS pDiveCaps,
181 ULONG ulPlaneBufNum ) = 0;
182
183static
184ULONG (APIENTRY *DiveOpen) ( HDIVE *phDiveInst,
185 BOOL fNonScreenInstance,
186 PVOID ppFrameBuffer ) = 0;
187
188static
189ULONG (APIENTRY *DiveSetupBlitter) ( HDIVE hDiveInst,
190 PSETUP_BLITTER pSetupBlitter ) = 0;
191
192static
193ULONG (APIENTRY *DiveBlitImage) ( HDIVE hDiveInst,
194 ULONG ulSrcBufNumber,
195 ULONG ulDstBufNumber ) = 0;
196
197static
198ULONG (APIENTRY *DiveClose) ( HDIVE hDiveInst ) = 0;
199
200static
201ULONG (APIENTRY *DiveAcquireFrameBuffer) ( HDIVE hDiveInst,
202 PRECTL prectlDst ) = 0;
203
204static
205ULONG (APIENTRY *DiveDeacquireFrameBuffer) ( HDIVE hDiveInst ) = 0;
206
207static
208ULONG (APIENTRY *DiveAllocImageBuffer) ( HDIVE hDiveInst,
209 PULONG pulBufferNumber,
210 FOURCC fccColorSpace,
211 ULONG ulWidth,
212 ULONG ulHeight,
213 ULONG ulLineSizeBytes,
214 PBYTE pbImageBuffer ) = 0;
215
216static
217ULONG (APIENTRY *DiveFreeImageBuffer) ( HDIVE hDiveInst,
218 ULONG ulBufferNumber ) = 0;
219
220static
221ULONG (APIENTRY *DiveBeginImageBufferAccess) ( HDIVE hDiveInst,
222 ULONG ulBufferNumber,
223 PBYTE *ppbImageBuffer,
224 PULONG pulBufferScanLineBytes,
225 PULONG pulBufferScanLines ) = 0;
226
227static
228ULONG (APIENTRY *DiveEndImageBufferAccess) ( HDIVE hDiveInst,
229 ULONG ulBufferNumber ) = 0;
230
231// function table
232
233#define FUNC_ENTRY(name) { #name, (void **)&name }
234
235static struct { const char *name; void **entry; } diveDllFuncs[] =
236{
237 FUNC_ENTRY(DiveQueryCaps),
238 FUNC_ENTRY(DiveOpen),
239 FUNC_ENTRY(DiveSetupBlitter),
240 FUNC_ENTRY(DiveBlitImage),
241 FUNC_ENTRY(DiveClose),
242 FUNC_ENTRY(DiveAcquireFrameBuffer),
243 FUNC_ENTRY(DiveDeacquireFrameBuffer),
244 FUNC_ENTRY(DiveAllocImageBuffer),
245 FUNC_ENTRY(DiveFreeImageBuffer),
246 FUNC_ENTRY(DiveBeginImageBufferAccess),
247 FUNC_ENTRY(DiveEndImageBufferAccess),
248};
249
250static QLibrary diveDll(QLatin1String("dive"));
251static bool diveDllResolved = false;
252static bool diveDllOK = false;
253
254static DIVE_CAPS diveCaps = { 0 };
255static bool diveUseFB = false;
256static ULONG diveColorMap[3][256] = { { 0 } };
257static HDIVE diveHandle = NULLHANDLE;
258static char *diveFrameBuf = NULL;
259static bool diveHideMouse = false;
260static FOURCC diveBestBufFormat = 0;
261
262static LONG mousePtrSize = 0;
263
264////////////////////////////////////////////////////////////////////////////////
265
266QT_BEGIN_NAMESPACE
267
268#ifdef Q_CC_GNU
269extern inline unsigned bswap32_p(unsigned u)
270{
271 __asm__ __volatile__ ("bswap %0\n"
272 : "=r" (u)
273 : "0" (u));
274 return u;
275}
276#else
277#define bswap32_p(a) \
278 ((((ULONG)(a)) >> 24) | (((ULONG)(a)) << 24) | \
279 (((ULONG)(a) << 8) & 0x00ff0000) | (((ULONG)(a) >> 8) & 0x0000ff00))
280#endif
281
282// Returns a directly matching QImage format for the given FOURCC (including
283// bit order and depth, scan line size etc.) or Format_Invalid.
284static QImage::Format fourccToFormat(FOURCC fourcc)
285{
286 switch (fourcc) {
287 case FOURCC_R555: return QImage::Format_RGB555;
288 case FOURCC_R565: return QImage::Format_RGB16;
289 case FOURCC_RGB3: return QImage::Format_RGB888;
290 case FOURCC_BGR4: return QImage::Format_RGB32;
291 default: return QImage::Format_Invalid;
292 }
293}
294
295// Returns a FOURCC that is best for the buffer used to blit to the given
296// screen FOURCC. Returns 0 (FOURC_SCRN) if there is no suitable conversion,
297// otherwise it is guaranteed that the returned value is accepted by
298// fourccToFormat(). If isFB is true, the selection is made for the direct
299// framebuffer access mode.
300static FOURCC fourccScreenToBuffer(FOURCC fourcc, bool isFB)
301{
302 // return it as is if supported by fourccToFormat()
303 if (fourccToFormat(fourcc) != QImage::Format_Invalid)
304 return fourcc;
305
306 if (!isFB) {
307 // otherwise, use FOURCC_RGB3 (which in theory should always work; if not,
308 // we will add exceptions here and return 0 in such cases). Note that
309 // although working with 32-bit pixels would be faster, we cannot return
310 // FOURCC_BGR4 here because DiveBlitImage() is known to crahsh when the
311 // source buffer is BGR4 and the screen is not (at least, it's the case
312 // with recent SNAP versions)
313 return FOURCC_RGB3;
314 }
315
316 // in direct framebuffer access mode, we use BGR4 which should be faster
317 // (note that our color conversion table built in QPMDiveWindowSurface::create()
318 // only works with BGR4 for now so we must return it here otherwise the FB
319 // mode will be disabled)
320 return FOURCC_BGR4;
321}
322
323class QPMDiveWindowSurfaceFB : public QPMDiveWindowSurface
324{
325public:
326 QPMDiveWindowSurfaceFB(QWidget *widget);
327 ~QPMDiveWindowSurfaceFB();
328 void doFlush(QWidget *widget, const QRect &from, const QPoint &to);
329};
330
331struct QPMDiveWindowSurfacePrivate : public QObject, private QWidget::PmEventFilter
332{
333 QPMDiveWindowSurface *that;
334
335 QImage *image;
336 HDIVE hDive;
337 bool useFB;
338 ULONG bufNum;
339 bool posDirty;
340 bool inDrag;
341 SETUP_BLITTER setup;
342
343 struct FlushArgs
344 {
345 QWidget *widget;
346 QRect from;
347 QPoint to;
348 };
349 QList<FlushArgs> pending;
350
351 struct WidgetData
352 {
353 int widgetHeight;
354 bool vrnDirty : 1;
355 bool vrnDisabled : 1;
356 size_t rclCount;
357 QVector<RECTL> rcls;
358 };
359
360 WidgetData data;
361 QMap<QWidget *, WidgetData> *subWidgets;
362
363 void addWidget(QWidget *widget);
364 void removeWidget(QWidget *widget);
365
366 WidgetData *widgetData(QWidget *widget) {
367 // check for the most common case (no sub-widgets with own HWNDs)
368 if (widget == that->window())
369 return &data;
370 if (!subWidgets || !subWidgets->contains(widget))
371 return 0;
372 return &(*subWidgets)[widget];
373 }
374
375 bool eventFilter(QObject *obj, QEvent *event);
376 bool pmEventFilter(QMSG *msg, MRESULT *result);
377};
378
379void QPMDiveWindowSurfacePrivate::addWidget(QWidget *widget)
380{
381 Q_ASSERT(widget->internalWinId());
382
383 WidgetData *wd = &data;
384 if (widget != that->window()) {
385 // lazily create the sub-widget map (only when really necessary)
386 if (!subWidgets)
387 subWidgets = new QMap<QWidget *, WidgetData>();
388 wd = &(*subWidgets)[widget];
389 }
390
391 wd->vrnDirty = true;
392 wd->widgetHeight = 0;
393 wd->rclCount = 0;
394
395 // receive visible region change messages and other PM messages
396 widget->addPmEventFilter(this);
397 WinSetVisibleRegionNotify(widget->internalWinId(), TRUE);
398
399 if (widget != that->window()) {
400 // receive reparent messages from children to cleanup the map
401 widget->installEventFilter(this);
402 }
403}
404
405void QPMDiveWindowSurfacePrivate::removeWidget(QWidget *widget)
406{
407 if (widget != that->window()) {
408 widget->removeEventFilter(this);
409 subWidgets->remove(widget);
410 }
411
412 WinSetVisibleRegionNotify(widget->internalWinId(), FALSE);
413 widget->removePmEventFilter(this);
414}
415
416bool QPMDiveWindowSurfacePrivate::eventFilter(QObject *obj, QEvent *event)
417{
418 QWidget *widget = qobject_cast<QWidget *>(obj);
419 if (event->type() == QEvent::ParentAboutToChange ||
420 event->type() == QEvent::Destroy) {
421 removeWidget(widget);
422 }
423
424 return false;
425}
426
427bool QPMDiveWindowSurfacePrivate::pmEventFilter(QMSG *msg, MRESULT *result)
428{
429 switch (msg->msg) {
430 case WM_VRNDISABLED: {
431 if (msg->hwnd == that->window()->internalWinId()) {
432 if (!useFB)
433 DiveSetupBlitter(hDive, NULL);
434 data.vrnDisabled = true;
435 } else {
436 WidgetData *wd = widgetData(QWidget::find(msg->hwnd));
437 Q_ASSERT(wd);
438 if (wd)
439 wd->vrnDisabled = true;
440 }
441 *result = 0;
442 return true;
443 }
444 case DM_DRAGOVER: {
445 inDrag = true;
446 break;
447 }
448 case DM_DRAGLEAVE:
449 case DM_DROP: {
450 inDrag = false;
451 break;
452 }
453 case WM_VRNENABLED: {
454 QWidget *widget = msg->hwnd == that->window()->internalWinId() ?
455 that->window() : QWidget::find(msg->hwnd);
456 WidgetData *wd = widgetData(widget);
457 Q_ASSERT(wd);
458 wd->vrnDisabled = false;
459 // Note that when an overlapping window of *other* process is moved
460 // over this window, PM still sends WM_VRNENABLED to it but with
461 // ffVisRgnChanged set to FALSE although the visible region *does*
462 // actually change (it doesn't seem to be the case for windows of
463 // the same process). The solution is to ignore this flag and always
464 // assume that the visible region was changed when we get this msg
465#if 0
466 if (LONGFROMMP(msg->mp1)) // window's visible region changed
467#endif
468 wd->vrnDirty = true;
469
470 if (widget == that->window())
471 posDirty = true;
472
473 // process pending flush events for this widget
474 for (QList<QPMDiveWindowSurfacePrivate::FlushArgs>::iterator
475 it = pending.begin(); it != pending.end();) {
476 if (it->widget == widget) {
477 that->doFlush(it->widget, it->from, it->to);
478 it = pending.erase(it);
479 } else {
480 ++it;
481 }
482 }
483
484 *result = 0;
485 return true;
486 }
487 default:
488 break;
489 }
490
491 return false;
492}
493
494QPMDiveWindowSurface::QPMDiveWindowSurface(QWidget* widget)
495 : QWindowSurface(widget), d(new QPMDiveWindowSurfacePrivate)
496{
497 d->that = this;
498 d->image = 0;
499 d->hDive = NULLHANDLE;
500 d->useFB = false;
501 d->bufNum = 0;
502 d->posDirty = true;
503 d->inDrag = false;
504 d->subWidgets = 0;
505
506 memset(&d->setup, 0, sizeof(SETUP_BLITTER));
507
508 // note that fccSrcColorFormat overrides the value specified during the
509 // source buffer creation, so they must match
510 Q_ASSERT(diveBestBufFormat != 0);
511 d->setup.fccSrcColorFormat = diveBestBufFormat;
512 d->setup.fccDstColorFormat = FOURCC_SCRN;
513
514 // add self to the map of participating widgets
515 d->addWidget(window());
516
517 setStaticContentsSupport(true);
518
519 DEBUG(() << "QPMDiveWindowSurface:" << widget);
520}
521
522QPMDiveWindowSurface::~QPMDiveWindowSurface()
523{
524 if (d->subWidgets) {
525 QList<QWidget *> keys = d->subWidgets->keys();
526 foreach(QWidget *w, keys)
527 d->removeWidget(w);
528 Q_ASSERT(d->subWidgets->count() == 0);
529 delete d->subWidgets;
530 }
531
532 d->removeWidget(window());
533
534 if (d->bufNum)
535 DiveFreeImageBuffer(d->hDive, d->bufNum);
536 if (d->hDive != NULLHANDLE)
537 DiveClose(d->hDive);
538 if (d->image)
539 delete d->image;
540}
541
542QPaintDevice *QPMDiveWindowSurface::paintDevice()
543{
544 return d->image;
545}
546
547void QPMDiveWindowSurface::flush(QWidget *widget, const QRegion &rgn,
548 const QPoint &offset)
549{
550 // Not ready for painting yet, bail out. This can happen in
551 // QWidget::create_sys()
552 if (!d->image || rgn.rectCount() == 0)
553 return;
554
555 // make sure the widget is known to us
556 if (!d->widgetData(widget))
557 d->addWidget(widget);
558
559 QRect br = rgn.boundingRect();
560
561 br.translate(offset);
562
563 if (d->inDrag || QDragManager::self()->isInOwnDrag()) {
564 // In drag, DIVE seems to not synchronize well with the mouse buffer
565 // that preserves the contents under the mouse pointer and the drag
566 // image while dragging (even when we use DiveBlitImage()). As a result,
567 // we will get some areas unpainted under if painting is done during
568 // drag. Use the WinGetPS()/GpiDrawBits() way to fix it. The below code
569 // is merely a copy of QT_BITMAP_MIRROR == 3 from qwindowsurface_raster.cpp
570
571 HPS wps = widget->getPS();
572
573 MATRIXLF m;
574 m.fxM11 = MAKEFIXED(1, 0);
575 m.fxM12 = 0;
576 m.lM13 = 0;
577 m.fxM21 = 0;
578 m.fxM22 = MAKEFIXED(-1, 0);
579 m.lM23 = 0;
580 m.lM31 = 0;
581 m.lM32 = widget->height() - 1;
582 GpiSetDefaultViewMatrix(wps, 8, &m, TRANSFORM_REPLACE);
583
584 // make sure br doesn't exceed the backing storage size (it may happen
585 // during resize & move due to the different event order)
586 br = br.intersected(QRect(0, 0, d->image->width(), d->image->height()));
587
588 QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
589 // note that we remove offset from wbr because the widget's HPS has a proper
590 // origin already that includes this offset (which is in fact a position of
591 // the widget relative to its top-level parent)
592 QRect wbr = br.translated(-offset - wOffset);
593
594 BITMAPINFOHEADER2 bmh;
595 memset(&bmh, 0, sizeof(BITMAPINFOHEADER2));
596 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
597 bmh.cPlanes = 1;
598 bmh.cBitCount = d->image->depth();
599 bmh.cx = d->image->width();
600 bmh.cy = d->image->height();
601
602#ifdef QT_PM_NATIVEWIDGETMASK
603 int wh = widget->height();
604
605 // produce a clip region that excludes all descending obstacles
606 // (like child widgets with real HWNDs which are not excluded by Qt)
607 HWND wwnd = widget->internalWinId();
608 RECTL wrcl = { wbr.left(), wh - wbr.bottom() - 1,
609 wbr.right() + 1, wh - wbr.top() };
610 HRGN wrgn = GpiCreateRegion(wps, 1L, &wrcl);
611 ULONG rc = qt_WinProcessWindowObstacles(wwnd, NULL, wrgn, CRGN_DIFF,
612 PWO_Children);
613 if (rc == RGN_RECT || rc == RGN_COMPLEX) {
614 // set the clip region
615 HRGN oldRgn;
616 GpiSetClipRegion(wps, wrgn, &oldRgn);
617 wrgn = oldRgn;
618#endif
619 // Note: target is inclusive-inclusive, source is inclusive-exclusive
620 POINTL ptls[] = { { wbr.left(), wbr.top() },
621 { wbr.right(), wbr.bottom() },
622 { br.left(), br.top() },
623 { br.right() + 1, br.bottom() + 1 } };
624 GpiDrawBits(wps, (PVOID) const_cast<const QImage *>(d->image)->bits(),
625 (PBITMAPINFO2) &bmh, 4, ptls, ROP_SRCCOPY, BBO_IGNORE);
626
627#ifdef QT_PM_NATIVEWIDGETMASK
628 }
629
630 if (wrgn != NULLHANDLE)
631 GpiDestroyRegion(wps, wrgn);
632#endif
633
634 widget->releasePS(wps);
635
636 return;
637 }
638
639 QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
640 // note that we leave offset in wbr since in DIVE mode the origin of the
641 // blit target is always the top level window so we need to properly offset
642 // the target position if we are flushing its child widget
643 QRect wbr = br.translated(-wOffset);
644
645 QPMDiveWindowSurfacePrivate::WidgetData *wd = d->widgetData(widget);
646 Q_ASSERT(wd);
647
648 if (wd->vrnDisabled) {
649 // defer the flush
650 QPMDiveWindowSurfacePrivate::FlushArgs args = { widget,
651 br, wbr.topLeft() };
652 d->pending.append(args);
653 return;
654 }
655
656 doFlush(widget, br, wbr.topLeft());
657}
658
659bool QPMDiveWindowSurface::adjustSetup(QWidget *widget)
660{
661 DEBUG(() << "QPMDiveWindowSurface::adjustSetup:" << window() << widget);
662
663 HWND hwnd = window()->internalWinId();
664
665 // don't include lScreenPosX and the rest by default
666 d->setup.ulStructLen = offsetof(SETUP_BLITTER, lScreenPosX);
667 bool setupDirty = false;
668
669 QPMDiveWindowSurfacePrivate::WidgetData *wd = d->widgetData(widget);
670 Q_ASSERT(wd);
671
672 if (d->posDirty || wd->vrnDirty) {
673 setupDirty = true;
674 d->posDirty = false;
675 // the main widget moved, adjust the target poition
676 POINTL ptl = { 0, 0 };
677 WinMapWindowPoints(hwnd, HWND_DESKTOP, &ptl, 1);
678 d->setup.lScreenPosX = ptl.x;
679 d->setup.lScreenPosY = ptl.y;
680 d->setup.ulStructLen = offsetof(SETUP_BLITTER, ulNumDstRects);
681
682 DEBUG(() << "QPMDiveWindowSurface::adjustSetup:" << "posDirty"
683 << ptl.x << ptl.y);
684 }
685
686 bool wasVrnDirty = wd->vrnDirty;
687
688 if (wd->vrnDirty) {
689 setupDirty = true;
690 wd->vrnDirty = false;
691
692 HPS hps = widget->getPS();
693 HRGN hrgn = GpiCreateRegion(hps, 0L, NULL);
694
695 HWND hwndWidget = widget->internalWinId();
696 ULONG rc = WinQueryVisibleRegion(hwndWidget, hrgn);
697#ifdef QT_PM_NATIVEWIDGETMASK
698 if (rc != RGN_ERROR) {
699 // substract children from the visible region, if any
700 rc = qt_WinProcessWindowObstacles(hwndWidget, NULL, hrgn,
701 CRGN_DIFF, PWO_Children);
702#endif
703 if (rc != RGN_ERROR && hwnd != hwndWidget) {
704 // translate coords to the main widget's coordinate space
705 POINTL ptlOffset = { 0, 0 };
706 WinMapWindowPoints(hwnd, hwndWidget, &ptlOffset, 1);
707 ptlOffset.x = -ptlOffset.x;
708 ptlOffset.y = -ptlOffset.y;
709 GpiOffsetRegion(hps, hrgn, &ptlOffset);
710 }
711#ifdef QT_PM_NATIVEWIDGETMASK
712 }
713#endif
714
715 if (rc == RGN_RECT || rc == RGN_COMPLEX) {
716 RGNRECT rgnCtl;
717 rgnCtl.ircStart = 1;
718 rgnCtl.crc = rgnCtl.crcReturned = 0;
719 rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
720 if (GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, NULL) &&
721 rgnCtl.crcReturned) {
722 rgnCtl.ircStart = 1;
723 rgnCtl.crc = rgnCtl.crcReturned;
724 rgnCtl.ulDirection = RECTDIR_LFRT_TOPBOT;
725 if (wd->rcls.size() < (int)rgnCtl.crc)
726 wd->rcls.resize((int)rgnCtl.crc);
727 wd->rclCount = rgnCtl.crc;
728 GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, wd->rcls.data());
729 }
730 } else if (rc == RGN_NULL) {
731 wd->rclCount = 0;
732 }
733
734 GpiDestroyRegion(hps, hrgn);
735 widget->releasePS(hps);
736
737 // memorize the window height used for the additional visible region
738 // validation in doFlush()
739 wd->widgetHeight = widget->height();
740
741#if defined(QDIVE_DEBUG)
742 DEBUG(() << "QPMDiveWindowSurface::adjustSetup:" << "vrnDirty");
743 for (size_t i = 0; i < wd->rclCount; ++i)
744 DEBUG(() << " " << i << ":" << wd->rcls[i]);
745#endif
746 }
747
748 // make sure setup points to the correct visible rectangle array (note that
749 // we switch it even vrnDirty is false since the widget may change)
750 if (wasVrnDirty || d->setup.ulNumDstRects != wd->rclCount ||
751 (wd->rclCount && d->setup.pVisDstRects != wd->rcls.data()) ||
752 (!wd->rclCount && d->setup.pVisDstRects != NULL)) {
753 setupDirty = true;
754 d->setup.ulNumDstRects = wd->rclCount;
755 d->setup.pVisDstRects = wd->rclCount ? wd->rcls.data() : NULL;
756 d->setup.ulStructLen = sizeof(SETUP_BLITTER);
757 }
758
759 return setupDirty;
760}
761
762void QPMDiveWindowSurface::doFlush(QWidget *widget, const QRect &from, const QPoint &to)
763{
764 DEBUG(() << "QPMDiveWindowSurface::doFlush:" << window() << widget
765 << "from" << from << "to" << to);
766
767 // make sure from doesn't exceed the backing storage size (it may happen
768 // during resize & move due to the different event order)
769 QRect src = from.intersected(QRect(0, 0, d->image->width(), d->image->height()));
770 QPoint dst = to + (src.topLeft() - from.topLeft());
771
772 bool setupDirty = adjustSetup(widget);
773
774 // note that the source image is expected to be top-left oriented
775 // by DiveSetupBlitter() so we don't perform y coordinate flip
776
777 // flip destination y coordinate
778 dst.setY(window()->height() - dst.y() - src.height());
779
780 SETUP_BLITTER setupTmp = d->setup;
781 setupTmp.ulSrcWidth = src.width();
782 setupTmp.ulSrcHeight = src.height();
783 setupTmp.ulSrcPosX = src.left();
784 setupTmp.ulSrcPosY = src.top();
785 setupTmp.ulDstWidth = setupTmp.ulSrcWidth;
786 setupTmp.ulDstHeight = setupTmp.ulSrcHeight;
787 setupTmp.lDstPosX = dst.x();
788 setupTmp.lDstPosY = dst.y();
789
790 if (memcmp(&d->setup, &setupTmp, d->setup.ulStructLen)) {
791 setupDirty = true;
792 memcpy(&d->setup, &setupTmp, d->setup.ulStructLen);
793 }
794
795 if (setupDirty) {
796 DiveSetupBlitter(d->hDive, &d->setup);
797 }
798
799 DiveBlitImage(d->hDive, d->bufNum, DIVE_BUFFER_SCREEN);
800}
801
802void QPMDiveWindowSurface::setGeometry(const QRect &rect)
803{
804 // this method is mostly like QRasterWindowSurface::prepareBuffer()
805
806 QWindowSurface::setGeometry(rect);
807
808 if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) {
809 int width = window()->width();
810 int height = window()->height();
811 if (d->image) {
812 width = qMax(d->image->width(), width);
813 height = qMax(d->image->height(), height);
814 }
815
816 if (width == 0 || height == 0) {
817 delete d->image;
818 d->image = 0;
819 return;
820 }
821
822 QImage *oldImage = d->image;
823
824 QImage::Format format = fourccToFormat(d->setup.fccSrcColorFormat);
825 Q_ASSERT(format != QImage::Format_Invalid);
826 d->image = new QImage(width, height, format);
827
828 if (!d->useFB) {
829 // associate the image data pointer with the buffer number
830 if (d->bufNum)
831 DiveFreeImageBuffer(d->hDive, d->bufNum);
832 d->bufNum = 0;
833 ULONG rc = DiveAllocImageBuffer(d->hDive, &d->bufNum,
834 d->setup.fccSrcColorFormat,
835 width, height,
836 d->image->bytesPerLine(),
837 (PBYTE)const_cast<const QImage *>(d->image)->bits());
838 if (rc != DIVE_SUCCESS) {
839 qWarning("QPMDiveWindowSurface::setGeometry: DiveAllocImageBuffer "
840 "returned 0x%08lX", rc);
841 delete d->image;
842 delete oldImage;
843 return;
844 }
845 }
846
847 if (oldImage && hasStaticContents()) {
848 // Make sure we use the const version of bits() (no detach).
849 const uchar *src = const_cast<const QImage *>(oldImage)->bits();
850 uchar *dst = d->image->bits();
851
852 const int srcBytesPerLine = oldImage->bytesPerLine();
853 const int dstBytesPerLine = d->image->bytesPerLine();
854 const int bytesPerPixel = oldImage->depth() >> 3;
855
856 QRegion staticRegion(staticContents());
857 // Make sure we're inside the boundaries of the old image.
858 staticRegion &= QRect(0, 0, oldImage->width(), oldImage->height());
859 const QVector<QRect> &rects = staticRegion.rects();
860 const QRect *srcRect = rects.constData();
861
862 // Copy the static content of the old image into the new one.
863 int numRectsLeft = rects.size();
864 do {
865 const int bytesOffset = srcRect->x() * bytesPerPixel;
866 const int dy = srcRect->y();
867
868 // Adjust src and dst to point to the right offset.
869 const uchar *s = src + dy * srcBytesPerLine + bytesOffset;
870 uchar *d = dst + dy * dstBytesPerLine + bytesOffset;
871 const int numBytes = srcRect->width() * bytesPerPixel;
872
873 int numScanLinesLeft = srcRect->height();
874 do {
875 ::memcpy(d, s, numBytes);
876 d += dstBytesPerLine;
877 s += srcBytesPerLine;
878 } while (--numScanLinesLeft);
879
880 ++srcRect;
881 } while (--numRectsLeft);
882 }
883
884 delete oldImage;
885 }
886}
887
888// from qwindowsurface.cpp
889extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
890
891bool QPMDiveWindowSurface::scroll(const QRegion &area, int dx, int dy)
892{
893 if (!d->image || d->image->isNull())
894 return false;
895
896 const QVector<QRect> rects = area.rects();
897 for (int i = 0; i < rects.size(); ++i)
898 qt_scrollRectInImage(*d->image, rects.at(i), QPoint(dx, dy));
899
900 return true;
901}
902
903// static
904QPMDiveWindowSurface *QPMDiveWindowSurface::create(QWidget *widget)
905{
906 if (!diveDllResolved) {
907 diveDllResolved = true;
908 diveDllOK = qgetenv("QT_PM_NO_DIVE").isEmpty();
909 if (diveDllOK) {
910 QByteArray diveEnv = qgetenv("QT_PM_DIVE").trimmed().toUpper();
911 if (diveEnv == "BLIT") {
912 // use DiveBlitImage()
913 diveUseFB = false;
914 } else if (diveEnv == "FB") {
915 // use direct framebuffer access
916 diveUseFB = true;
917 } else if (diveEnv == "FBSWM") {
918 // use direct framebuffer access, hide the mouse pointer
919 diveUseFB = true;
920 diveHideMouse = true;
921 } else {
922 // dedect the Panorama video driver presence
923 HMODULE hmod;
924 bool isPanorama =
925 DosQueryModuleHandle("VBE2GRAD", &hmod) == NO_ERROR &&
926 DosQueryModuleHandle("PANOGREX", &hmod) == NO_ERROR;
927 if (isPanorama) {
928 // if Panorama is detected, disable DIVE by default due to
929 // bugs in the DIVE implementation
930 diveDllOK = false;
931 } else {
932 // FBSWM is a safe default for SNAP and probably other drivers
933 diveUseFB = true;
934 diveHideMouse = true;
935 }
936 }
937 }
938
939 if (diveDllOK) {
940 // resolve Dive functions
941 for (size_t i = 0; i < sizeof(diveDllFuncs) / sizeof(diveDllFuncs[0]); ++i) {
942 *diveDllFuncs[i].entry = diveDll.resolve(diveDllFuncs[i].name);
943 if (!*diveDllFuncs[i].entry) {
944 diveDllOK = false;
945 break;
946 }
947 }
948
949 if (diveDllOK) {
950 diveCaps.ulStructLen = sizeof(diveCaps);
951 DiveQueryCaps(&diveCaps, DIVE_BUFFER_SCREEN);
952
953 DEBUG_VAR(diveCaps.fScreenDirect);
954 DEBUG_VAR(diveCaps.fBankSwitched);
955 DEBUG_VAR(diveCaps.ulDepth);
956 DEBUG_VAR(diveCaps.ulHorizontalResolution);
957 DEBUG_VAR(diveCaps.ulVerticalResolution);
958 DEBUG_VAR(diveCaps.ulScanLineBytes);
959 DEBUG(() << "diveCaps.fccColorEncoding"
960 << ((char*)&diveCaps.fccColorEncoding)[0]
961 << ((char*)&diveCaps.fccColorEncoding)[1]
962 << ((char*)&diveCaps.fccColorEncoding)[2]
963 << ((char*)&diveCaps.fccColorEncoding)[3]);
964
965 diveBestBufFormat = fourccScreenToBuffer(diveCaps.fccColorEncoding,
966 diveUseFB);
967 DEBUG(() << "diveBestBufFormat"
968 << ((char*)&diveBestBufFormat)[0]
969 << ((char*)&diveBestBufFormat)[1]
970 << ((char*)&diveBestBufFormat)[2]
971 << ((char*)&diveBestBufFormat)[3]);
972
973 if (diveUseFB) {
974 if (!diveCaps.fScreenDirect || diveCaps.fBankSwitched) {
975 // direct framebuffer is not supported by the driver
976 // (and switching banks is not supported by our code)
977 diveUseFB = false;
978 } else {
979 if (diveBestBufFormat == diveCaps.fccColorEncoding) {
980 // no color conversion is required
981 } else if (diveBestBufFormat == FOURCC_BGR4) {
982 // build the color conversion table
983 switch (diveCaps.fccColorEncoding) {
984#if 0
985 // FOURCC_R565/FOURCC_R555 should be handled directly
986 case FOURCC_R565: {
987 for (ULONG u = 0; u < 256; ++u) {
988 diveColorMap[0][u] = (u >> 3) << 0;
989 diveColorMap[1][u] = (u >> 2) << 5;
990 diveColorMap[2][u] = (u >> 3) << 11;
991 }
992 break;
993 }
994 case FOURCC_R555: {
995 for (ULONG u = 0; u < 256; ++u) {
996 diveColorMap[0][u] = (u >> 3) << 0;
997 diveColorMap[1][u] = (u >> 3) << 5;
998 diveColorMap[2][u] = (u >> 3) << 10;
999 }
1000 break;
1001 }
1002#endif
1003 case FOURCC_R664: {
1004 for (ULONG u = 0; u < 256; ++u) {
1005 diveColorMap[0][u] = (u >> 2) << 0;
1006 diveColorMap[1][u] = (u >> 2) << 6;
1007 diveColorMap[2][u] = (u >> 4) << 12;
1008 }
1009 break;
1010 }
1011#if 0
1012 // FOURCC_BGR4 should be handled directly
1013 case FOURCC_BGR4:
1014#endif
1015 case FOURCC_RGB4:
1016 case FOURCC_BGR3:
1017 case FOURCC_RGB3:
1018 break;
1019 default:
1020 // screen pixel format is not supported
1021 diveUseFB = false;
1022 break;
1023 }
1024 } else {
1025 Q_ASSERT(false);
1026 diveUseFB = false;
1027 }
1028 }
1029
1030 if (!diveUseFB) {
1031 // we disabled FB, recalculate the best format
1032 diveBestBufFormat = fourccScreenToBuffer(diveCaps.fccColorEncoding,
1033 diveUseFB);
1034 DEBUG(() << "diveBestBufFormat (final)"
1035 << ((char*)&diveBestBufFormat)[0]
1036 << ((char*)&diveBestBufFormat)[1]
1037 << ((char*)&diveBestBufFormat)[2]
1038 << ((char*)&diveBestBufFormat)[3]);
1039 }
1040 }
1041
1042 if (!diveUseFB) {
1043 if (diveBestBufFormat == 0) {
1044 // there is no working pixel format for the buffer for
1045 // DiveBlitImage() to work correctly with the current screen
1046 // format, give up
1047 diveDllOK = false;
1048 }
1049 }
1050
1051 mousePtrSize = WinQuerySysValue(HWND_DESKTOP, SV_CXPOINTER);
1052 }
1053 }
1054
1055 DEBUG_VAR(diveDllOK);
1056 DEBUG_VAR(diveUseFB);
1057 DEBUG_VAR(diveHideMouse);
1058 }
1059
1060 // Note: returning 0 from this method will cause using QRasterWindowSurface
1061 // as a fallback
1062
1063 if (!diveDllOK)
1064 return 0;
1065
1066 HDIVE hDive = NULLHANDLE;
1067 ULONG rc = DIVE_SUCCESS;
1068
1069 if (diveUseFB) {
1070 // we use a shared DIVE instance for all widgets
1071 if (diveHandle == NULLHANDLE)
1072 rc = DiveOpen(&diveHandle, FALSE, &diveFrameBuf);
1073 hDive = diveHandle;
1074 } else {
1075 // we need a new DIVE instance to reduce the number of calls to
1076 // DiveSetupBlitter() (as recommended by MMAPG)
1077 rc = DiveOpen(&hDive, FALSE, 0);
1078 }
1079
1080 if (rc != DIVE_SUCCESS) {
1081 qWarning("QPMDiveWindowSurface::create: DiveOpen returned 0x%08lX", rc);
1082 return 0;
1083 }
1084
1085 QPMDiveWindowSurface *surface = diveUseFB ?
1086 new QPMDiveWindowSurfaceFB(widget) : new QPMDiveWindowSurface(widget);
1087
1088 if (surface)
1089 surface->d->hDive = hDive;
1090 else if (!diveUseFB)
1091 DiveClose(hDive);
1092
1093 return surface;
1094}
1095
1096////////////////////////////////////////////////////////////////////////////////
1097
1098QPMDiveWindowSurfaceFB::QPMDiveWindowSurfaceFB(QWidget* widget)
1099 : QPMDiveWindowSurface(widget)
1100{
1101 d->useFB = true;
1102}
1103
1104QPMDiveWindowSurfaceFB::~QPMDiveWindowSurfaceFB()
1105{
1106 // prevent the shared DIVE handle from closing
1107 d->hDive = NULLHANDLE;
1108}
1109
1110void QPMDiveWindowSurfaceFB::doFlush(QWidget *widget, const QRect &from, const QPoint &to)
1111{
1112 DEBUG(() << "QPMDiveWindowSurfaceFB::doFlush:" << window() << widget
1113 << "from" << from << "to" << to);
1114
1115 // make sure from doesn't exceed the backing storage size (it may happen
1116 // during resize & move due to the different event order)
1117 QRect src = from.intersected(QRect(0, 0, d->image->width(), d->image->height()));
1118
1119 // convert the "to" coordinate to the delta