source: trunk/src/gui/painting/qbackingstore.cpp@ 463

Last change on this file since 463 was 2, checked in by Dmitry A. Kuminov, 17 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 52.3 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qbackingstore_p.h"
43
44#include <QtCore/qglobal.h>
45#include <QtCore/qdebug.h>
46#include <QtCore/qvarlengtharray.h>
47#include <QtGui/qevent.h>
48#include <QtGui/qapplication.h>
49#include <QtGui/qpaintengine.h>
50#include <QtGui/qgraphicsproxywidget.h>
51
52#include <private/qwidget_p.h>
53#include <private/qwindowsurface_raster_p.h>
54#include <private/qapplication_p.h>
55#include <private/qpaintengine_raster_p.h>
56
57#include "qgraphicssystem_p.h"
58
59#ifdef Q_WS_QWS
60#include <QtGui/qwsmanager_qws.h>
61#include <private/qwsmanager_p.h>
62#endif
63
64QT_BEGIN_NAMESPACE
65
66extern QRegion qt_dirtyRegion(QWidget *);
67
68/*
69 A version of QRect::intersects() that does not normalize the rects.
70*/
71static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
72{
73 return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right())
74 && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom()));
75}
76
77/**
78 * Flushes the contents of the \a windowSurface into the screen area of \a widget.
79 * \a tlwOffset is the position of the top level widget relative to the window surface.
80 * \a region is the region to be updated in \a widget coordinates.
81 */
82static inline void qt_flush(QWidget *widget, const QRegion &region, QWindowSurface *windowSurface,
83 QWidget *tlw, const QPoint &tlwOffset)
84{
85 Q_ASSERT(widget);
86 Q_ASSERT(!region.isEmpty());
87 Q_ASSERT(windowSurface);
88 Q_ASSERT(tlw);
89
90#if !defined(QT_NO_PAINT_DEBUG) && !defined(Q_WS_QWS)
91 // QWS does flush update in QWindowSurface::flush (because it needs to lock the surface etc).
92 static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt();
93 if (flushUpdate > 0)
94 QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
95#endif
96
97 if (widget != tlw)
98 windowSurface->flush(widget, region, tlwOffset + widget->mapTo(tlw, QPoint()));
99 else
100 windowSurface->flush(widget, region, tlwOffset);
101}
102
103#ifndef QT_NO_PAINT_DEBUG
104#ifdef Q_WS_WIN
105static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec)
106{
107 HBRUSH brush;
108 static int i = 0;
109 switch (i) {
110 case 0:
111 brush = CreateSolidBrush(RGB(255, 255, 0));
112 break;
113 case 1:
114 brush = CreateSolidBrush(RGB(255, 200, 55));
115 break;
116 case 2:
117 brush = CreateSolidBrush(RGB(200, 255, 55));
118 break;
119 case 3:
120 brush = CreateSolidBrush(RGB(200, 200, 0));
121 break;
122 }
123 i = (i + 1) & 3;
124
125 HDC hdc = widget->getDC();
126
127 const QVector<QRect> &rects = region.rects();
128 foreach (QRect rect, rects) {
129 RECT winRect;
130 SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
131 FillRect(hdc, &winRect, brush);
132 }
133
134 widget->releaseDC(hdc);
135 ::Sleep(msec);
136}
137#endif
138
139void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
140{
141#ifdef Q_WS_QWS
142 Q_UNUSED(widget);
143 Q_UNUSED(unclipped);
144 static QWSYellowSurface surface(true);
145 surface.setDelay(msec);
146 surface.flush(widget, toBePainted, QPoint());
147#else
148 QRegion paintRegion = toBePainted;
149 QRect widgetRect = widget->rect();
150
151 if (!widget->internalWinId()) {
152 QWidget *nativeParent = widget->nativeParentWidget();
153 const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
154 paintRegion.translate(offset);
155 widgetRect.translate(offset);
156 widget = nativeParent;
157 }
158
159#ifdef Q_WS_WIN
160 Q_UNUSED(unclipped);
161 showYellowThing_win(widget, paintRegion, msec);
162#else
163 //flags to fool painter
164 bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
165 if (unclipped && !widget->d_func()->paintOnScreen())
166 widget->setAttribute(Qt::WA_PaintUnclipped);
167
168 const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
169 if (setFlag)
170 widget->setAttribute(Qt::WA_WState_InPaintEvent);
171
172 //setup the engine
173 QPaintEngine *pe = widget->paintEngine();
174 if (pe) {
175 pe->setSystemClip(paintRegion);
176 {
177 QPainter p(widget);
178 p.setClipRegion(paintRegion);
179 static int i = 0;
180 switch (i) {
181 case 0:
182 p.fillRect(widgetRect, QColor(255,255,0));
183 break;
184 case 1:
185 p.fillRect(widgetRect, QColor(255,200,55));
186 break;
187 case 2:
188 p.fillRect(widgetRect, QColor(200,255,55));
189 break;
190 case 3:
191 p.fillRect(widgetRect, QColor(200,200,0));
192 break;
193 }
194 i = (i+1) & 3;
195 p.end();
196 }
197 }
198
199 if (setFlag)
200 widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
201
202 //restore
203 widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
204
205 if (pe)
206 pe->setSystemClip(QRegion());
207
208 QApplication::syncX();
209
210#if defined(Q_OS_UNIX)
211 ::usleep(1000 * msec);
212#endif
213#endif // Q_WS_WIN
214#endif // Q_WS_QWS
215}
216
217bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
218{
219 if (!widget)
220 return false;
221
222 int delay = 0;
223 if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
224 static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt();
225 if (!flushPaintEvent)
226 return false;
227 delay = flushPaintEvent;
228 } else {
229 static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt();
230 if (!flushPaint)
231 return false;
232 delay = flushPaint;
233 }
234
235 QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
236 return true;
237}
238
239void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
240{
241 if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
242 return;
243
244 QWidget *tlw = widget->window();
245 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
246 if (!tlwExtra)
247 return;
248
249 const QPoint offset = widget->mapTo(tlw, QPoint());
250 qt_flush(widget, rgn, tlwExtra->backingStore->windowSurface, tlw, offset);
251}
252#endif // QT_NO_PAINT_DEBUG
253
254/*
255 Moves the whole rect by (dx, dy) in widget's coordinate system.
256 Doesn't generate any updates.
257*/
258bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
259{
260 const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft()));
261 return windowSurface->scroll(QRect(pos, rect.size()), dx, dy);
262}
263
264void QWidgetBackingStore::releaseBuffer()
265{
266 if (windowSurface)
267 windowSurface->setGeometry(QRect());
268#ifdef Q_BACKINGSTORE_SUBSURFACES
269 for (int i = 0; i < subSurfaces.size(); ++i)
270 subSurfaces.at(i)->setGeometry(QRect());
271#endif
272}
273
274/*!
275 Prepares the window surface to paint a\ toClean region of the \a widget and
276 updates the BeginPaintInfo struct accordingly.
277
278 The \a toClean region might be clipped by the window surface.
279*/
280void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface,
281 BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
282{
283#ifdef Q_WS_QWS
284 QWSWindowSurface *surface = static_cast<QWSWindowSurface *>(windowSurface);
285 QWidget *surfaceWidget = surface->window();
286
287 if (!surface->isValid()) {
288 // this looks strange but it really just releases the surface
289 surface->releaseSurface();
290 // the old window surface is deleted in setWindowSurface, which is
291 // called from QWindowSurface constructor.
292 windowSurface = tlw->d_func()->createDefaultWindowSurface();
293 surface = static_cast<QWSWindowSurface *>(windowSurface);
294 // createDefaultWindowSurface() will set topdata->windowSurface on the
295 // widget to zero. However, if this is a sub-surface, it should point
296 // to the widget's sub windowSurface, so we set that here:
297 if (!surfaceWidget->isWindow())
298 surfaceWidget->d_func()->topData()->windowSurface = windowSurface;
299 surface->setGeometry(topLevelRect());
300 returnInfo->windowSurfaceRecreated = true;
301 }
302
303 const QRegion toCleanUnclipped(toClean);
304
305 if (surfaceWidget->isWindow())
306 tlwOffset = surface->painterOffset();
307#ifdef Q_BACKINGSTORE_SUBSURFACES
308 else if (toCleanIsInTopLevelCoordinates)
309 toClean &= surface->clipRegion().translated(surfaceWidget->mapTo(tlw, QPoint()));
310 if (!toCleanIsInTopLevelCoordinates && windowSurface == this->windowSurface)
311 toClean &= surface->clipRegion().translated(-widget->mapTo(surfaceWidget, QPoint()));
312#else
313 toClean &= surface->clipRegion();
314#endif
315
316 if (toClean.isEmpty()) {
317 if (surfaceWidget->isWindow()) {
318 dirtyFromPreviousSync += toCleanUnclipped;
319 hasDirtyFromPreviousSync = true;
320 }
321
322 returnInfo->nothingToPaint = true;
323 // Nothing to repaint. However, we might have newly exposed areas on the
324 // screen, so we have to make sure those are flushed.
325 flush();
326 return;
327 }
328
329 if (surfaceWidget->isWindow()) {
330 if (toCleanUnclipped != toClean) {
331 dirtyFromPreviousSync += (toCleanUnclipped - surface->clipRegion());
332 hasDirtyFromPreviousSync = true;
333 }
334 if (hasDirtyFromPreviousSync) {
335 dirtyFromPreviousSync -= toClean;
336 hasDirtyFromPreviousSync = !dirtyFromPreviousSync.isEmpty();
337 }
338 }
339
340#endif // Q_WS_QWS
341
342 Q_UNUSED(widget);
343 Q_UNUSED(toCleanIsInTopLevelCoordinates);
344
345 // Always flush repainted areas.
346 dirtyOnScreen += toClean;
347
348#ifdef QT_NO_PAINT_DEBUG
349 windowSurface->beginPaint(toClean);
350#else
351 returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
352 // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
353 // the BackingStore lock, so if we hold that, the server will
354 // never release the Communication lock that we are waiting for in
355 // sendSynchronousCommand
356 if (!returnInfo->wasFlushed)
357 windowSurface->beginPaint(toClean);
358#endif
359
360 Q_UNUSED(returnInfo);
361}
362
363void QWidgetBackingStore::endPaint(const QRegion &cleaned, QWindowSurface *windowSurface,
364 BeginPaintInfo *beginPaintInfo)
365{
366#ifndef QT_NO_PAINT_DEBUG
367 if (!beginPaintInfo->wasFlushed)
368 windowSurface->endPaint(cleaned);
369 else
370 QWidgetBackingStore::unflushPaint(tlw, cleaned);
371#else
372 Q_UNUSED(beginPaintInfo);
373 windowSurface->endPaint(cleaned);
374#endif
375
376#ifdef Q_BACKINGSTORE_SUBSURFACES
377 flush(static_cast<QWSWindowSurface *>(windowSurface)->window(), windowSurface);
378#else
379 flush();
380#endif
381}
382
383/*!
384 Returns the region (in top-level coordinates) that needs repaint and/or flush.
385
386 If the widget is non-zero, only the dirty region for the widget is returned
387 and the region will be in widget coordinates.
388*/
389QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
390{
391 const bool widgetDirty = widget && widget != tlw;
392 const QRect tlwRect(topLevelRect());
393 const QRect surfaceGeometry(windowSurface->geometry());
394 if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) {
395 if (widgetDirty) {
396 const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
397 const QPoint offset(widget->mapTo(tlw, QPoint()));
398 const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
399 return dirtyWidgetRect.translated(-offset);
400 }
401 return QRect(QPoint(), tlwRect.size());
402 }
403
404 // Calculate the region that needs repaint.
405 QRegion r(dirty);
406 for (int i = 0; i < dirtyWidgets.size(); ++i) {
407 QWidget *w = dirtyWidgets.at(i);
408 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
409 continue;
410 r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
411 }
412
413 // Append the region that needs flush.
414 r += dirtyOnScreen;
415
416 if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
417 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
418 QWidget *w = dirtyOnScreenWidgets->at(i);
419 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
420 continue;
421 QWidgetPrivate *wd = w->d_func();
422 Q_ASSERT(wd->needsFlush);
423 r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
424 }
425 }
426
427 if (widgetDirty) {
428 // Intersect with the widget geometry and translate to its coordinates.
429 const QPoint offset(widget->mapTo(tlw, QPoint()));
430 r &= widget->rect().translated(offset);
431 r.translate(-offset);
432 }
433 return r;
434}
435
436/*!
437 Returns the static content inside the \a parent if non-zero; otherwise the static content
438 for the entire backing store is returned. The content will be clipped to \a withingClipRect
439 if non-empty.
440*/
441QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
442{
443 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
444 const QRect surfaceGeometry(windowSurface->geometry());
445 QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
446 if (!withinClipRect.isEmpty())
447 surfaceRect &= withinClipRect;
448 return QRegion(surfaceRect);
449 }
450
451 QRegion region;
452 if (parent && parent->d_func()->children.isEmpty())
453 return region;
454
455 const bool clipToRect = !withinClipRect.isEmpty();
456 const int count = staticWidgets.count();
457 for (int i = 0; i < count; ++i) {
458 QWidget *w = staticWidgets.at(i);
459 QWidgetPrivate *wd = w->d_func();
460 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
461 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
462 continue;
463 }
464
465 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
466 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
467 if (clipToRect)
468 rect &= withinClipRect.translated(-offset);
469 if (rect.isEmpty())
470 continue;
471
472 rect &= wd->clipRect();
473 if (rect.isEmpty())
474 continue;
475
476 QRegion visible(rect);
477 wd->clipToEffectiveMask(visible);
478 if (visible.isEmpty())
479 continue;
480 wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
481