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