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