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

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

gui: Minor cleanup.

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