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 #include <QtGui/QPaintDevice>
43 #include <QtGui/QPainter>
44 #include <QtGui/QPixmap>
45 #include <QtGui/QWidget>
46 
47 #include "private/qt_x11_p.h"
48 #include "private/qpixmap_x11_p.h"
49 #include "private/qwidget_p.h"
50 #include "qx11info_x11.h"
51 #include "qwindowsurface_x11_p.h"
52 
53 QT_BEGIN_NAMESPACE
54 
55 extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp
56 
57 struct QX11WindowSurfacePrivate
58 {
59     QWidget *widget;
60     QPixmap device;
61 #ifndef QT_NO_XRENDER
62     bool translucentBackground;
63 #endif
64 };
65 
QX11WindowSurface(QWidget * widget)66 QX11WindowSurface::QX11WindowSurface(QWidget *widget)
67     : QWindowSurface(widget), d_ptr(new QX11WindowSurfacePrivate), gc(0)
68 {
69     d_ptr->widget = widget;
70 #ifndef QT_NO_XRENDER
71     d_ptr->translucentBackground = X11->use_xrender
72         && widget->x11Info().depth() == 32;
73 #endif
74 }
75 
76 
~QX11WindowSurface()77 QX11WindowSurface::~QX11WindowSurface()
78 {
79     delete d_ptr;
80     if (gc) {
81         XFreeGC(X11->display, gc);
82         gc = 0;
83     }
84 }
85 
paintDevice()86 QPaintDevice *QX11WindowSurface::paintDevice()
87 {
88     return &d_ptr->device;
89 }
90 
beginPaint(const QRegion & rgn)91 void QX11WindowSurface::beginPaint(const QRegion &rgn)
92 {
93 #ifndef QT_NO_XRENDER
94     Q_ASSERT(!d_ptr->device.isNull());
95 
96     if (d_ptr->translucentBackground) {
97         if (d_ptr->device.depth() != 32)
98             static_cast<QX11PixmapData *>(d_ptr->device.data_ptr().data())->convertToARGB32();
99         ::Picture src = X11->getSolidFill(d_ptr->device.x11Info().screen(), Qt::transparent);
100         ::Picture dst = d_ptr->device.x11PictureHandle();
101         const QVector<QRect> rects = rgn.rects();
102         const int w = d_ptr->device.width();
103         const int h = d_ptr->device.height();
104         for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it)
105             XRenderComposite(X11->display, PictOpSrc, src, 0, dst,
106                              0, 0, w, h, it->x(), it->y(),
107                              it->width(), it->height());
108     }
109 #endif
110 }
111 
flush(QWidget * widget,const QRegion & rgn,const QPoint & offset)112 void QX11WindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
113 {
114     if (d_ptr->device.isNull())
115         return;
116 
117     QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
118     QRegion wrgn(rgn);
119     QRect br = rgn.boundingRect();
120     if (!wOffset.isNull())
121         wrgn.translate(-wOffset);
122     QRect wbr = wrgn.boundingRect();
123 
124     int num;
125     XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
126     if (num <= 0)
127         return;
128 //         qDebug() << "XSetClipRectangles";
129 //         for  (int i = 0; i < num; ++i)
130 //             qDebug() << ' ' << i << rects[i].x << rects[i].x << rects[i].y << rects[i].width << rects[i].height;
131     if (num != 1)
132         XSetClipRectangles(X11->display, gc, 0, 0, rects, num, YXBanded);
133     XCopyArea(X11->display, d_ptr->device.handle(), widget->handle(), gc,
134               br.x() + offset.x(), br.y() + offset.y(), br.width(), br.height(), wbr.x(), wbr.y());
135     if (num != 1)
136         XSetClipMask(X11->display, gc, XNone);
137 }
138 
setGeometry(const QRect & rect)139 void QX11WindowSurface::setGeometry(const QRect &rect)
140 {
141     QWindowSurface::setGeometry(rect);
142 
143     const QSize size = rect.size();
144 
145     if (d_ptr->device.size() == size || size.width() <= 0 || size.height() <= 0)
146         return;
147 #ifndef QT_NO_XRENDER
148     if (d_ptr->translucentBackground) {
149         QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen());
150 
151         QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
152         data->xinfo = d_ptr->widget->x11Info();
153         data->resize(size.width(), size.height());
154         d_ptr->device = QPixmap(data);
155     } else
156 #endif
157     {
158         QPixmap::x11SetDefaultScreen(d_ptr->widget->x11Info().screen());
159 
160         QX11PixmapData *oldData = static_cast<QX11PixmapData *>(d_ptr->device.pixmapData());
161 
162         if (oldData && !(oldData->flags & QX11PixmapData::Uninitialized) && hasStaticContents()) {
163             // Copy the content of the old pixmap into the new one.
164             QX11PixmapData *newData = new QX11PixmapData(QPixmapData::PixmapType);
165             newData->resize(size.width(), size.height());
166             Q_ASSERT(oldData->d == newData->d);
167 
168             QRegion staticRegion(staticContents());
169             // Make sure we're inside the boundaries of the old pixmap.
170             staticRegion &= QRect(0, 0, oldData->w, oldData->h);
171             const QRect boundingRect(staticRegion.boundingRect());
172             const int dx = boundingRect.x();
173             const int dy = boundingRect.y();
174 
175             int num;
176             XRectangle *rects = (XRectangle *)qt_getClipRects(staticRegion, num);
177             GC tmpGc = XCreateGC(X11->display, oldData->hd, 0, 0);
178             XSetClipRectangles(X11->display, tmpGc, 0, 0, rects, num, YXBanded);
179             XCopyArea(X11->display, oldData->hd, newData->hd, tmpGc,
180                       dx, dy, qMin(boundingRect.width(), size.width()),
181                       qMin(boundingRect.height(), size.height()), dx, dy);
182             XFreeGC(X11->display, tmpGc);
183             newData->flags &= ~QX11PixmapData::Uninitialized;
184 
185             d_ptr->device = QPixmap(newData);
186         } else {
187             d_ptr->device = QPixmap(size);
188         }
189     }
190 
191     if (gc) {
192         XFreeGC(X11->display, gc);
193         gc = 0;
194     }
195     if (!d_ptr->device.isNull()) {
196         gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
197         XSetGraphicsExposures(X11->display, gc, False);
198     }
199 }
200 
scroll(const QRegion & area,int dx,int dy)201 bool QX11WindowSurface::scroll(const QRegion &area, int dx, int dy)
202 {
203     QRect rect = area.boundingRect();
204 
205     if (d_ptr->device.isNull())
206         return false;
207 
208     GC gc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
209     XCopyArea(X11->display, d_ptr->device.handle(), d_ptr->device.handle(), gc,
210               rect.x(), rect.y(), rect.width(), rect.height(),
211               rect.x()+dx, rect.y()+dy);
212     XFreeGC(X11->display, gc);
213 
214     return true;
215 }
216 
grabWidget(const QWidget * widget,const QRect & rect) const217 QPixmap QX11WindowSurface::grabWidget(const QWidget *widget,
218                                       const QRect& rect) const
219 {
220     if (!widget || d_ptr->device.isNull())
221         return QPixmap();
222 
223     QRect srcRect;
224 
225     // make sure the rect is inside the widget & clip to widget's rect
226     if (!rect.isEmpty())
227         srcRect = rect & widget->rect();
228     else
229         srcRect = widget->rect();
230 
231     if (srcRect.isEmpty())
232         return QPixmap();
233 
234     // If it's a child widget we have to translate the coordinates
235     if (widget != window())
236         srcRect.translate(widget->mapTo(window(), QPoint(0, 0)));
237 
238     QPixmap::x11SetDefaultScreen(widget->x11Info().screen());
239     QPixmap px(srcRect.width(), srcRect.height());
240 
241     GC tmpGc = XCreateGC(X11->display, d_ptr->device.handle(), 0, 0);
242 
243     // Copy srcRect from the backing store to the new pixmap
244     XSetGraphicsExposures(X11->display, tmpGc, False);
245     XCopyArea(X11->display, d_ptr->device.handle(), px.handle(), tmpGc,
246               srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), 0, 0);
247 
248     XFreeGC(X11->display, tmpGc);
249 
250     return px;
251 }
252 
features() const253 QWindowSurface::WindowSurfaceFeatures QX11WindowSurface::features() const
254 {
255     WindowSurfaceFeatures features = QWindowSurface::PartialUpdates | QWindowSurface::PreservedContents;
256 #ifndef QT_NO_XRENDER
257     if (!d_ptr->translucentBackground)
258         features |= QWindowSurface::StaticContents;
259 #else
260     features |= QWindowSurface::StaticContents;
261 #endif
262     return features;
263 }
264 
265 QT_END_NAMESPACE
266