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 <QtCore/qdebug.h>
43 
44 #include <QtGui/private/qt_x11_p.h>
45 #include <QtGui/qx11info_x11.h>
46 #include <QtGui/private/qpixmapdata_p.h>
47 #include <QtGui/private/qpixmap_x11_p.h>
48 #include <QtGui/private/qimagepixmapcleanuphooks_p.h>
49 
50 #include <QtGui/qpaintdevice.h>
51 #include <QtGui/qpixmap.h>
52 #include <QtGui/qwidget.h>
53 #include <QtGui/qcolormap.h>
54 
55 #include "QtGui/private/qegl_p.h"
56 #include "QtGui/private/qeglcontext_p.h"
57 
58 QT_BEGIN_NAMESPACE
59 
60 
nativeDisplay()61 EGLNativeDisplayType QEgl::nativeDisplay()
62 {
63     Display *xdpy = QX11Info::display();
64     if (!xdpy) {
65         qWarning("QEglContext::getDisplay(): X11 display is not open");
66         return EGLNativeDisplayType(EGL_DEFAULT_DISPLAY);
67     }
68     return EGLNativeDisplayType(xdpy);
69 }
70 
nativeWindow(QWidget * widget)71 EGLNativeWindowType QEgl::nativeWindow(QWidget* widget)
72 {
73     return (EGLNativeWindowType)(widget->winId());
74 }
75 
nativePixmap(QPixmap * pixmap)76 EGLNativePixmapType QEgl::nativePixmap(QPixmap* pixmap)
77 {
78     return (EGLNativePixmapType)(pixmap->handle());
79 }
80 
countBits(unsigned long mask)81 static int countBits(unsigned long mask)
82 {
83     int count = 0;
84     while (mask != 0) {
85         if (mask & 1)
86             ++count;
87         mask >>= 1;
88     }
89     return count;
90 }
91 
92 // Set the pixel format parameters from the visual in "xinfo".
setVisualFormat(const QX11Info * xinfo)93 void QEglProperties::setVisualFormat(const QX11Info *xinfo)
94 {
95     if (!xinfo)
96         return;
97     Visual *visual = (Visual*)xinfo->visual();
98     if (!visual)
99         return;
100     if (visual->c_class != TrueColor && visual->c_class != DirectColor)
101         return;
102     setValue(EGL_RED_SIZE, countBits(visual->red_mask));
103     setValue(EGL_GREEN_SIZE, countBits(visual->green_mask));
104     setValue(EGL_BLUE_SIZE, countBits(visual->blue_mask));
105 
106     EGLint alphaBits = 0;
107 #if !defined(QT_NO_XRENDER)
108     XRenderPictFormat *format;
109     format = XRenderFindVisualFormat(xinfo->display(), visual);
110     if (format && (format->type == PictTypeDirect) && format->direct.alphaMask) {
111         alphaBits = countBits(format->direct.alphaMask);
112         qDebug("QEglProperties::setVisualFormat() - visual's alphaMask is %d", alphaBits);
113     }
114 #endif
115     setValue(EGL_ALPHA_SIZE, alphaBits);
116 }
117 
118 extern const QX11Info *qt_x11Info(const QPaintDevice *pd);
119 
120 // Set pixel format and other properties based on a paint device.
setPaintDeviceFormat(QPaintDevice * dev)121 void QEglProperties::setPaintDeviceFormat(QPaintDevice *dev)
122 {
123     if (!dev)
124         return;
125     if (dev->devType() == QInternal::Image)
126         setPixelFormat(static_cast<QImage *>(dev)->format());
127     else
128         setVisualFormat(qt_x11Info(dev));
129 }
130 
131 //#define QT_DEBUG_X11_VISUAL_SELECTION 1
132 
getCompatibleVisualId(EGLConfig config)133 VisualID QEgl::getCompatibleVisualId(EGLConfig config)
134 {
135     VisualID    visualId = 0;
136     EGLint      eglValue = 0;
137 
138     EGLint configRedSize = 0;
139     eglGetConfigAttrib(display(), config, EGL_RED_SIZE, &configRedSize);
140 
141     EGLint configGreenSize = 0;
142     eglGetConfigAttrib(display(), config, EGL_GREEN_SIZE, &configGreenSize);
143 
144     EGLint configBlueSize = 0;
145     eglGetConfigAttrib(display(), config, EGL_BLUE_SIZE, &configBlueSize);
146 
147     EGLint configAlphaSize = 0;
148     eglGetConfigAttrib(display(), config, EGL_ALPHA_SIZE, &configAlphaSize);
149 
150     eglGetConfigAttrib(display(), config, EGL_CONFIG_ID, &eglValue);
151     int configId = eglValue;
152 
153     // See if EGL provided a valid VisualID:
154     eglGetConfigAttrib(display(), config, EGL_NATIVE_VISUAL_ID, &eglValue);
155     visualId = (VisualID)eglValue;
156     if (visualId) {
157         // EGL has suggested a visual id, so get the rest of the visual info for that id:
158         XVisualInfo visualInfoTemplate;
159         memset(&visualInfoTemplate, 0, sizeof(XVisualInfo));
160         visualInfoTemplate.visualid = visualId;
161 
162         XVisualInfo *chosenVisualInfo;
163         int matchingCount = 0;
164         chosenVisualInfo = XGetVisualInfo(X11->display, VisualIDMask, &visualInfoTemplate, &matchingCount);
165         if (chosenVisualInfo) {
166             // Skip size checks if implementation supports non-matching visual
167             // and config (http://bugreports.qt-project.org/browse/QTBUG-9444).
168             if (QEgl::hasExtension("EGL_NV_post_convert_rounding")) {
169                 XFree(chosenVisualInfo);
170                 return visualId;
171             }
172 
173             int visualRedSize = countBits(chosenVisualInfo->red_mask);
174             int visualGreenSize = countBits(chosenVisualInfo->green_mask);
175             int visualBlueSize = countBits(chosenVisualInfo->blue_mask);
176             int visualAlphaSize = -1; // Need XRender to tell us the alpha channel size
177 
178 #if !defined(QT_NO_XRENDER)
179             if (X11->use_xrender) {
180                 // If we have XRender, actually check the visual supplied by EGL is ARGB
181                 XRenderPictFormat *format;
182                 format = XRenderFindVisualFormat(X11->display, chosenVisualInfo->visual);
183                 if (format && (format->type == PictTypeDirect))
184                     visualAlphaSize = countBits(format->direct.alphaMask);
185             }
186 #endif
187 
188             bool visualMatchesConfig = false;
189             if ( visualRedSize == configRedSize &&
190                  visualGreenSize == configGreenSize &&
191                  visualBlueSize == configBlueSize )
192             {
193                 // We need XRender to check the alpha channel size of the visual. If we don't have
194                 // the alpha size, we don't check it against the EGL config's alpha size.
195                 if (visualAlphaSize >= 0)
196                     visualMatchesConfig = visualAlphaSize == configAlphaSize;
197                 else
198                     visualMatchesConfig = true;
199             }
200 
201             if (!visualMatchesConfig) {
202                 if (visualAlphaSize >= 0) {
203                     qWarning("Warning: EGL suggested using X Visual ID %d (ARGB%d%d%d%d) for EGL config %d (ARGB%d%d%d%d), but this is incompatable",
204                              (int)visualId, visualAlphaSize, visualRedSize, visualGreenSize, visualBlueSize,
205                              configId, configAlphaSize, configRedSize, configGreenSize, configBlueSize);
206                 } else {
207                     qWarning("Warning: EGL suggested using X Visual ID %d (RGB%d%d%d) for EGL config %d (RGB%d%d%d), but this is incompatable",
208                              (int)visualId, visualRedSize, visualGreenSize, visualBlueSize,
209                              configId, configRedSize, configGreenSize, configBlueSize);
210                 }
211                 visualId = 0;
212             }
213         } else {
214             qWarning("Warning: EGL suggested using X Visual ID %d for EGL config %d, but that isn't a valid ID",
215                      (int)visualId, configId);
216             visualId = 0;
217         }
218         XFree(chosenVisualInfo);
219     }
220 #ifdef QT_DEBUG_X11_VISUAL_SELECTION
221     else
222         qDebug("EGL did not suggest a VisualID (EGL_NATIVE_VISUAL_ID was zero) for EGLConfig %d", configId);
223 #endif
224 
225     if (visualId) {
226 #ifdef QT_DEBUG_X11_VISUAL_SELECTION
227         if (configAlphaSize > 0)
228             qDebug("Using ARGB Visual ID %d provided by EGL for config %d", (int)visualId, configId);
229         else
230             qDebug("Using Opaque Visual ID %d provided by EGL for config %d", (int)visualId, configId);
231 #endif
232         return visualId;
233     }
234 
235 
236     // If EGL didn't give us a valid visual ID, try XRender
237 #if !defined(QT_NO_XRENDER)
238     if (!visualId && X11->use_xrender) {
239         XVisualInfo visualInfoTemplate;
240         memset(&visualInfoTemplate, 0, sizeof(XVisualInfo));
241 
242         visualInfoTemplate.c_class = TrueColor;
243 
244         XVisualInfo *matchingVisuals;
245         int matchingCount = 0;
246         matchingVisuals = XGetVisualInfo(X11->display,
247                                          VisualClassMask,
248                                          &visualInfoTemplate,
249                                          &matchingCount);
250 
251         for (int i = 0; i < matchingCount; ++i) {
252             XRenderPictFormat *format;
253             format = XRenderFindVisualFormat(X11->display, matchingVisuals[i].visual);
254 
255             // Check the format for the visual matches the EGL config
256             if ( (countBits(format->direct.redMask) == configRedSize) &&
257                  (countBits(format->direct.greenMask) == configGreenSize) &&
258                  (countBits(format->direct.blueMask) == configBlueSize) &&
259                  (countBits(format->direct.alphaMask) == configAlphaSize) )
260             {
261                 visualId = matchingVisuals[i].visualid;
262                 break;
263             }
264         }
265         if (matchingVisuals)
266             XFree(matchingVisuals);
267 
268     }
269     if (visualId) {
270 # ifdef QT_DEBUG_X11_VISUAL_SELECTION
271         if (configAlphaSize > 0)
272             qDebug("Using ARGB Visual ID %d provided by XRender for EGL config %d", (int)visualId, configId);
273         else
274             qDebug("Using Opaque Visual ID %d provided by XRender for EGL config %d", (int)visualId, configId);
275 # endif // QT_DEBUG_X11_VISUAL_SELECTION
276         return visualId;
277     }
278 # ifdef QT_DEBUG_X11_VISUAL_SELECTION
279     else
280         qDebug("Failed to find an XVisual which matches EGL config %d using XRender", configId);
281 # endif // QT_DEBUG_X11_VISUAL_SELECTION
282 
283 #endif //!defined(QT_NO_XRENDER)
284 
285 
286     // Finally, if XRender also failed to find a visual (or isn't present), try to
287     // use XGetVisualInfo and only use the bit depths to match on:
288     if (!visualId) {
289         XVisualInfo visualInfoTemplate;
290         memset(&visualInfoTemplate, 0, sizeof(XVisualInfo));
291         XVisualInfo *matchingVisuals;
292         int matchingCount = 0;
293 
294         visualInfoTemplate.depth = configRedSize + configGreenSize + configBlueSize + configAlphaSize;
295         matchingVisuals = XGetVisualInfo(X11->display,
296                                          VisualDepthMask,
297                                          &visualInfoTemplate,
298                                          &matchingCount);
299         if (!matchingVisuals) {
300             // Try again without taking the alpha channel into account:
301             visualInfoTemplate.depth = configRedSize + configGreenSize + configBlueSize;
302             matchingVisuals = XGetVisualInfo(X11->display,
303                                              VisualDepthMask,
304                                              &visualInfoTemplate,
305                                              &matchingCount);
306         }
307 
308         if (matchingVisuals) {
309             visualId = matchingVisuals[0].visualid;
310             XFree(matchingVisuals);
311         }
312     }
313 
314     if (visualId) {
315 #ifdef QT_DEBUG_X11_VISUAL_SELECTION
316         qDebug("Using Visual ID %d provided by XGetVisualInfo for EGL config %d", (int)visualId, configId);
317 #endif
318         return visualId;
319     }
320 
321     qWarning("Unable to find an X11 visual which matches EGL config %d", configId);
322     return (VisualID)0;
323 }
324 
qt_set_winid_on_widget(QWidget * w,Qt::HANDLE id)325 void qt_set_winid_on_widget(QWidget* w, Qt::HANDLE id)
326 {
327     w->create(id);
328 }
329 
330 
331 // NOTE: The X11 version of createSurface will re-create the native drawable if it's visual doesn't
332 // match the one for the passed in EGLConfig
createSurface(QPaintDevice * device,EGLConfig config,const QEglProperties * properties)333 EGLSurface QEgl::createSurface(QPaintDevice *device, EGLConfig config, const QEglProperties *properties)
334 {
335     int devType = device->devType();
336 
337     if (devType == QInternal::Pbuffer) {
338         // TODO
339         return EGL_NO_SURFACE;
340     }
341 
342     QX11PixmapData *x11PixmapData = 0;
343     if (devType == QInternal::Pixmap) {
344         QPixmapData *pmd = static_cast<QPixmap*>(device)->data_ptr().data();
345         if (pmd->classId() == QPixmapData::X11Class)
346             x11PixmapData = static_cast<QX11PixmapData*>(pmd);
347         else {
348             // TODO: Replace the pixmap's data with a new QX11PixmapData
349             qWarning("WARNING: Creating an EGL surface on a QPixmap is only supported for QX11PixmapData");
350             return EGL_NO_SURFACE;
351         }
352     } else if ((devType != QInternal::Widget) && (devType != QInternal::Pbuffer)) {
353         qWarning("WARNING: Creating an EGLSurface for device type %d isn't supported", devType);
354         return EGL_NO_SURFACE;
355     }
356 
357     VisualID visualId = QEgl::getCompatibleVisualId(config);
358     EGLint alphaSize;
359     eglGetConfigAttrib(QEgl::display(), config, EGL_ALPHA_SIZE, &alphaSize);
360 
361     if (devType == QInternal::Widget) {
362         QWidget *widget = static_cast<QWidget*>(device);
363 
364         VisualID currentVisualId = 0;
365         if (widget->testAttribute(Qt::WA_WState_Created))
366             currentVisualId = XVisualIDFromVisual((Visual*)widget->x11Info().visual());
367 
368         if (currentVisualId != visualId) {
369             // The window is either not created or has the wrong visual. Either way, we need
370             // to create a window with the correct visual and call create() on the widget:
371 
372             bool visible = widget->isVisible();
373             if (visible)
374                 widget->hide();
375 
376             XVisualInfo visualInfo;
377             visualInfo.visualid = visualId;
378             {
379                 XVisualInfo *visualInfoPtr;
380                 int matchingCount = 0;
381                 visualInfoPtr = XGetVisualInfo(widget->x11Info().display(), VisualIDMask,
382                                                &visualInfo, &matchingCount);
383                 Q_ASSERT(visualInfoPtr); // visualId really should be valid!
384                 visualInfo = *visualInfoPtr;
385                 XFree(visualInfoPtr);
386             }
387 
388             Window parentWindow = RootWindow(widget->x11Info().display(), widget->x11Info().screen());
389             if (widget->parentWidget())
390                 parentWindow = widget->parentWidget()->winId();
391 
392             XSetWindowAttributes windowAttribs;
393             QColormap colmap = QColormap::instance(widget->x11Info().screen());
394             windowAttribs.background_pixel = colmap.pixel(widget->palette().color(widget->backgroundRole()));
395             windowAttribs.border_pixel = colmap.pixel(Qt::black);
396 
397             unsigned int valueMask = CWBackPixel|CWBorderPixel;
398             if (alphaSize > 0) {
399                 windowAttribs.colormap = XCreateColormap(widget->x11Info().display(), parentWindow,
400                                                          visualInfo.visual, AllocNone);
401                 valueMask |= CWColormap;
402             }
403 
404             Window window = XCreateWindow(widget->x11Info().display(), parentWindow,
405                                           widget->x(), widget->y(), widget->width(), widget->height(),
406                                           0, visualInfo.depth, InputOutput, visualInfo.visual,
407                                           valueMask, &windowAttribs);
408 
409             // This is a nasty hack to get round the fact that we can't be a friend of QWidget:
410             qt_set_winid_on_widget(widget, window);
411 
412             if (visible)
413                 widget->show();
414         }
415 
416         // At this point, the widget's window should be created and have the correct visual. Now we
417         // just need to create the EGL surface for it:
418         const int *props;
419         if (properties)
420             props = properties->properties();
421         else
422             props = 0;
423         EGLSurface surf = eglCreateWindowSurface(QEgl::display(), config, (EGLNativeWindowType)widget->winId(), props);
424         if (surf == EGL_NO_SURFACE)
425             qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError());
426         return surf;
427     }
428 
429     if (x11PixmapData) {
430         // X11 Pixmaps are only created with a depth, so that's all we need to check
431         EGLint configDepth;
432         eglGetConfigAttrib(QEgl::display(), config, EGL_BUFFER_SIZE , &configDepth);
433         if (x11PixmapData->depth() != configDepth) {
434             // The bit depths are wrong which means the EGLConfig isn't compatable with
435             // this pixmap. So we need to replace the pixmap's existing data with a new
436             // one which is created with the correct depth:
437 
438 #ifndef QT_NO_XRENDER
439             if (configDepth == 32) {
440                 qWarning("Warning: EGLConfig's depth (32) != pixmap's depth (%d), converting to ARGB32",
441                          x11PixmapData->depth());
442                 x11PixmapData->convertToARGB32(true);
443             } else
444 #endif
445             {
446                 qWarning("Warning: EGLConfig's depth (%d) != pixmap's depth (%d)",
447                          configDepth, x11PixmapData->depth());
448             }
449         }
450 
451         QEglProperties surfaceAttribs;
452 
453         // If the pixmap can't be bound to a texture, it's pretty useless
454         surfaceAttribs.setValue(EGL_TEXTURE_TARGET, EGL_TEXTURE_2D);
455         if (alphaSize > 0)
456             surfaceAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGBA);
457         else
458             surfaceAttribs.setValue(EGL_TEXTURE_FORMAT, EGL_TEXTURE_RGB);
459 
460         EGLSurface surf = eglCreatePixmapSurface(QEgl::display(), config,
461                                                  (EGLNativePixmapType) x11PixmapData->handle(),
462                                                  surfaceAttribs.properties());
463         x11PixmapData->gl_surface = (void*)surf;
464         QImagePixmapCleanupHooks::enableCleanupHooks(x11PixmapData);
465         return surf;
466     }
467 
468     return EGL_NO_SURFACE;
469 }
470 
471 QT_END_NAMESPACE
472