1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation (qt-info@nokia.com)
|
---|
6 | **
|
---|
7 | ** This file is part of the QtGui module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Lesser General Public License Usage
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
18 | ** General Public License version 2.1 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
20 | ** packaging of this file. Please review the following information to
|
---|
21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
23 | **
|
---|
24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at qt-info@nokia.com.
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 |
|
---|
43 | #include "qplatformdefs.h"
|
---|
44 |
|
---|
45 | #include "qbackingstore_p.h"
|
---|
46 |
|
---|
47 | #include <QtCore/qglobal.h>
|
---|
48 | #include <QtCore/qdebug.h>
|
---|
49 | #include <QtCore/qvarlengtharray.h>
|
---|
50 | #include <QtGui/qevent.h>
|
---|
51 | #include <QtGui/qapplication.h>
|
---|
52 | #include <QtGui/qpaintengine.h>
|
---|
53 | #include <QtGui/qgraphicsproxywidget.h>
|
---|
54 |
|
---|
55 | #include <private/qwidget_p.h>
|
---|
56 | #include <private/qwindowsurface_raster_p.h>
|
---|
57 | #include <private/qapplication_p.h>
|
---|
58 | #include <private/qpaintengine_raster_p.h>
|
---|
59 | #include <private/qgraphicseffect_p.h>
|
---|
60 |
|
---|
61 | #include "qgraphicssystem_p.h"
|
---|
62 |
|
---|
63 | #ifdef Q_WS_QWS
|
---|
64 | #include <QtGui/qwsmanager_qws.h>
|
---|
65 | #include <private/qwsmanager_p.h>
|
---|
66 | #endif
|
---|
67 |
|
---|
68 | QT_BEGIN_NAMESPACE
|
---|
69 |
|
---|
70 | extern QRegion qt_dirtyRegion(QWidget *);
|
---|
71 |
|
---|
72 | /*
|
---|
73 | A version of QRect::intersects() that does not normalize the rects.
|
---|
74 | */
|
---|
75 | static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
|
---|
76 | {
|
---|
77 | return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right())
|
---|
78 | && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom()));
|
---|
79 | }
|
---|
80 |
|
---|
81 | /**
|
---|
82 | * Flushes the contents of the \a windowSurface into the screen area of \a widget.
|
---|
83 | * \a tlwOffset is the position of the top level widget relative to the window surface.
|
---|
84 | * \a region is the region to be updated in \a widget coordinates.
|
---|
85 | */
|
---|
86 | static inline void qt_flush(QWidget *widget, const QRegion ®ion, QWindowSurface *windowSurface,
|
---|
87 | QWidget *tlw, const QPoint &tlwOffset)
|
---|
88 | {
|
---|
89 | Q_ASSERT(widget);
|
---|
90 | Q_ASSERT(!region.isEmpty());
|
---|
91 | Q_ASSERT(windowSurface);
|
---|
92 | Q_ASSERT(tlw);
|
---|
93 |
|
---|
94 | #if !defined(QT_NO_PAINT_DEBUG) && !defined(Q_WS_QWS)
|
---|
95 | // QWS does flush update in QWindowSurface::flush (because it needs to lock the surface etc).
|
---|
96 | static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt();
|
---|
97 | if (flushUpdate > 0)
|
---|
98 | QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
|
---|
99 | #endif
|
---|
100 |
|
---|
101 | if (widget != tlw)
|
---|
102 | windowSurface->flush(widget, region, tlwOffset + widget->mapTo(tlw, QPoint()));
|
---|
103 | else
|
---|
104 | windowSurface->flush(widget, region, tlwOffset);
|
---|
105 | }
|
---|
106 |
|
---|
107 | #ifndef QT_NO_PAINT_DEBUG
|
---|
108 | #ifdef Q_WS_WIN
|
---|
109 | static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec)
|
---|
110 | {
|
---|
111 | HBRUSH brush;
|
---|
112 | static int i = 0;
|
---|
113 | switch (i) {
|
---|
114 | case 0:
|
---|
115 | brush = CreateSolidBrush(RGB(255, 255, 0));
|
---|
116 | break;
|
---|
117 | case 1:
|
---|
118 | brush = CreateSolidBrush(RGB(255, 200, 55));
|
---|
119 | break;
|
---|
120 | case 2:
|
---|
121 | brush = CreateSolidBrush(RGB(200, 255, 55));
|
---|
122 | break;
|
---|
123 | case 3:
|
---|
124 | brush = CreateSolidBrush(RGB(200, 200, 0));
|
---|
125 | break;
|
---|
126 | }
|
---|
127 | i = (i + 1) & 3;
|
---|
128 |
|
---|
129 | HDC hdc = widget->getDC();
|
---|
130 |
|
---|
131 | const QVector<QRect> &rects = region.rects();
|
---|
132 | foreach (QRect rect, rects) {
|
---|
133 | RECT winRect;
|
---|
134 | SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
|
---|
135 | FillRect(hdc, &winRect, brush);
|
---|
136 | }
|
---|
137 |
|
---|
138 | widget->releaseDC(hdc);
|
---|
139 | ::Sleep(msec);
|
---|
140 | }
|
---|
141 | #endif
|
---|
142 |
|
---|
143 | void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
|
---|
144 | {
|
---|
145 | #ifdef Q_WS_QWS
|
---|
146 | Q_UNUSED(widget);
|
---|
147 | Q_UNUSED(unclipped);
|
---|
148 | static QWSYellowSurface surface(true);
|
---|
149 | surface.setDelay(msec);
|
---|
150 | surface.flush(widget, toBePainted, QPoint());
|
---|
151 | #else
|
---|
152 | QRegion paintRegion = toBePainted;
|
---|
153 | QRect widgetRect = widget->rect();
|
---|
154 |
|
---|
155 | if (!widget->internalWinId()) {
|
---|
156 | QWidget *nativeParent = widget->nativeParentWidget();
|
---|
157 | const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
|
---|
158 | paintRegion.translate(offset);
|
---|
159 | widgetRect.translate(offset);
|
---|
160 | widget = nativeParent;
|
---|
161 | }
|
---|
162 |
|
---|
163 | #ifdef Q_WS_WIN
|
---|
164 | Q_UNUSED(unclipped);
|
---|
165 | showYellowThing_win(widget, paintRegion, msec);
|
---|
166 | #else
|
---|
167 | //flags to fool painter
|
---|
168 | bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
|
---|
169 | if (unclipped && !widget->d_func()->paintOnScreen())
|
---|
170 | widget->setAttribute(Qt::WA_PaintUnclipped);
|
---|
171 |
|
---|
172 | const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
|
---|
173 | if (setFlag)
|
---|
174 | widget->setAttribute(Qt::WA_WState_InPaintEvent);
|
---|
175 |
|
---|
176 | //setup the engine
|
---|
177 | QPaintEngine *pe = widget->paintEngine();
|
---|
178 | if (pe) {
|
---|
179 | pe->setSystemClip(paintRegion);
|
---|
180 | {
|
---|
181 | QPainter p(widget);
|
---|
182 | p.setClipRegion(paintRegion);
|
---|
183 | static int i = 0;
|
---|
184 | switch (i) {
|
---|
185 | case 0:
|
---|
186 | p.fillRect(widgetRect, QColor(255,255,0));
|
---|
187 | break;
|
---|
188 | case 1:
|
---|
189 | p.fillRect(widgetRect, QColor(255,200,55));
|
---|
190 | break;
|
---|
191 | case 2:
|
---|
192 | p.fillRect(widgetRect, QColor(200,255,55));
|
---|
193 | break;
|
---|
194 | case 3:
|
---|
195 | p.fillRect(widgetRect, QColor(200,200,0));
|
---|
196 | break;
|
---|
197 | }
|
---|
198 | i = (i+1) & 3;
|
---|
199 | p.end();
|
---|
200 | }
|
---|
201 | }
|
---|
202 |
|
---|
203 | if (setFlag)
|
---|
204 | widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
|
---|
205 |
|
---|
206 | //restore
|
---|
207 | widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
|
---|
208 |
|
---|
209 | if (pe)
|
---|
210 | pe->setSystemClip(QRegion());
|
---|
211 |
|
---|
212 | QApplication::syncX();
|
---|
213 |
|
---|
214 | #if defined(Q_OS_UNIX)
|
---|
215 | ::usleep(1000 * msec);
|
---|
216 | #endif
|
---|
217 | #endif // Q_WS_WIN
|
---|
218 | #endif // Q_WS_QWS
|
---|
219 | }
|
---|
220 |
|
---|
221 | bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
|
---|
222 | {
|
---|
223 | if (!widget)
|
---|
224 | return false;
|
---|
225 |
|
---|
226 | int delay = 0;
|
---|
227 | if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
|
---|
228 | static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt();
|
---|
229 | if (!flushPaintEvent)
|
---|
230 | return false;
|
---|
231 | delay = flushPaintEvent;
|
---|
232 | } else {
|
---|
233 | static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt();
|
---|
234 | if (!flushPaint)
|
---|
235 | return false;
|
---|
236 | delay = flushPaint;
|
---|
237 | }
|
---|
238 |
|
---|
239 | QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
|
---|
240 | return true;
|
---|
241 | }
|
---|
242 |
|
---|
243 | void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
|
---|
244 | {
|
---|
245 | if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
|
---|
246 | return;
|
---|
247 |
|
---|
248 | QWidget *tlw = widget->window();
|
---|
249 | QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
|
---|
250 | if (!tlwExtra)
|
---|
251 | return;
|
---|
252 |
|
---|
253 | const QPoint offset = widget->mapTo(tlw, QPoint());
|
---|
254 | qt_flush(widget, rgn, tlwExtra->backingStore->windowSurface, tlw, offset);
|
---|
255 | }
|
---|
256 | #endif // QT_NO_PAINT_DEBUG
|
---|
257 |
|
---|
258 | /*
|
---|
259 | Moves the whole rect by (dx, dy) in widget's coordinate system.
|
---|
260 | Doesn't generate any updates.
|
---|
261 | */
|
---|
262 | bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
|
---|
263 | {
|
---|
264 | const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft()));
|
---|
265 | const QRect tlwRect(QRect(pos, rect.size()));
|
---|
266 | if (fullUpdatePending || dirty.intersects(tlwRect))
|
---|
267 | return false; // We don't want to scroll junk.
|
---|
268 | return windowSurface->scroll(tlwRect, dx, dy);
|
---|
269 | }
|
---|
270 |
|
---|
271 | void QWidgetBackingStore::releaseBuffer()
|
---|
272 | {
|
---|
273 | if (windowSurface)
|
---|
274 | windowSurface->setGeometry(QRect());
|
---|
275 | #ifdef Q_BACKINGSTORE_SUBSURFACES
|
---|
276 | for (int i = 0; i < subSurfaces.size(); ++i)
|
---|
277 | subSurfaces.at(i)->setGeometry(QRect());
|
---|
278 | #endif
|
---|
279 | }
|
---|
280 |
|
---|
281 | /*!
|
---|
282 | Prepares the window surface to paint a\ toClean region of the \a widget and
|
---|
283 | updates the BeginPaintInfo struct accordingly.
|
---|
284 |
|
---|
285 | The \a toClean region might be clipped by the window surface.
|
---|
286 | */
|
---|
287 | void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface,
|
---|
288 | BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
|
---|
289 | {
|
---|
290 | #ifdef Q_WS_QWS
|
---|
291 | QWSWindowSurface *surface = static_cast<QWSWindowSurface *>(windowSurface);
|
---|
292 | QWidget *surfaceWidget = surface->window();
|
---|
293 |
|
---|
294 | if (!surface->isValid()) {
|
---|
295 | // this looks strange but it really just releases the surface
|
---|
296 | surface->releaseSurface();
|
---|
297 | // the old window surface is deleted in setWindowSurface, which is
|
---|
298 | // called from QWindowSurface constructor.
|
---|
299 | windowSurface = tlw->d_func()->createDefaultWindowSurface();
|
---|
300 | surface = static_cast<QWSWindowSurface *>(windowSurface);
|
---|
301 | // createDefaultWindowSurface() will set topdata->windowSurface on the
|
---|
302 | // widget to zero. However, if this is a sub-surface, it should point
|
---|
303 | // to the widget's sub windowSurface, so we set that here:
|
---|
304 | if (!surfaceWidget->isWindow())
|
---|
305 | surfaceWidget->d_func()->topData()->windowSurface = windowSurface;
|
---|
306 | surface->setGeometry(topLevelRect());
|
---|
307 | returnInfo->windowSurfaceRecreated = true;
|
---|
308 | }
|
---|
309 |
|
---|
310 | const QRegion toCleanUnclipped(toClean);
|
---|
311 |
|
---|
312 | if (surfaceWidget->isWindow())
|
---|
313 | tlwOffset = surface->painterOffset();
|
---|
314 | #ifdef Q_BACKINGSTORE_SUBSURFACES
|
---|
315 | else if (toCleanIsInTopLevelCoordinates)
|
---|
316 | toClean &= surface->clipRegion().translated(surfaceWidget->mapTo(tlw, QPoint()));
|
---|
317 | if (!toCleanIsInTopLevelCoordinates && windowSurface == this->windowSurface)
|
---|
318 | toClean &= surface->clipRegion().translated(-widget->mapTo(surfaceWidget, QPoint()));
|
---|
319 | #else
|
---|
320 | toClean &= surface->clipRegion();
|
---|
321 | #endif
|
---|
322 |
|
---|
323 | if (toClean.isEmpty()) {
|
---|
324 | if (surfaceWidget->isWindow()) {
|
---|
325 | dirtyFromPreviousSync += toCleanUnclipped;
|
---|
326 | hasDirtyFromPreviousSync = true;
|
---|
327 | }
|
---|
328 |
|
---|
329 | returnInfo->nothingToPaint = true;
|
---|
330 | // Nothing to repaint. However, we might have newly exposed areas on the
|
---|
331 | // screen, so we have to make sure those are flushed.
|
---|
332 | flush();
|
---|
333 | return;
|
---|
334 | }
|
---|
335 |
|
---|
336 | if (surfaceWidget->isWindow()) {
|
---|
337 | if (toCleanUnclipped != toClean) {
|
---|
338 | dirtyFromPreviousSync += (toCleanUnclipped - surface->clipRegion());
|
---|
339 | hasDirtyFromPreviousSync = true;
|
---|
340 | }
|
---|
341 | if (hasDirtyFromPreviousSync) {
|
---|
342 | dirtyFromPreviousSync -= toClean;
|
---|
343 | hasDirtyFromPreviousSync = !dirtyFromPreviousSync.isEmpty();
|
---|
344 | }
|
---|
345 | }
|
---|
346 |
|
---|
347 | #endif // Q_WS_QWS
|
---|
348 |
|
---|
349 | Q_UNUSED(widget);
|
---|
350 | Q_UNUSED(toCleanIsInTopLevelCoordinates);
|
---|
351 |
|
---|
352 | // Always flush repainted areas.
|
---|
353 | dirtyOnScreen += toClean;
|
---|
354 |
|
---|
355 | #if defined(Q_WS_QWS) && !defined(Q_BACKINGSTORE_SUBSURFACES)
|
---|
356 | toClean.translate(tlwOffset);
|
---|
357 | #endif
|
---|
358 |
|
---|
359 | #ifdef QT_NO_PAINT_DEBUG
|
---|
360 | windowSurface->beginPaint(toClean);
|
---|
361 | #else
|
---|
362 | returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
|
---|
363 | // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
|
---|
364 | // the BackingStore lock, so if we hold that, the server will
|
---|
365 | // never release the Communication lock that we are waiting for in
|
---|
366 | // sendSynchronousCommand
|
---|
367 | if (!returnInfo->wasFlushed)
|
---|
368 | windowSurface->beginPaint(toClean);
|
---|
369 | #endif
|
---|
370 |
|
---|
371 | Q_UNUSED(returnInfo);
|
---|
372 | }
|
---|
373 |
|
---|
374 | void QWidgetBackingStore::endPaint(const QRegion &cleaned, QWindowSurface *windowSurface,
|
---|
375 | BeginPaintInfo *beginPaintInfo)
|
---|
376 | {
|
---|
377 | #ifndef QT_NO_PAINT_DEBUG
|
---|
378 | if (!beginPaintInfo->wasFlushed)
|
---|
379 | windowSurface->endPaint(cleaned);
|
---|
380 | else
|
---|
381 | QWidgetBackingStore::unflushPaint(tlw, cleaned);
|
---|
382 | #else
|
---|
383 | Q_UNUSED(beginPaintInfo);
|
---|
384 | windowSurface->endPaint(cleaned);
|
---|
385 | #endif
|
---|
386 |
|
---|
387 | #ifdef Q_BACKINGSTORE_SUBSURFACES
|
---|
388 | flush(static_cast<QWSWindowSurface *>(windowSurface)->window(), windowSurface);
|
---|
389 | #else
|
---|
390 | flush();
|
---|
391 | #endif
|
---|
392 | }
|
---|
393 |
|
---|
394 | /*!
|
---|
395 | Returns the region (in top-level coordinates) that needs repaint and/or flush.
|
---|
396 |
|
---|
397 | If the widget is non-zero, only the dirty region for the widget is returned
|
---|
398 | and the region will be in widget coordinates.
|
---|
399 | */
|
---|
400 | QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
|
---|
401 | {
|
---|
402 | const bool widgetDirty = widget && widget != tlw;
|
---|
403 | const QRect tlwRect(topLevelRect());
|
---|
404 | const QRect surfaceGeometry(windowSurface->geometry());
|
---|
405 | if (fullUpdatePending || (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size())) {
|
---|
406 | if (widgetDirty) {
|
---|
407 | const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
|
---|
408 | const QPoint offset(widget->mapTo(tlw, QPoint()));
|
---|
409 | const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
|
---|
410 | return dirtyWidgetRect.translated(-offset);
|
---|
411 | }
|
---|
412 | return QRect(QPoint(), tlwRect.size());
|
---|
413 | }
|
---|
414 |
|
---|
415 | // Calculate the region that needs repaint.
|
---|
416 | QRegion r(dirty);
|
---|
417 | for (int i = 0; i < dirtyWidgets.size(); ++i) {
|
---|
418 | QWidget *w = dirtyWidgets.at(i);
|
---|
419 | if (widgetDirty && w != widget && !widget->isAncestorOf(w))
|
---|
420 | continue;
|
---|
421 | r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
|
---|
422 | }
|
---|
423 |
|
---|
424 | // Append the region that needs flush.
|
---|
425 | r += dirtyOnScreen;
|
---|
426 |
|
---|
427 | if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
|
---|
428 | for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
|
---|
429 | QWidget *w = dirtyOnScreenWidgets->at(i);
|
---|
430 | if (widgetDirty && w != widget && !widget->isAncestorOf(w))
|
---|
431 | continue;
|
---|
432 | QWidgetPrivate *wd = w->d_func();
|
---|
433 | Q_ASSERT(wd->needsFlush);
|
---|
434 | r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
|
---|
435 | }
|
---|
436 | }
|
---|
437 |
|
---|
438 | if (widgetDirty) {
|
---|
439 | // Intersect with the widget geometry and translate to its coordinates.
|
---|
440 | const QPoint offset(widget->mapTo(tlw, QPoint()));
|
---|
441 | r &= widget->rect().translated(offset);
|
---|
442 | r.translate(-offset);
|
---|
443 | }
|
---|
444 | return r;
|
---|
445 | }
|
---|
446 |
|
---|
447 | /*!
|
---|
448 | Returns the static content inside the \a parent if non-zero; otherwise the static content
|
---|
449 | for the entire backing store is returned. The content will be clipped to \a withinClipRect
|
---|
450 | if non-empty.
|
---|
451 | */
|
---|
452 | QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
|
---|
453 | {
|
---|
454 | if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
|
---|
455 | const QRect surfaceGeometry(windowSurface->geometry());
|
---|
456 | QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
|
---|
457 | if (!withinClipRect.isEmpty())
|
---|
458 | surfaceRect &= withinClipRect;
|
---|
459 | return QRegion(surfaceRect);
|
---|
460 | }
|
---|
461 |
|
---|
462 | QRegion region;
|
---|
463 | if (parent && parent->d_func()->children.isEmpty())
|
---|
464 | return region;
|
---|
465 |
|
---|
466 | const bool clipToRect = !withinClipRect.isEmpty();
|
---|
467 | const int count = staticWidgets.count();
|
---|
468 | for (int i = 0; i < count; ++i) {
|
---|
469 | QWidget *w = staticWidgets.at(i);
|
---|
470 | QWidgetPrivate *wd = w->d_func();
|
---|
471 | if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
|
---|
472 | || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
|
---|
473 | continue;
|
---|
474 | }
|
---|
475 |
|
---|
476 | QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
|
---|
477 | const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
|
---|
478 | if (clipToRect)
|
---|
479 | rect &= withinClipRect.translated(-offset);
|
---|
480 | if (rect.isEmpty())
|
---|
481 | continue;
|
---|
482 |
|
---|
483 | rect &= wd->clipRect();
|
---|
484 | if (rect.isEmpty())
|
---|
485 | continue;
|
---|
486 |
|
---|
487 | QRegion visible(rect);
|
---|
488 | wd->clipToEffectiveMask(visible);
|
---|
489 | if (visible.isEmpty())
|
---|
490 | continue;
|
---|
491 | wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
|
---|
492 |
|
---|
493 | visible.translate(offset);
|
---|
494 | region += visible;
|
---|
495 | }
|
---|
496 |
|
---|
497 | return region;
|
---|
498 | }
|
---|
499 |
|
---|
500 | static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
|
---|
501 | {
|
---|
502 | if (!widget)
|
---|
503 | return;
|
---|
504 |
|
---|
505 | if (updateImmediately) {
|
---|
506 | QEvent event(QEvent::UpdateRequest);
|
---|
507 | QApplication::sendEvent(widget, &event);
|
---|
508 | } else {
|
---|
509 | QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
|
---|
510 | }
|
---|
511 | }
|
---|
512 |
|
---|
513 | /*!
|
---|
514 | Marks the region of the widget as dirty (if not already marked as dirty) and
|
---|
515 | posts an UpdateRequest event to the top-level widget (if not already posted).
|
---|
516 |
|
---|
517 | If updateImmediately is true, the event is sent immediately instead of posted.
|
---|
518 |
|
---|
519 | If invalidateBuffer is true, all widgets intersecting with the region will be dirty.
|
---|
520 |
|
---|
521 | If the widget paints directly on screen, the event is sent to the widget
|
---|
522 | instead of the top-level widget, and invalidateBuffer is completely ignored.
|
---|
523 |
|
---|
524 | ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
|
---|
525 | */
|
---|
526 | void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately,
|
---|
527 | bool invalidateBuffer)
|
---|
528 | {
|
---|
529 | Q_ASSERT(tlw->d_func()->extra);
|
---|
530 | Q_ASSERT(tlw->d_func()->extra->topextra);
|
---|
531 | Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
|
---|
532 | Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
|
---|
533 | Q_ASSERT(widget->window() == tlw);
|
---|
534 | Q_ASSERT(!rgn.isEmpty());
|
---|
535 |
|
---|
536 | #ifndef QT_NO_GRAPHICSEFFECT
|
---|
537 | widget->d_func()->invalidateGraphicsEffectsRecursively();
|
---|
538 | #endif //QT_NO_GRAPHICSEFFECT
|
---|
539 |
|
---|
540 | if (widget->d_func()->paintOnScreen()) {
|
---|
541 | if (widget->d_func()->dirty.isEmpty()) {
|
---|
542 | widget->d_func()->dirty = rgn;
|
---|
543 | sendUpdateRequest(widget, updateImmediately);
|
---|
544 | return;
|
---|
545 | } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) {
|
---|
546 | if (updateImmediately)
|
---|
547 | sendUpdateRequest(widget, updateImmediately);
|
---|
548 | return; // Already dirty.
|
---|
549 | }
|
---|
550 |
|
---|
551 | const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
|
---|
552 | widget->d_func()->dirty += rgn;
|
---|
553 | if (!eventAlreadyPosted || updateImmediately)
|
---|
554 | sendUpdateRequest(widget, updateImmediately);
|
---|
555 | return;
|
---|
556 | }
|
---|
557 |
|
---|
558 | if (fullUpdatePending) {
|
---|
559 | if (updateImmediately)
|
---|
560 | sendUpdateRequest(tlw, updateImmediately);
|
---|
561 | return;
|
---|
562 | }
|
---|
563 |
|
---|
564 | if (!windowSurface->hasPartialUpdateSupport()) {
|
---|
565 | fullUpdatePending = true;
|
---|
566 | sendUpdateRequest(tlw, updateImmediately);
|
---|
567 | return;
|
---|
568 | }
|
---|
569 |
|
---|
570 | const QPoint offset = widget->mapTo(tlw, QPoint());
|
---|
571 | const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
|
---|
572 | if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
|
---|
573 | if (updateImmediately)
|
---|
574 | sendUpdateRequest(tlw, updateImmediately);
|
---|
575 | return; // Already dirty.
|
---|
576 | }
|
---|
577 |
|
---|
578 | if (invalidateBuffer) {
|
---|
579 | const bool eventAlreadyPosted = !dirty.isEmpty();
|
---|
580 | #ifndef QT_NO_GRAPHICSEFFECT
|
---|
581 | if (widget->d_func()->graphicsEffect)
|
---|
582 | dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
|
---|
583 | else
|
---|
584 | #endif //QT_NO_GRAPHICSEFFECT
|
---|
585 | dirty += rgn.translated(offset);
|
---|
586 | if (!eventAlreadyPosted || updateImmediately)
|
---|
587 | sendUpdateRequest(tlw, updateImmediately);
|
---|
588 | return;
|
---|
589 | }
|
---|
590 |
|
---|
591 | if (dirtyWidgets.isEmpty()) {
|
---|
592 | addDirtyWidget(widget, rgn);
|
---|
593 | sendUpdateRequest(tlw, updateImmediately);
|
---|
594 | return;
|
---|
595 | }
|
---|
596 |
|
---|
597 | if (widget->d_func()->inDirtyList) {
|
---|
598 | if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
|
---|
599 | #ifndef QT_NO_GRAPHICSEFFECT
|
---|
600 | if (widget->d_func()->graphicsEffect)
|
---|
601 | widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
|
---|
602 | else
|
---|
603 | #endif //QT_NO_GRAPHICSEFFECT
|
---|
604 | widget->d_func()->dirty += rgn;
|
---|
605 | }
|
---|
606 | } else {
|
---|
607 | addDirtyWidget(widget, rgn);
|
---|
608 | }
|
---|
609 |
|
---|
610 | if (updateImmediately)
|
---|
611 | sendUpdateRequest(tlw, updateImmediately);
|
---|
612 | }
|
---|
613 |
|
---|
614 | /*!
|
---|
615 | This function is equivalent to calling markDirty(QRegion(rect), ...), but
|
---|
616 | is more efficient as it eliminates QRegion operations/allocations and can
|
---|
617 | use the rect more precisely for additional cut-offs.
|
---|
618 |
|
---|
619 | ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
|
---|
620 | */
|
---|
621 | void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
|
---|
622 | bool invalidateBuffer)
|
---|
623 | {
|
---|
624 | Q_ASSERT(tlw->d_func()->extra);
|
---|
625 | Q_ASSERT(tlw->d_func()->extra->topextra);
|
---|
626 | Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
|
---|
627 | Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
|
---|
628 | Q_ASSERT(widget->window() == tlw);
|
---|
629 | Q_ASSERT(!rect.isEmpty());
|
---|
630 |
|
---|
631 | #ifndef QT_NO_GRAPHICSEFFECT
|
---|
632 | widget->d_func()->invalidateGraphicsEffectsRecursively();
|
---|
633 | #endif //QT_NO_GRAPHICSEFFECT
|
---|
634 |
|
---|
635 | if (widget->d_func()->paintOnScreen()) {
|
---|
636 | if (widget->d_func()->dirty.isEmpty()) {
|
---|
637 | widget->d_func()->dirty = QRegion(rect);
|
---|
638 | sendUpdateRequest(widget, updateImmediately);
|
---|
639 | return;
|
---|
640 | } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
|
---|
641 | if (updateImmediately)
|
---|
642 | sendUpdateRequest(widget, updateImmediately);
|
---|
643 | return; // Already dirty.
|
---|
644 | }
|
---|
645 |
|
---|
646 | const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
|
---|
647 | widget->d_func()->dirty += rect;
|
---|
648 | if (!eventAlreadyPosted || updateImmediately)
|
---|
649 | sendUpdateRequest(widget, updateImmediately);
|
---|
650 | return;
|
---|
651 | }
|
---|
652 |
|
---|
653 | if (fullUpdatePending) {
|
---|
654 | if (updateImmediately)
|
---|
655 | sendUpdateRequest(tlw, updateImmediately);
|
---|
656 | return;
|
---|
657 | }
|
---|
658 |
|
---|
659 | if (!windowSurface->hasPartialUpdateSupport()) {
|
---|
660 | fullUpdatePending = true;
|
---|
661 | sendUpdateRequest(tlw, updateImmediately);
|
---|
662 | return;
|
---|
663 | }
|
---|
664 |
|
---|
665 | const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
|
---|
666 | const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
|
---|
667 | if (qt_region_strictContains(dirty, translatedRect)) {
|
---|
668 | if (updateImmediately)
|
---|
669 | sendUpdateRequest(tlw, updateImmediately);
|
---|
670 | return; // Already dirty
|
---|
671 | }
|
---|
672 |
|
---|
673 | if (invalidateBuffer) {
|
---|
674 | const bool eventAlreadyPosted = !dirty.isEmpty();
|
---|
675 | dirty += translatedRect;
|
---|
676 | if (!eventAlreadyPosted || updateImmediately)
|
---|
677 | sendUpdateRequest(tlw, updateImmediately);
|
---|
678 | return;
|
---|
679 | }
|
---|
680 |
|
---|
681 | if (dirtyWidgets.isEmpty()) {
|
---|
682 | addDirtyWidget(widget, rect);
|
---|
683 | sendUpdateRequest(tlw, updateImmediately);
|
---|
684 | return;
|
---|
685 | }
|
---|
686 |
|
---|
687 | if (widget->d_func()->inDirtyList) {
|
---|
688 | if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
|
---|
689 | widget->d_func()->dirty += widgetRect;
|
---|
690 | } else {
|
---|
691 | addDirtyWidget(widget, rect);
|
---|
692 | }
|
---|
693 |
|
---|
694 | if (updateImmediately)
|
---|
695 | sendUpdateRequest(tlw, updateImmediately);
|
---|
696 | }
|
---|
697 |
|
---|
698 | /*!
|
---|
699 | Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
|
---|
700 | the backing store to the \a widget's native parent next time flush() is called.
|
---|
701 |
|
---|
702 | Paint on screen widgets are ignored.
|
---|
703 | */
|
---|
704 | void QWidgetBackingStore::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset)
|
---|
705 | {
|
---|
706 | if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
|
---|
707 | return;
|
---|
708 |
|
---|
709 | #if defined(Q_WS_QWS) || defined(Q_WS_MAC)
|
---|
710 | if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
|
---|
711 | dirtyOnScreen += region.translated(topLevelOffset);
|
---|
712 | return;
|
---|
713 | #endif
|
---|
714 |
|
---|
715 | // Top-level.
|
---|
716 | if (widget == tlw) {
|
---|
717 | if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
|
---|
718 | dirtyOnScreen += region;
|
---|
719 | return;
|
---|
720 | }
|
---|
721 |
|
---|
722 | // Alien widgets.
|
---|
723 | if (!widget->internalWinId()) {
|
---|
724 | QWidget *nativeParent = widget->nativeParentWidget();
|
---|
725 | // Alien widgets with the top-level as the native parent (common case).
|
---|
726 | if (nativeParent == tlw) {
|
---|
727 | if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
|
---|
728 | dirtyOnScreen += region.translated(topLevelOffset);
|
---|
729 | return;
|
---|
730 | }
|
---|
731 |
|
---|
732 | // Alien widgets with native parent != tlw.
|
---|
733 | QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
|
---|
734 | if (!nativeParentPrivate->needsFlush)
|
---|
735 | nativeParentPrivate->needsFlush = new QRegion;
|
---|
736 | const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
|
---|
737 | *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
|
---|
738 | appendDirtyOnScreenWidget(nativeParent);
|
---|
739 | return;
|
---|
740 | }
|
---|
741 |
|
---|
742 | // Native child widgets.
|
---|
743 | QWidgetPrivate *widgetPrivate = widget->d_func();
|
---|
744 | if (!widgetPrivate->needsFlush)
|
---|
745 | widgetPrivate->needsFlush = new QRegion;
|
---|
746 | *widgetPrivate->needsFlush += region;
|
---|
747 | appendDirtyOnScreenWidget(widget);
|
---|
748 | }
|
---|
749 |
|
---|
750 | void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
|
---|
751 | {
|
---|
752 | if (!w)
|
---|
753 | return;
|
---|
754 |
|
---|
755 | dirtyWidgetsRemoveAll(w);
|
---|
756 | dirtyOnScreenWidgetsRemoveAll(w);
|
---|
757 | resetWidget(w);
|
---|
758 |
|
---|
759 | QWidgetPrivate *wd = w->d_func();
|
---|
760 | const int n = wd->children.count();
|
---|
761 | for (int i = 0; i < n; ++i) {
|
---|
762 | if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
|
---|
763 | removeDirtyWidget(child);
|
---|
764 | }
|
---|
765 | }
|
---|
766 |
|
---|
767 | #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
|
---|
768 | bool QWidgetBackingStore::hasDirtyWindowDecoration() const
|
---|
769 | {
|
---|
770 | QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
|
---|
771 | if (tlwExtra && tlwExtra->qwsManager)
|
---|
772 | return !tlwExtra->qwsManager->d_func()->dirtyRegions.isEmpty();
|
---|
773 | return false;
|
---|
774 | }
|
---|
775 |
|
---|
776 | void QWidgetBackingStore::paintWindowDecoration()
|
---|
777 | {
|
---|
778 | if (!hasDirtyWindowDecoration())
|
---|
779 | return;
|
---|
780 |
|
---|
781 | QDecoration &decoration = QApplication::qwsDecoration();
|
---|
782 | const QRect decorationRect = tlw->rect();
|
---|
783 | QRegion decorationRegion = decoration.region(tlw, decorationRect);
|
---|
784 |
|
---|
785 | QWSManagerPrivate *managerPrivate = tlw->d_func()->topData()->qwsManager->d_func();
|
---|
786 | const bool doClipping = !managerPrivate->entireDecorationNeedsRepaint
|
---|
787 | && !managerPrivate->dirtyClip.isEmpty();
|
---|
788 |
|
---|
789 | if (doClipping) {
|
---|
790 | decorationRegion &= static_cast<QWSWindowSurface *>(windowSurface)->clipRegion();
|
---|
791 | decorationRegion &= managerPrivate->dirtyClip;
|
---|
792 | }
|
---|
793 |
|
---|
794 | if (decorationRegion.isEmpty())
|
---|
795 | return;
|
---|
796 |
|
---|
797 | //### The QWS decorations do not always paint the pixels they promise to paint.
|
---|
798 | // This causes painting problems with QWSMemorySurface. Since none of the other
|
---|
799 | // window surfaces actually use the region, passing an empty region is a safe
|
---|
800 | // workaround.
|
---|
801 |
|
---|
802 | windowSurface->beginPaint(QRegion());
|
---|
803 |
|
---|
804 | QPaintEngine *engine = windowSurface->paintDevice()->paintEngine();
|
---|
805 | Q_ASSERT(engine);
|
---|
806 | const QRegion oldSystemClip(engine->systemClip());
|
---|
807 | engine->setSystemClip(decorationRegion.translated(tlwOffset));
|
---|
808 |
|
---|
809 | QPainter painter(windowSurface->paintDevice());
|
---|
810 | painter.setFont(QApplication::font());
|
---|
811 | painter.translate(tlwOffset);
|
---|
812 |
|
---|
813 | const int numDirty = managerPrivate->dirtyRegions.size();
|
---|
814 | for (int i = 0; i < numDirty; ++i) {
|
---|
815 | const int area = managerPrivate->dirtyRegions.at(i);
|
---|
816 |
|
---|
817 | QRegion clipRegion = decoration.region(tlw, decorationRect, area);
|
---|
818 | if (!clipRegion.isEmpty()) {
|
---|
819 | // Decoration styles changes the clip and assumes the old clip is non-empty,
|
---|
820 | // so we have to set it, but in theory it shouldn't be required.
|
---|
821 | painter.setClipRegion(clipRegion);
|
---|
822 | decoration.paint(&painter, tlw, area, managerPrivate->dirtyStates.at(i));
|
---|
823 | }
|
---|
824 | }
|
---|
825 | markDirtyOnScreen(decorationRegion, tlw, QPoint());
|
---|
826 |
|
---|
827 | painter.end();
|
---|
828 | windowSurface->endPaint(decorationRegion);
|
---|
829 | managerPrivate->clearDirtyRegions();
|
---|
830 | engine->setSystemClip(oldSystemClip);
|
---|
831 | }
|
---|
832 | #endif
|
---|
833 |
|
---|
834 | void QWidgetBackingStore::updateLists(QWidget *cur)
|
---|
835 | {
|
---|
836 | if (!cur)
|
---|
837 | return;
|
---|
838 |
|
---|
839 | QList<QObject*> children = cur->children();
|
---|
840 | for (int i = 0; i < children.size(); ++i) {
|
---|
841 | QWidget *child = qobject_cast<QWidget*>(children.at(i));
|
---|
842 | if (!child)
|
---|
843 | continue;
|
---|
844 |
|
---|
845 | updateLists(child);
|
---|
846 | }
|
---|
847 |
|
---|
848 | if (cur->testAttribute(Qt::WA_StaticContents))
|
---|
849 | addStaticWidget(cur);
|
---|
850 |
|
---|
851 | #ifdef Q_BACKINGSTORE_SUBSURFACES
|
---|
852 | QTLWExtra *extra = cur->d_func()->maybeTopData();
|
---|
853 | if (extra && extra->windowSurface && cur != tlw)
|
---|
854 | subSurfaces.append(extra->windowSurface);
|
---|
855 | #endif
|
---|
856 | }
|
---|
857 |
|
---|
858 | QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
|
---|
859 | : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false)
|
---|
860 | , fullUpdatePending(0)
|
---|
861 | {
|
---|
862 | windowSurface = tlw->windowSurface();
|
---|
863 | if (!windowSurface)
|
---|
864 | windowSurface = topLevel->d_func()->createDefaultWindowSurface();
|
---|
865 |
|
---|
866 | // The QWindowSurface constructor will call QWidget::setWindowSurface(),
|
---|
867 | // but automatically created surfaces should not be added to the topdata.
|
---|
868 | #ifdef Q_BACKINGSTORE_SUBSURFACES
|
---|
869 | Q_ASSERT(topLevel->d_func()->topData()->windowSurface == windowSurface);
|
---|
870 | #endif
|
---|
871 | topLevel->d_func()->topData()->windowSurface = 0;
|
---|
872 |
|
---|
873 | // Ensure all existing subsurfaces and static widgets are added to their respective lists.
|
---|
874 | updateLists(topLevel);
|
---|
875 | }
|
---|
876 |
|
---|
877 | QWidgetBackingStore::~QWidgetBackingStore()
|
---|
878 | {
|
---|
879 | for (int c = 0; c < dirtyWidgets.size(); ++c) {
|
---|
880 | resetWidget(dirtyWidgets.at(c));
|
---|
881 | }
|
---|
882 |
|
---|
883 | delete windowSurface;
|
---|
884 | windowSurface = 0;
|
---|
885 | delete dirtyOnScreenWidgets;
|
---|
886 | dirtyOnScreenWidgets = 0;
|
---|
887 | }
|
---|
888 |
|
---|
889 | //parent's coordinates; move whole rect; update parent and widget
|
---|
890 | //assume the screen blt has already been done, so we don't need to refresh that part
|
---|
891 | void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
|
---|
892 | {
|
---|
893 | Q_Q(QWidget);
|
---|
894 | if (!q->isVisible() || (dx == 0 && dy == 0))
|
---|
895 | return;
|
---|
896 |
|
---|
897 | QWidget *tlw = q->window();
|
---|
898 | QTLWExtra* x = tlw->d_func()->topData();
|
---|
899 | if (x->inTopLevelResize)
|
---|
900 | return;
|
---|
901 |
|
---|
902 | static int accelEnv = -1;
|
---|
903 | if (accelEnv == -1) {
|
---|
904 | accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0;
|
---|
905 | }
|
---|
906 |
|
---|
907 | QWidget *pw = q->parentWidget();
|
---|
908 | QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
|
---|
909 | QWidgetPrivate *pd = pw->d_func();
|
---|
910 | QRect clipR(pd->clipRect());
|
---|
911 | #ifdef Q_WS_QWS
|
---|
912 | QWidgetBackingStore *wbs = x->backingStore.data();
|
---|
913 | QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
|
---|
914 | clipR = clipR.intersected(surface->clipRegion().translated(-toplevelOffset).boundingRect());
|
---|
915 | #endif
|
---|
916 | const QRect newRect(rect.translated(dx, dy));
|
---|
917 | QRect destRect = rect.intersected(clipR);
|
---|
918 | if (destRect.isValid())
|
---|
919 | destRect = destRect.translated(dx, dy).intersected(clipR);
|
---|
920 | const QRect sourceRect(destRect.translated(-dx, -dy));
|
---|
921 | const QRect parentRect(rect & clipR);
|
---|
922 |
|
---|
923 | bool accelerateMove = accelEnv && isOpaque
|
---|
924 | #ifndef QT_NO_GRAPHICSVIEW
|
---|
925 | // No accelerate move for proxy widgets.
|
---|
926 | && !tlw->d_func()->extra->proxyWidget
|
---|
927 | #endif
|
---|
928 | && !isOverlapped(sourceRect) && !isOverlapped(destRect);
|
---|
929 |
|
---|
930 | if (!accelerateMove) {
|
---|
931 | QRegion parentR(effectiveRectFor(parentRect));
|
---|
932 | if (!extra || !extra->hasMask) {
|
---|
933 | parentR -= newRect;
|
---|
934 | } else {
|
---|
935 | // invalidateBuffer() excludes anything outside the mask
|
---|
936 | parentR += newRect & clipR;
|
---|
937 | }
|
---|
938 | pd->invalidateBuffer(parentR);
|
---|
939 | invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft()));
|
---|
940 | } else {
|
---|
941 |
|
---|
942 | QWidgetBackingStore *wbs = x->backingStore.data();
|
---|
943 | QRegion childExpose(newRect & clipR);
|
---|
944 |
|
---|
945 | if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
|
---|
946 | childExpose -= destRect;
|
---|
947 |
|
---|
948 | if (!pw->updatesEnabled())
|
---|
949 | return;
|
---|
950 |
|
---|
951 | const bool childUpdatesEnabled = q->updatesEnabled();
|
---|
952 | if (childUpdatesEnabled && !childExpose.isEmpty()) {
|
---|
953 | childExpose.translate(-data.crect.topLeft());
|
---|
954 | wbs->markDirty(childExpose, q);
|
---|
955 | isMoved = true;
|
---|
956 | }
|
---|
957 |
|
---|
958 | QRegion parentExpose(parentRect);
|
---|
959 | parentExpose -= newRect;
|
---|
960 | if (extra && extra->hasMask)
|
---|
961 | parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
|
---|
962 |
|
---|
963 | if (!parentExpose.isEmpty()) {
|
---|
964 | wbs->markDirty(parentExpose, pw);
|
---|
965 | pd->isMoved = true;
|
---|
966 | }
|
---|
967 |
|
---|
968 | if (childUpdatesEnabled) {
|
---|
969 | QRegion needsFlush(sourceRect);
|
---|
970 | needsFlush += destRect;
|
---|
971 | wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
|
---|
972 | }
|
---|
973 | }
|
---|
974 | }
|
---|
975 |
|
---|
976 | //widget's coordinates; scroll within rect; only update widget
|
---|
977 | void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
|
---|
978 | {
|
---|
979 | Q_Q(QWidget);
|
---|
980 | QWidget *tlw = q->window();
|
---|
981 | QTLWExtra* x = tlw->d_func()->topData();
|
---|
982 | if (x->inTopLevelResize)
|
---|
983 | return;
|
---|
984 |
|
---|
985 | QWidgetBackingStore *wbs = x->backingStore.data();
|
---|
986 | if (!wbs)
|
---|
987 | return;
|
---|
988 |
|
---|
989 | static int accelEnv = -1;
|
---|
990 | if (accelEnv == -1) {
|
---|
991 | accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
|
---|
992 | }
|
---|
993 |
|
---|
994 | QRect scrollRect = rect & clipRect();
|
---|
995 | bool overlapped = false;
|
---|
996 | bool accelerateScroll = accelEnv && isOpaque
|
---|
997 | && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
|
---|
998 |
|
---|
999 | #if defined(Q_WS_QWS)
|
---|
1000 | QWSWindowSurface *surface;
|
---|
1001 | surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
|
---|
1002 |
|
---|
1003 | if (accelerateScroll && !surface->isBuffered()) {
|
---|
1004 | const QRegion surfaceClip = surface->clipRegion();
|
---|
1005 | const QRegion outsideClip = QRegion(rect) - surfaceClip;
|
---|
1006 | if (!outsideClip.isEmpty()) {
|
---|
1007 | const QVector<QRect> clipped = (surfaceClip & rect).rects();
|
---|
1008 | if (clipped.size() < 8) {
|
---|
1009 | for (int i = 0; i < clipped.size(); ++i)
|
---|
1010 | this->scrollRect(clipped.at(i), dx, dy);
|
---|
1011 | return;
|
---|
1012 | } else {
|
---|
1013 | accelerateScroll = false;
|
---|
1014 | }
|
---|
1015 | }
|
---|
1016 | }
|
---|
1017 | #endif // Q_WS_QWS
|
---|
1018 |
|
---|
1019 | if (!accelerateScroll) {
|
---|
1020 | if (overlapped) {
|
---|
1021 | QRegion region(scrollRect);
|
---|
1022 | subtractOpaqueSiblings(region);
|
---|
1023 | invalidateBuffer(region);
|
---|
1024 | }else {
|
---|
1025 | invalidateBuffer(scrollRect);
|
---|
1026 | }
|
---|
1027 | } else {
|
---|
1028 | const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
|
---|
1029 | #ifdef Q_WS_QWS
|
---|
1030 | QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
|
---|
1031 | const QRegion clip = surface->clipRegion().translated(-toplevelOffset) & scrollRect;
|
---|
1032 | const QRect clipBoundingRect = clip.boundingRect();
|
---|
1033 | scrollRect &= clipBoundingRect;
|
---|
1034 | #endif
|
---|
1035 | const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
|
---|
1036 | const QRect sourceRect = destRect.translated(-dx, -dy);
|
---|
1037 |
|
---|
1038 | QRegion childExpose(scrollRect);
|
---|
1039 | if (sourceRect.isValid()) {
|
---|
1040 | if (wbs->bltRect(sourceRect, dx, dy, q))
|
---|
1041 | childExpose -= destRect;
|
---|
1042 | }
|
---|
1043 |
|
---|
1044 | if (inDirtyList) {
|
---|
1045 | if (rect == q->rect()) {
|
---|
1046 | dirty.translate(dx, dy);
|
---|
1047 | } else {
|
---|
1048 | QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
|
---|
1049 | if (!dirtyScrollRegion.isEmpty()) {
|
---|
1050 | dirty -= dirtyScrollRegion;
|
---|
1051 | dirtyScrollRegion.translate(dx, dy);
|
---|
1052 | dirty += dirtyScrollRegion;
|
---|
1053 | }
|
---|
1054 | }
|
---|
1055 | }
|
---|
1056 |
|
---|
1057 | if (!q->updatesEnabled())
|
---|
1058 | return;
|
---|
1059 |
|
---|
1060 | if (!childExpose.isEmpty()) {
|
---|
1061 | wbs->markDirty(childExpose, q);
|
---|
1062 | isScrolled = true;
|
---|
1063 | }
|
---|
1064 |
|
---|
1065 | // Instead of using native scroll-on-screen, we copy from
|
---|
1066 | // backingstore, giving only one screen update for each
|
---|
1067 | // scroll, and a solid appearance
|
---|
1068 | wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
|
---|
1069 | }
|
---|
1070 | }
|
---|
1071 |
|
---|
1072 | static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
|
---|
1073 | {
|
---|
1074 | if (!tlw || !tlwExtra)
|
---|
1075 | return true;
|
---|
1076 |
|
---|
1077 | #ifdef Q_WS_X11
|
---|
1078 | // Delay the sync until we get an Expose event from X11 (initial show).
|
---|
1079 | // Qt::WA_Mapped is set to true, but the actual mapping has not yet occurred.
|
---|
1080 | // However, we must repaint immediately regardless of the state if someone calls repaint().
|
---|
1081 | if (tlwExtra->waitingForMapNotify && !tlwExtra->inRepaint)
|
---|
1082 | return true;
|
---|
1083 | #endif
|
---|
1084 |
|
---|
1085 | if (!tlw->testAttribute(Qt::WA_Mapped))
|
---|
1086 | return true;
|
---|
1087 |
|
---|
1088 | if (!tlw->isVisible()
|
---|
1089 | #ifndef Q_WS_X11
|
---|
1090 | // If we're minimized on X11, WA_Mapped will be false and we
|
---|
1091 | // will return in the case above. Some window managers on X11
|
---|
1092 | // sends us the PropertyNotify to change the minimized state
|
---|
1093 | // *AFTER* we've received the expose event, which is baaad.
|
---|
1094 | || tlw->isMinimized()
|
---|
1095 | #endif
|
---|
1096 | )
|
---|
1097 | return true;
|
---|
1098 |
|
---|
1099 | return false;
|
---|
1100 | }
|
---|
1101 |
|
---|
1102 | /*!
|
---|
1103 | Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
|
---|
1104 |
|
---|
1105 | If there's nothing to repaint, the area is flushed and painting does not occur;
|
---|
1106 | otherwise the area is marked as dirty on screen and will be flushed right after
|
---|
1107 | we are done with all painting.
|
---|
1108 | */
|
---|
1109 | void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
|
---|
1110 | {
|
---|
1111 | QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
|
---|
1112 | if (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize)
|
---|
1113 | return;
|
---|
1114 |
|
---|
1115 | if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible()
|
---|
1116 | || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
|
---|
1117 | return;
|
---|
1118 | }
|
---|
1119 |
|
---|
1120 | // Nothing to repaint.
|
---|
1121 | if (!isDirty()) {
|
---|
1122 | qt_flush(exposedWidget, exposedRegion, windowSurface, tlw, tlwOffset);
|
---|
1123 | return;
|
---|
1124 | }
|
---|
1125 |
|
---|
1126 | if (exposedWidget != tlw)
|
---|
1127 | markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
|
---|
1128 | else
|
---|
1129 | markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
|
---|
1130 | sync();
|
---|
1131 | }
|
---|
1132 |
|
---|
1133 | /*!
|
---|
1134 | Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
|
---|
1135 | */
|
---|
1136 | void QWidgetBackingStore::sync()
|
---|
1137 | {
|
---|
1138 | QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
|
---|
1139 | if (discardSyncRequest(tlw, tlwExtra)) {
|
---|
1140 | // If the top-level is minimized, it's not visible on the screen so we can delay the
|
---|
1141 | // update until it's shown again. In order to do that we must keep the dirty states.
|
---|
1142 | // These will be cleared when we receive the first expose after showNormal().
|
---|
1143 | // However, if the widget is not visible (isVisible() returns false), everything will
|
---|
1144 | // be invalidated once the widget is shown again, so clear all dirty states.
|
---|
1145 | if (!tlw->isVisible()) {
|
---|
1146 | dirty = QRegion();
|
---|
1147 | for (int i = 0; i < dirtyWidgets.size(); ++i)
|
---|
1148 | resetWidget(dirtyWidgets.at(i));
|
---|
1149 | dirtyWidgets.clear();
|
---|
1150 | fullUpdatePending = false;
|
---|
1151 | }
|
---|
1152 | return;
|
---|
1153 | }
|
---|
1154 |
|
---|
1155 | const bool inTopLevelResize = tlwExtra->inTopLevelResize;
|
---|
1156 | const bool updatesDisabled = !tlw->updatesEnabled();
|
---|
1157 | const QRect tlwRect(topLevelRect());
|
---|
1158 | const QRect surfaceGeometry(windowSurface->geometry());
|
---|
1159 | bool repaintAllWidgets = false;
|
---|
1160 |
|
---|
1161 | if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
|
---|
1162 | if (hasStaticContents()) {
|
---|
1163 | // Repaint existing dirty area and newly visible area.
|
---|
1164 | const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
|
---|
1165 | const QRegion staticRegion(staticContents(0, clipRect));
|
---|
1166 | QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
|
---|
1167 | newVisible -= staticRegion;
|
---|
1168 | dirty += newVisible;
|
---|
1169 | windowSurface->setStaticContents(staticRegion);
|
---|
1170 | } else {
|
---|
1171 | // Repaint everything.
|
---|
1172 | dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
|
---|
1173 | for (int i = 0; i < dirtyWidgets.size(); ++i)
|
---|
1174 | resetWidget(dirtyWidgets.at(i));
|
---|
1175 | dirtyWidgets.clear();
|
---|
1176 | repaintAllWidgets = true;
|
---|
1177 | }
|
---|
1178 | }
|
---|
1179 |
|
---|
1180 | if (inTopLevelResize || surfaceGeometry != tlwRect)
|
---|
1181 | windowSurface->setGeometry(tlwRect);
|
---|
1182 |
|
---|
1183 | if (updatesDisabled)
|
---|
1184 | return;
|
---|
1185 |
|
---|
1186 | if (hasDirtyFromPreviousSync)
|
---|
1187 | dirty += dirtyFromPreviousSync;
|
---|
1188 |
|
---|
1189 | // Contains everything that needs repaint.
|
---|
1190 | QRegion toClean(dirty);
|
---|
1191 |
|
---|
1192 | // Loop through all update() widgets and remove them from the list before they are
|
---|
1193 | // painted (in case someone calls update() in paintEvent). If the widget is opaque
|
---|
1194 | // and does not have transparent overlapping siblings, append it to the
|
---|
1195 | // opaqueNonOverlappedWidgets list and paint it directly without composition.
|
---|
1196 | QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
|
---|
1197 | for (int i = 0; i < dirtyWidgets.size(); ++i) {
|
---|
1198 | QWidget *w = dirtyWidgets.at(i);
|
---|
1199 | QWidgetPrivate *wd = w->d_func();
|
---|
1200 | if (wd->data.in_destructor)
|
---|
1201 | continue;
|
---|
1202 |
|
---|
1203 | // Clip with mask() and clipRect().
|
---|
1204 | wd->dirty &= wd->clipRect();
|
---|
1205 | wd->clipToEffectiveMask(wd->dirty);
|
---|
1206 |
|
---|
1207 | // Subtract opaque siblings and children.
|
---|
1208 | bool hasDirtySiblingsAbove = false;
|
---|
1209 | // We know for sure that the widget isn't overlapped if 'isMoved' is true.
|
---|
1210 | if (!wd->isMoved)
|
---|
1211 | wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
|
---|
1212 | // Scrolled and moved widgets must draw all children.
|
---|
1213 | if (!wd->isScrolled && !wd->isMoved)
|
---|
1214 | wd->subtractOpaqueChildren(wd->dirty, w->rect());
|
---|
1215 |
|
---|
1216 | if (wd->dirty.isEmpty()) {
|
---|
1217 | resetWidget(w);
|
---|
1218 | continue;
|
---|
1219 | }
|
---|
1220 |
|
---|
1221 | const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
|
---|
1222 | : wd->dirty);
|
---|
1223 | toClean += widgetDirty;
|
---|
1224 |
|
---|
1225 | #ifndef QT_NO_GRAPHICSVIEW
|
---|
1226 | if (tlw->d_func()->extra->proxyWidget) {
|
---|
1227 | resetWidget(w);
|
---|
1228 | continue;
|
---|
1229 | }
|
---|
1230 | #endif
|
---|
1231 |
|
---|
1232 | if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
|
---|
1233 | opaqueNonOverlappedWidgets.append(w);
|
---|
1234 | } else {
|
---|
1235 | resetWidget(w);
|
---|
1236 | dirty += widgetDirty;
|
---|
1237 | }
|
---|
1238 | }
|
---|
1239 | dirtyWidgets.clear();
|
---|
1240 |
|
---|
1241 | fullUpdatePending = false;
|
---|
1242 |
|
---|
1243 | if (toClean.isEmpty()) {
|
---|
1244 | // Nothing to repaint. However, we might have newly exposed areas on the
|
---|
1245 | // screen if this function was called from sync(QWidget *, QRegion)), so
|
---|
1246 | // we have to make sure those are flushed.
|
---|
1247 | flush();
|
---|
1248 | return;
|
---|
1249 | }
|
---|
1250 |
|
---|
1251 | #ifndef QT_NO_GRAPHICSVIEW
|
---|
1252 | if (tlw->d_func()->extra->proxyWidget) {
|
---|
1253 | updateStaticContentsSize();
|
---|
1254 | dirty = QRegion();
|
---|
1255 | const QVector<QRect> rects(toClean.rects());
|
---|
1256 | for (int i = 0; i < rects.size(); ++i)
|
---|
1257 | tlw->d_func()->extra->proxyWidget->update(rects.at(i));
|
---|
1258 | return;
|
---|
1259 | }
|
---|
1260 | #endif
|
---|
1261 |
|
---|
1262 | #ifndef Q_BACKINGSTORE_SUBSURFACES
|
---|
1263 | BeginPaintInfo beginPaintInfo;
|
---|
1264 | beginPaint(toClean, tlw, windowSurface, &beginPaintInfo);
|
---|
1265 | if (beginPaintInfo.nothingToPaint) {
|
---|
1266 | for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
|
---|
1267 | resetWidget(opaqueNonOverlappedWidgets[i]);
|
---|
1268 | dirty = QRegion();
|
---|
1269 | return;
|
---|
1270 | }
|
---|
1271 | #endif
|
---|
1272 |
|
---|
1273 | // Must do this before sending any paint events because
|
---|
1274 | // the size may change in the paint event.
|
---|
1275 | updateStaticContentsSize();
|
---|
1276 | const QRegion dirtyCopy(dirty);
|
---|
1277 | dirty = QRegion();
|
---|
1278 |
|
---|
1279 | // Paint opaque non overlapped widgets.
|
---|
1280 | for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
|
---|
1281 | QWidget *w = opaqueNonOverlappedWidgets[i];
|
---|
1282 | QWidgetPrivate *wd = w->d_func();
|
---|
1283 |
|
---|
1284 | int flags = QWidgetPrivate::DrawRecursive;
|
---|
1285 | // Scrolled and moved widgets must draw all children.
|
---|
1286 | if (!wd->isScrolled && !wd->isMoved)
|
---|
1287 | flags |= QWidgetPrivate::DontDrawOpaqueChildren;
|
---|
1288 | if (w == tlw)
|
---|
1289 | flags |= QWidgetPrivate::DrawAsRoot;
|
---|
1290 |
|
---|
1291 | QRegion toBePainted(wd->dirty);
|
---|
1292 | resetWidget(w);
|
---|
1293 |
|
---|
1294 | #ifdef Q_BACKINGSTORE_SUBSURFACES
|
---|
1295 | QWindowSurface *subSurface = w->windowSurface();
|
---|
1296 | BeginPaintInfo beginPaintInfo;
|
---|
1297 |
|
---|
1298 | QPoint off = w->mapTo(tlw, QPoint());
|
---|
1299 | toBePainted.translate(off);
|
---|
1300 | beginPaint(toBePainted, w, subSurface, &beginPaintInfo, true);
|
---|
1301 | toBePainted.translate(-off);
|
---|
1302 |
|
---|
1303 | if (beginPaintInfo.nothingToPaint)
|
---|
1304 | continue;
|
---|
1305 |
|
---|
1306 | if (beginPaintInfo.windowSurfaceRecreated) {
|
---|
1307 | // Eep the window surface has changed. The old one may have been
|
---|
1308 | // deleted, in which case we will segfault on the call to
|
---|
1309 | // painterOffset() below. Use the new window surface instead.
|
---|
1310 | subSurface = w->windowSurface();
|
---|
1311 | }
|
---|
1312 |
|
---|
1313 | QPoint offset(tlwOffset);
|
---|
1314 | if (subSurface == windowSurface)
|
---|
1315 | offset += w->mapTo(tlw, QPoint());
|
---|
1316 | else
|
---|
1317 | offset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
|
---|
1318 | wd->drawWidget(subSurface->paintDevice(), toBePainted, offset, flags, 0, this);
|
---|
1319 |
|
---|
1320 | endPaint(toBePainted, subSurface, &beginPaintInfo);
|
---|
1321 | #else
|
---|
1322 | QPoint offset(tlwOffset);
|
---|
1323 | if (w != tlw)
|
---|
1324 | offset += w->mapTo(tlw, QPoint());
|
---|
1325 | wd->drawWidget(windowSurface->paintDevice(), toBePainted, offset, flags, 0, this);
|
---|
1326 | #endif
|
---|
1327 | }
|
---|
1328 |
|
---|
1329 | // Paint the rest with composition.
|
---|
1330 | #ifndef Q_BACKINGSTORE_SUBSURFACES
|
---|
1331 | if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
|
---|
1332 | const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
|
---|
1333 | tlw->d_func()->drawWidget(windowSurface->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);
|
---|
1334 | }
|
---|
1335 |
|
---|
1336 | endPaint(toClean, windowSurface, &beginPaintInfo);
|
---|
1337 | #else
|
---|
1338 | if (!repaintAllWidgets && dirtyCopy.isEmpty())
|
---|
1339 | return; // Nothing more to paint.
|
---|
1340 |
|
---|
1341 | QList<QWindowSurface *> surfaceList(subSurfaces);
|
---|
1342 | surfaceList.prepend(windowSurface);
|
---|
1343 | const QRect dirtyBoundingRect(dirtyCopy.boundingRect());
|
---|
1344 |
|
---|
1345 | // Loop through all window surfaces (incl. the top-level surface) and
|
---|
1346 | // repaint those intersecting with the bounding rect of the dirty region.
|
---|
1347 | for (int i = 0; i < surfaceList.size(); ++i) {
|
---|
1348 | QWindowSurface *subSurface = surfaceList.at(i);
|
---|
1349 | QWidget *w = subSurface->window();
|
---|
1350 | QWidgetPrivate *wd = w->d_func();
|
---|
1351 |
|
---|
1352 | const QRect clipRect = wd->clipRect().translated(w->mapTo(tlw, QPoint()));
|
---|
1353 | if (!qRectIntersects(dirtyBoundingRect, clipRect))
|
---|
1354 | continue;
|
---|
1355 |
|
---|
1356 | toClean = dirtyCopy;
|
---|
1357 | BeginPaintInfo beginPaintInfo;
|
---|
1358 | beginPaint(toClean, w, subSurface, &beginPaintInfo);
|
---|
1359 | if (beginPaintInfo.nothingToPaint)
|
---|
1360 | continue;
|
---|
1361 |
|
---|
1362 | if (beginPaintInfo.windowSurfaceRecreated) {
|
---|
1363 | // Eep the window surface has changed. The old one may have been
|
---|
1364 | // deleted, in which case we will segfault on the call to
|
---|
1365 | // painterOffset() below. Use the new window surface instead.
|
---|
1366 | subSurface = w->windowSurface();
|
---|
1367 | }
|
---|
1368 |
|
---|
1369 | int flags = QWidgetPrivate::DrawRecursive;
|
---|
1370 | if (w == tlw)
|
---|
1371 | flags |= QWidgetPrivate::DrawAsRoot;
|
---|
1372 | const QPoint painterOffset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
|
---|
1373 | wd->drawWidget(subSurface->paintDevice(), toClean, painterOffset, flags, 0, this);
|
---|
1374 |
|
---|
1375 | endPaint(toClean, subSurface, &beginPaintInfo);
|
---|
1376 | }
|
---|
1377 | #endif
|
---|
1378 | }
|
---|
1379 |
|
---|
1380 | /*!
|
---|
1381 | Flushes the contents of the backing store into the top-level widget.
|
---|
1382 | If the \a widget is non-zero, the content is flushed to the \a widget.
|
---|
1383 | If the \a surface is non-zero, the content of the \a surface is flushed.
|
---|
1384 | */
|
---|
1385 | void QWidgetBackingStore::flush(QWidget *widget, QWindowSurface *surface)
|
---|
1386 | {
|
---|
1387 | #if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
|
---|
1388 | paintWindowDecoration();
|
---|
1389 | #endif
|
---|
1390 |
|
---|
1391 | if (!dirtyOnScreen.isEmpty()) {
|
---|
1392 | QWidget *target = widget ? widget : tlw;
|
---|
1393 | QWindowSurface *source = surface ? surface : windowSurface;
|
---|
1394 | qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset);
|
---|
1395 | dirtyOnScreen = QRegion();
|
---|
1396 | }
|
---|
1397 |
|
---|
1398 | if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty())
|
---|
1399 | return;
|
---|
1400 |
|
---|
1401 | for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
|
---|
1402 | QWidget *w = dirtyOnScreenWidgets->at(i);
|
---|
1403 | QWidgetPrivate *wd = w->d_func();
|
---|
1404 | Q_ASSERT(wd->needsFlush);
|
---|
1405 | qt_flush(w, *wd->needsFlush, windowSurface, tlw, tlwOffset);
|
---|
1406 | *wd->needsFlush = QRegion();
|
---|
1407 | }
|
---|
1408 | dirtyOnScreenWidgets->clear();
|
---|
1409 | }
|
---|
1410 |
|
---|
1411 | static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra)
|
---|
1412 | {
|
---|
1413 | Q_ASSERT(widget);
|
---|
1414 | if (QApplication::closingDown())
|
---|
1415 | return true;
|
---|
1416 |
|
---|
1417 | if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
|
---|
1418 | return true;
|
---|
1419 |
|
---|
1420 | if (!widget->isVisible() || !widget->updatesEnabled())
|
---|
1421 | return true;
|
---|
1422 |
|
---|
1423 | return false;
|
---|
1424 | }
|
---|
1425 |
|
---|
1426 | /*!
|
---|
1427 | Invalidates the buffer when the widget is resized.
|
---|
1428 | Static areas are never invalidated unless absolutely needed.
|
---|
1429 | */
|
---|
1430 | void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
|
---|
1431 | {
|
---|
1432 | Q_Q(QWidget);
|
---|
1433 | Q_ASSERT(!q->isWindow());
|
---|
1434 | Q_ASSERT(q->parentWidget());
|
---|
1435 |
|
---|
1436 | const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
|
---|
1437 | const bool sizeDecreased = (data.crect.width() < oldSize.width())
|
---|
1438 | || (data.crect.height() < oldSize.height());
|
---|
1439 |
|
---|
1440 | const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
|
---|
1441 | const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
|
---|
1442 | const QRect newWidgetRect(q->rect());
|
---|
1443 | const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
|
---|
1444 |
|
---|
1445 | if (!staticContents || graphicsEffect) {
|
---|
1446 | QRegion staticChildren;
|
---|
1447 | QWidgetBackingStore *bs = 0;
|
---|
1448 | if (offset.isNull() && (bs = maybeBackingStore()))
|
---|
1449 | staticChildren = bs->staticContents(q, oldWidgetRect);
|
---|
1450 | const bool hasStaticChildren = !staticChildren.isEmpty();
|
---|
1451 |
|
---|
1452 | if (hasStaticChildren) {
|
---|
1453 | QRegion dirty(newWidgetRect);
|
---|
1454 | dirty -= staticChildren;
|
---|
1455 | invalidateBuffer(dirty);
|
---|
1456 | } else {
|
---|
1457 | // Entire widget needs repaint.
|
---|
1458 | invalidateBuffer(newWidgetRect);
|
---|
1459 | }
|
---|
1460 |
|
---|
1461 | if (!parentAreaExposed)
|
---|
1462 | return;
|
---|
1463 |
|
---|
1464 | // Invalidate newly exposed area of the parent.
|
---|
1465 | if (!graphicsEffect && extra && extra->hasMask) {
|
---|
1466 | QRegion parentExpose(extra->mask.translated(oldPos));
|
---|
1467 | parentExpose &= QRect(oldPos, oldSize);
|
---|
1468 | if (hasStaticChildren)
|
---|
1469 | parentExpose -= data.crect; // Offset is unchanged, safe to do this.
|
---|
1470 | q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
|
---|
1471 | } else {
|
---|
1472 | if (hasStaticChildren && !graphicsEffect) {
|
---|
1473 | QRegion parentExpose(QRect(oldPos, oldSize));
|
---|
1474 | parentExpose -= data.crect; // Offset is unchanged, safe to do this.
|
---|
1475 | q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
|
---|
1476 | } else {
|
---|
1477 | q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize)));
|
---|
1478 | }
|
---|
1479 | }
|
---|
1480 | return;
|
---|
1481 | }
|
---|
1482 |
|
---|
1483 | // Move static content to its new position.
|
---|
1484 | if (!offset.isNull()) {
|
---|
1485 | if (sizeDecreased) {
|
---|
1486 | const QSize minSize(qMin(oldSize.width(), data.crect.width()),
|
---|
1487 | qMin(oldSize.height(), data.crect.height()));
|
---|
1488 | moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
|
---|
1489 | } else {
|
---|
1490 | moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
|
---|
1491 | }
|
---|
1492 | }
|
---|
1493 |
|
---|
1494 | // Invalidate newly visible area of the widget.
|
---|
1495 | if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
|
---|
1496 | QRegion newVisible(newWidgetRect);
|
---|
1497 | newVisible -= oldWidgetRect;
|
---|
1498 | invalidateBuffer(newVisible);
|
---|
1499 | }
|
---|
1500 |
|
---|
1501 | if (!parentAreaExposed)
|
---|
1502 | return;
|
---|
1503 |
|
---|
1504 | // Invalidate newly exposed area of the parent.
|
---|
1505 | const QRect oldRect(oldPos, oldSize);
|
---|
1506 | if (extra && extra->hasMask) {
|
---|
1507 | QRegion parentExpose(oldRect);
|
---|
1508 | parentExpose &= extra->mask.translated(oldPos);
|
---|
1509 | parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
|
---|
1510 | q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
|
---|
1511 | } else {
|
---|
1512 | QRegion parentExpose(oldRect);
|
---|
1513 | parentExpose -= data.crect;
|
---|
1514 | q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
|
---|
1515 | }
|
---|
1516 | }
|
---|
1517 |
|
---|
1518 | /*!
|
---|
1519 | Invalidates the \a rgn (in widget's coordinates) of the backing store, i.e.
|
---|
1520 | all widgets intersecting with the region will be repainted when the backing store
|
---|
1521 | is synced.
|
---|
1522 |
|
---|
1523 | ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
|
---|
1524 | */
|
---|
1525 | void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
|
---|
1526 | {
|
---|
1527 | Q_Q(QWidget);
|
---|
1528 |
|
---|
1529 | QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
|
---|
1530 | if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty())
|
---|
1531 | return;
|
---|
1532 |
|
---|
1533 | QRegion wrgn(rgn);
|
---|
1534 | wrgn &= clipRect();
|
---|
1535 | if (!graphicsEffect && extra && extra->hasMask)
|
---|
1536 | wrgn &= extra->mask;
|
---|
1537 | if (wrgn.isEmpty())
|
---|
1538 | return;
|
---|
1539 |
|
---|
1540 | tlwExtra->backingStore->markDirty(wrgn, q, false, true);
|
---|
1541 | }
|
---|
1542 |
|
---|
1543 | /*!
|
---|
1544 | This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but
|
---|
1545 | is more efficient as it eliminates QRegion operations/allocations and can
|
---|
1546 | use the rect more precisely for additional cut-offs.
|
---|
1547 |
|
---|
1548 | ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
|
---|
1549 | */
|
---|
1550 | void QWidgetPrivate::invalidateBuffer(const QRect &rect)
|
---|
1551 | {
|
---|
1552 | Q_Q(QWidget);
|
---|
1553 |
|
---|
1554 | QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
|
---|
1555 | if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty())
|
---|
1556 | return;
|
---|
1557 |
|
---|
1558 | QRect wRect(rect);
|
---|
1559 | wRect &= clipRect();
|
---|
1560 | if (wRect.isEmpty())
|
---|
1561 | return;
|
---|
1562 |
|
---|
1563 | if (graphicsEffect || !extra || !extra->hasMask) {
|
---|
1564 | tlwExtra->backingStore->markDirty(wRect, q, false, true);
|
---|
1565 | return;
|
---|
1566 | }
|
---|
1567 |
|
---|
1568 | QRegion wRgn(extra->mask);
|
---|
1569 | wRgn &= wRect;
|
---|
1570 | if (wRgn.isEmpty())
|
---|
1571 | return;
|
---|
1572 |
|
---|
1573 | tlwExtra->backingStore->markDirty(wRgn, q, false, true);
|
---|
1574 | }
|
---|
1575 |
|
---|
1576 | void QWidgetPrivate::repaint_sys(const QRegion &rgn)
|
---|
1577 | {
|
---|
1578 | if (data.in_destructor)
|
---|
1579 | return;
|
---|
1580 |
|
---|
1581 | Q_Q(QWidget);
|
---|
1582 | if (q->testAttribute(Qt::WA_StaticContents)) {
|
---|
1583 | if (!extra)
|
---|
1584 | createExtra();
|
---|
1585 | extra->staticContentsSize = data.crect.size();
|
---|
1586 | }
|
---|
1587 |
|
---|
1588 | QPaintEngine *engine = q->paintEngine();
|
---|
1589 | // QGLWidget does not support partial updates if:
|
---|
1590 | // 1) The context is double buffered
|
---|
1591 | // 2) The context is single buffered and auto-fill background is enabled.
|
---|
1592 | const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL
|
---|
1593 | || engine->type() == QPaintEngine::OpenGL2))
|
---|
1594 | && (usesDoubleBufferedGLContext || q->autoFillBackground());
|
---|
1595 | QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn);
|
---|
1596 |
|
---|
1597 | #ifdef Q_WS_MAC
|
---|
1598 | // No difference between update() and repaint() on the Mac.
|
---|
1599 | update_sys(toBePainted);
|
---|
1600 | return;
|
---|
1601 | #endif
|
---|
1602 |
|
---|
1603 | toBePainted &= clipRect();
|
---|
1604 | clipToEffectiveMask(toBePainted);
|
---|
1605 | if (toBePainted.isEmpty())
|
---|
1606 | return; // Nothing to repaint.
|
---|
1607 |
|
---|
1608 | #ifndef QT_NO_PAINT_DEBUG
|
---|
1609 | bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
|
---|
1610 | #endif
|
---|
1611 |
|
---|
1612 | drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
|
---|
1613 |
|
---|
1614 | #ifndef QT_NO_PAINT_DEBUG
|
---|
1615 | if (flushed)
|
---|
1616 | QWidgetBackingStore::unflushPaint(q, toBePainted);
|
---|
1617 | #endif
|
---|
1618 |
|
---|
1619 | if (!q->testAttribute(Qt::WA_PaintOutsidePaintEvent) && q->paintingActive())
|
---|
1620 | qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");
|
---|
1621 | }
|
---|
1622 |
|
---|
1623 |
|
---|
1624 | QT_END_NAMESPACE
|
---|