1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins 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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <QByteArray>
41 #include <QOpenGLContext>
42 
43 #ifdef Q_OS_LINUX
44 #include <sys/ioctl.h>
45 #include <linux/fb.h>
46 #endif
47 #include <private/qmath_p.h>
48 
49 #include "qeglconvenience_p.h"
50 
51 #ifndef EGL_OPENGL_ES3_BIT_KHR
52 #define EGL_OPENGL_ES3_BIT_KHR 0x0040
53 #endif
54 
55 QT_BEGIN_NAMESPACE
56 
q_createConfigAttributesFromFormat(const QSurfaceFormat & format)57 QVector<EGLint> q_createConfigAttributesFromFormat(const QSurfaceFormat &format)
58 {
59     int redSize     = format.redBufferSize();
60     int greenSize   = format.greenBufferSize();
61     int blueSize    = format.blueBufferSize();
62     int alphaSize   = format.alphaBufferSize();
63     int depthSize   = format.depthBufferSize();
64     int stencilSize = format.stencilBufferSize();
65     int sampleCount = format.samples();
66 
67     QVector<EGLint> configAttributes;
68 
69     // Map default, unspecified values (-1) to 0. This is important due to sorting rule #3
70     // in section 3.4.1 of the spec and allows picking a potentially faster 16-bit config
71     // over 32-bit ones when there is no explicit request for the color channel sizes:
72     //
73     // The red/green/blue sizes have a sort priority of 3, so they are sorted by
74     // first. (unless a caveat like SLOW or NON_CONFORMANT is present) The sort order is
75     // Special and described as "by larger _total_ number of color bits.". So EGL will put
76     // 32-bit configs in the list before the 16-bit configs. However, the spec also goes
77     // on to say "If the requested number of bits in attrib_list for a particular
78     // component is 0, then the number of bits for that component is not considered". This
79     // part of the spec also seems to imply that setting the red/green/blue bits to zero
80     // means none of the components are considered and EGL disregards the entire sorting
81     // rule. It then looks to the next highest priority rule, which is
82     // EGL_BUFFER_SIZE. Despite the selection criteria being "AtLeast" for
83     // EGL_BUFFER_SIZE, it's sort order is "smaller" meaning 16-bit configs are put in the
84     // list before 32-bit configs.
85     //
86     // This also means that explicitly specifying a size like 565 will still result in
87     // having larger (888) configs first in the returned list. We need to handle this
88     // ourselves later by manually filtering the list, instead of just blindly taking the
89     // first config from it.
90 
91     configAttributes.append(EGL_RED_SIZE);
92     configAttributes.append(redSize > 0 ? redSize : 0);
93 
94     configAttributes.append(EGL_GREEN_SIZE);
95     configAttributes.append(greenSize > 0 ? greenSize : 0);
96 
97     configAttributes.append(EGL_BLUE_SIZE);
98     configAttributes.append(blueSize > 0 ? blueSize : 0);
99 
100     configAttributes.append(EGL_ALPHA_SIZE);
101     configAttributes.append(alphaSize > 0 ? alphaSize : 0);
102 
103     configAttributes.append(EGL_SAMPLES);
104     configAttributes.append(sampleCount > 0 ? sampleCount : 0);
105 
106     configAttributes.append(EGL_SAMPLE_BUFFERS);
107     configAttributes.append(sampleCount > 0);
108 
109     if (format.renderableType() != QSurfaceFormat::OpenVG) {
110         configAttributes.append(EGL_DEPTH_SIZE);
111         configAttributes.append(depthSize > 0 ? depthSize : 0);
112 
113         configAttributes.append(EGL_STENCIL_SIZE);
114         configAttributes.append(stencilSize > 0 ? stencilSize : 0);
115     } else {
116         // OpenVG needs alpha mask for clipping
117         configAttributes.append(EGL_ALPHA_MASK_SIZE);
118         configAttributes.append(8);
119     }
120 
121     return configAttributes;
122 }
123 
q_reduceConfigAttributes(QVector<EGLint> * configAttributes)124 bool q_reduceConfigAttributes(QVector<EGLint> *configAttributes)
125 {
126     int i = -1;
127     // Reduce the complexity of a configuration request to ask for less
128     // because the previous request did not result in success.  Returns
129     // true if the complexity was reduced, or false if no further
130     // reductions in complexity are possible.
131 
132     i = configAttributes->indexOf(EGL_SWAP_BEHAVIOR);
133     if (i >= 0) {
134         configAttributes->remove(i,2);
135     }
136 
137 #ifdef EGL_VG_ALPHA_FORMAT_PRE_BIT
138     // For OpenVG, we sometimes try to create a surface using a pre-multiplied format. If we can't
139     // find a config which supports pre-multiplied formats, remove the flag on the surface type:
140 
141     i = configAttributes->indexOf(EGL_SURFACE_TYPE);
142     if (i >= 0) {
143         EGLint surfaceType = configAttributes->at(i +1);
144         if (surfaceType & EGL_VG_ALPHA_FORMAT_PRE_BIT) {
145             surfaceType ^= EGL_VG_ALPHA_FORMAT_PRE_BIT;
146             configAttributes->replace(i+1,surfaceType);
147             return true;
148         }
149     }
150 #endif
151 
152     // EGL chooses configs with the highest color depth over
153     // those with smaller (but faster) lower color depths. One
154     // way around this is to set EGL_BUFFER_SIZE to 16, which
155     // trumps the others. Of course, there may not be a 16-bit
156     // config available, so it's the first restraint we remove.
157     i = configAttributes->indexOf(EGL_BUFFER_SIZE);
158     if (i >= 0) {
159         if (configAttributes->at(i+1) == 16) {
160             configAttributes->remove(i,2);
161             return true;
162         }
163     }
164 
165     i = configAttributes->indexOf(EGL_SAMPLES);
166     if (i >= 0) {
167         EGLint value = configAttributes->value(i+1, 0);
168         if (value > 1)
169             configAttributes->replace(i+1, qMin(EGLint(16), value / 2));
170         else
171             configAttributes->remove(i, 2);
172         return true;
173     }
174 
175     i = configAttributes->indexOf(EGL_SAMPLE_BUFFERS);
176     if (i >= 0) {
177         configAttributes->remove(i,2);
178         return true;
179     }
180 
181     i = configAttributes->indexOf(EGL_DEPTH_SIZE);
182     if (i >= 0) {
183         if (configAttributes->at(i + 1) >= 32)
184             configAttributes->replace(i + 1, 24);
185         else if (configAttributes->at(i + 1) > 1)
186             configAttributes->replace(i + 1, 1);
187         else
188             configAttributes->remove(i, 2);
189         return true;
190     }
191 
192     i = configAttributes->indexOf(EGL_ALPHA_SIZE);
193     if (i >= 0) {
194         configAttributes->remove(i,2);
195 #if defined(EGL_BIND_TO_TEXTURE_RGBA) && defined(EGL_BIND_TO_TEXTURE_RGB)
196         i = configAttributes->indexOf(EGL_BIND_TO_TEXTURE_RGBA);
197         if (i >= 0) {
198             configAttributes->replace(i,EGL_BIND_TO_TEXTURE_RGB);
199             configAttributes->replace(i+1,true);
200 
201         }
202 #endif
203         return true;
204     }
205 
206     i = configAttributes->indexOf(EGL_STENCIL_SIZE);
207     if (i >= 0) {
208         if (configAttributes->at(i + 1) > 1)
209             configAttributes->replace(i + 1, 1);
210         else
211             configAttributes->remove(i, 2);
212         return true;
213     }
214 
215 #ifdef EGL_BIND_TO_TEXTURE_RGB
216     i = configAttributes->indexOf(EGL_BIND_TO_TEXTURE_RGB);
217     if (i >= 0) {
218         configAttributes->remove(i,2);
219         return true;
220     }
221 #endif
222 
223     return false;
224 }
225 
QEglConfigChooser(EGLDisplay display)226 QEglConfigChooser::QEglConfigChooser(EGLDisplay display)
227     : m_display(display)
228     , m_surfaceType(EGL_WINDOW_BIT)
229     , m_ignore(false)
230     , m_confAttrRed(0)
231     , m_confAttrGreen(0)
232     , m_confAttrBlue(0)
233     , m_confAttrAlpha(0)
234 {
235 }
236 
~QEglConfigChooser()237 QEglConfigChooser::~QEglConfigChooser()
238 {
239 }
240 
chooseConfig()241 EGLConfig QEglConfigChooser::chooseConfig()
242 {
243     QVector<EGLint> configureAttributes = q_createConfigAttributesFromFormat(m_format);
244     configureAttributes.append(EGL_SURFACE_TYPE);
245     configureAttributes.append(surfaceType());
246 
247     configureAttributes.append(EGL_RENDERABLE_TYPE);
248     bool needsES2Plus = false;
249     switch (m_format.renderableType()) {
250     case QSurfaceFormat::OpenVG:
251         configureAttributes.append(EGL_OPENVG_BIT);
252         break;
253 #ifdef EGL_VERSION_1_4
254     case QSurfaceFormat::DefaultRenderableType:
255 #ifndef QT_NO_OPENGL
256         if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL)
257             configureAttributes.append(EGL_OPENGL_BIT);
258         else
259 #endif // QT_NO_OPENGL
260             needsES2Plus = true;
261         break;
262     case QSurfaceFormat::OpenGL:
263          configureAttributes.append(EGL_OPENGL_BIT);
264          break;
265 #endif
266     case QSurfaceFormat::OpenGLES:
267         if (m_format.majorVersion() == 1) {
268             configureAttributes.append(EGL_OPENGL_ES_BIT);
269             break;
270         }
271         Q_FALLTHROUGH();
272     default:
273         needsES2Plus = true;
274         break;
275     }
276     if (needsES2Plus) {
277         if (m_format.majorVersion() >= 3 && q_hasEglExtension(display(), "EGL_KHR_create_context"))
278             configureAttributes.append(EGL_OPENGL_ES3_BIT_KHR);
279         else
280             configureAttributes.append(EGL_OPENGL_ES2_BIT);
281     }
282     configureAttributes.append(EGL_NONE);
283 
284     EGLConfig cfg = nullptr;
285     do {
286         // Get the number of matching configurations for this set of properties.
287         EGLint matching = 0;
288         if (!eglChooseConfig(display(), configureAttributes.constData(), nullptr, 0, &matching) || !matching)
289             continue;
290 
291         // Fetch all of the matching configurations and find the
292         // first that matches the pixel format we wanted.
293         int i = configureAttributes.indexOf(EGL_RED_SIZE);
294         m_confAttrRed = configureAttributes.at(i+1);
295         i = configureAttributes.indexOf(EGL_GREEN_SIZE);
296         m_confAttrGreen = configureAttributes.at(i+1);
297         i = configureAttributes.indexOf(EGL_BLUE_SIZE);
298         m_confAttrBlue = configureAttributes.at(i+1);
299         i = configureAttributes.indexOf(EGL_ALPHA_SIZE);
300         m_confAttrAlpha = i == -1 ? 0 : configureAttributes.at(i+1);
301 
302         QVector<EGLConfig> configs(matching);
303         eglChooseConfig(display(), configureAttributes.constData(), configs.data(), configs.size(), &matching);
304         if (!cfg && matching > 0)
305             cfg = configs.first();
306 
307         // Filter the list. Due to the EGL sorting rules configs with higher depth are
308         // placed first when the minimum color channel sizes have been specified (i.e. the
309         // QSurfaceFormat contains color sizes > 0). To prevent returning a 888 config
310         // when the QSurfaceFormat explicitly asked for 565, go through the returned
311         // configs and look for one that exactly matches the requested sizes. When no
312         // sizes have been given, take the first, which will be a config with the smaller
313         // (e.g. 16-bit) depth.
314         for (int i = 0; i < configs.size(); ++i) {
315             if (filterConfig(configs[i]))
316                 return configs.at(i);
317         }
318     } while (q_reduceConfigAttributes(&configureAttributes));
319 
320     if (!cfg)
321         qWarning("Cannot find EGLConfig, returning null config");
322     return cfg;
323 }
324 
filterConfig(EGLConfig config) const325 bool QEglConfigChooser::filterConfig(EGLConfig config) const
326 {
327     // If we are fine with the highest depth (e.g. RGB888 configs) even when something
328     // smaller (565) was explicitly requested, do nothing.
329     if (m_ignore)
330         return true;
331 
332     EGLint red = 0;
333     EGLint green = 0;
334     EGLint blue = 0;
335     EGLint alpha = 0;
336 
337     // Compare only if a size was given. Otherwise just accept.
338     if (m_confAttrRed)
339         eglGetConfigAttrib(display(), config, EGL_RED_SIZE, &red);
340     if (m_confAttrGreen)
341         eglGetConfigAttrib(display(), config, EGL_GREEN_SIZE, &green);
342     if (m_confAttrBlue)
343         eglGetConfigAttrib(display(), config, EGL_BLUE_SIZE, &blue);
344     if (m_confAttrAlpha)
345         eglGetConfigAttrib(display(), config, EGL_ALPHA_SIZE, &alpha);
346 
347     return red == m_confAttrRed && green == m_confAttrGreen
348            && blue == m_confAttrBlue && alpha == m_confAttrAlpha;
349 }
350 
q_configFromGLFormat(EGLDisplay display,const QSurfaceFormat & format,bool highestPixelFormat,int surfaceType)351 EGLConfig q_configFromGLFormat(EGLDisplay display, const QSurfaceFormat &format, bool highestPixelFormat, int surfaceType)
352 {
353     QEglConfigChooser chooser(display);
354     chooser.setSurfaceFormat(format);
355     chooser.setSurfaceType(surfaceType);
356     chooser.setIgnoreColorChannels(highestPixelFormat);
357 
358     return chooser.chooseConfig();
359 }
360 
q_glFormatFromConfig(EGLDisplay display,const EGLConfig config,const QSurfaceFormat & referenceFormat)361 QSurfaceFormat q_glFormatFromConfig(EGLDisplay display, const EGLConfig config, const QSurfaceFormat &referenceFormat)
362 {
363     QSurfaceFormat format;
364     EGLint redSize     = 0;
365     EGLint greenSize   = 0;
366     EGLint blueSize    = 0;
367     EGLint alphaSize   = 0;
368     EGLint depthSize   = 0;
369     EGLint stencilSize = 0;
370     EGLint sampleCount = 0;
371     EGLint renderableType = 0;
372 
373     eglGetConfigAttrib(display, config, EGL_RED_SIZE,     &redSize);
374     eglGetConfigAttrib(display, config, EGL_GREEN_SIZE,   &greenSize);
375     eglGetConfigAttrib(display, config, EGL_BLUE_SIZE,    &blueSize);
376     eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE,   &alphaSize);
377     eglGetConfigAttrib(display, config, EGL_DEPTH_SIZE,   &depthSize);
378     eglGetConfigAttrib(display, config, EGL_STENCIL_SIZE, &stencilSize);
379     eglGetConfigAttrib(display, config, EGL_SAMPLES,      &sampleCount);
380     eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType);
381 
382     if (referenceFormat.renderableType() == QSurfaceFormat::OpenVG && (renderableType & EGL_OPENVG_BIT))
383         format.setRenderableType(QSurfaceFormat::OpenVG);
384 #ifdef EGL_VERSION_1_4
385     else if (referenceFormat.renderableType() == QSurfaceFormat::OpenGL
386              && (renderableType & EGL_OPENGL_BIT))
387         format.setRenderableType(QSurfaceFormat::OpenGL);
388     else if (referenceFormat.renderableType() == QSurfaceFormat::DefaultRenderableType
389 #ifndef QT_NO_OPENGL
390              && QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL
391 #endif
392              && (renderableType & EGL_OPENGL_BIT))
393         format.setRenderableType(QSurfaceFormat::OpenGL);
394 #endif
395     else
396         format.setRenderableType(QSurfaceFormat::OpenGLES);
397 
398     format.setRedBufferSize(redSize);
399     format.setGreenBufferSize(greenSize);
400     format.setBlueBufferSize(blueSize);
401     format.setAlphaBufferSize(alphaSize);
402     format.setDepthBufferSize(depthSize);
403     format.setStencilBufferSize(stencilSize);
404     format.setSamples(sampleCount);
405     format.setStereo(false);         // EGL doesn't support stereo buffers
406     format.setSwapInterval(referenceFormat.swapInterval());
407 
408     // Clear the EGL error state because some of the above may
409     // have errored out because the attribute is not applicable
410     // to the surface type.  Such errors don't matter.
411     eglGetError();
412 
413     return format;
414 }
415 
q_hasEglExtension(EGLDisplay display,const char * extensionName)416 bool q_hasEglExtension(EGLDisplay display, const char* extensionName)
417 {
418     QList<QByteArray> extensions =
419         QByteArray(reinterpret_cast<const char *>
420             (eglQueryString(display, EGL_EXTENSIONS))).split(' ');
421     return extensions.contains(extensionName);
422 }
423 
424 struct AttrInfo { EGLint attr; const char *name; };
425 static struct AttrInfo attrs[] = {
426     {EGL_BUFFER_SIZE, "EGL_BUFFER_SIZE"},
427     {EGL_ALPHA_SIZE, "EGL_ALPHA_SIZE"},
428     {EGL_BLUE_SIZE, "EGL_BLUE_SIZE"},
429     {EGL_GREEN_SIZE, "EGL_GREEN_SIZE"},
430     {EGL_RED_SIZE, "EGL_RED_SIZE"},
431     {EGL_DEPTH_SIZE, "EGL_DEPTH_SIZE"},
432     {EGL_STENCIL_SIZE, "EGL_STENCIL_SIZE"},
433     {EGL_CONFIG_CAVEAT, "EGL_CONFIG_CAVEAT"},
434     {EGL_CONFIG_ID, "EGL_CONFIG_ID"},
435     {EGL_LEVEL, "EGL_LEVEL"},
436     {EGL_MAX_PBUFFER_HEIGHT, "EGL_MAX_PBUFFER_HEIGHT"},
437     {EGL_MAX_PBUFFER_PIXELS, "EGL_MAX_PBUFFER_PIXELS"},
438     {EGL_MAX_PBUFFER_WIDTH, "EGL_MAX_PBUFFER_WIDTH"},
439     {EGL_NATIVE_RENDERABLE, "EGL_NATIVE_RENDERABLE"},
440     {EGL_NATIVE_VISUAL_ID, "EGL_NATIVE_VISUAL_ID"},
441     {EGL_NATIVE_VISUAL_TYPE, "EGL_NATIVE_VISUAL_TYPE"},
442     {EGL_SAMPLES, "EGL_SAMPLES"},
443     {EGL_SAMPLE_BUFFERS, "EGL_SAMPLE_BUFFERS"},
444     {EGL_SURFACE_TYPE, "EGL_SURFACE_TYPE"},
445     {EGL_TRANSPARENT_TYPE, "EGL_TRANSPARENT_TYPE"},
446     {EGL_TRANSPARENT_BLUE_VALUE, "EGL_TRANSPARENT_BLUE_VALUE"},
447     {EGL_TRANSPARENT_GREEN_VALUE, "EGL_TRANSPARENT_GREEN_VALUE"},
448     {EGL_TRANSPARENT_RED_VALUE, "EGL_TRANSPARENT_RED_VALUE"},
449     {EGL_BIND_TO_TEXTURE_RGB, "EGL_BIND_TO_TEXTURE_RGB"},
450     {EGL_BIND_TO_TEXTURE_RGBA, "EGL_BIND_TO_TEXTURE_RGBA"},
451     {EGL_MIN_SWAP_INTERVAL, "EGL_MIN_SWAP_INTERVAL"},
452     {EGL_MAX_SWAP_INTERVAL, "EGL_MAX_SWAP_INTERVAL"},
453     {-1, nullptr}};
454 
q_printEglConfig(EGLDisplay display,EGLConfig config)455 void q_printEglConfig(EGLDisplay display, EGLConfig config)
456 {
457     EGLint index;
458     for (index = 0; attrs[index].attr != -1; ++index) {
459         EGLint value;
460         if (eglGetConfigAttrib(display, config, attrs[index].attr, &value)) {
461             qDebug("\t%s: %d", attrs[index].name, (int)value);
462         }
463     }
464 }
465 
466 #ifdef Q_OS_UNIX
467 
q_physicalScreenSizeFromFb(int framebufferDevice,const QSize & screenSize)468 QSizeF q_physicalScreenSizeFromFb(int framebufferDevice, const QSize &screenSize)
469 {
470 #ifndef Q_OS_LINUX
471     Q_UNUSED(framebufferDevice)
472 #endif
473     const int defaultPhysicalDpi = 100;
474     static QSizeF size;
475 
476     if (size.isEmpty()) {
477         // Note: in millimeters
478         int width = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_WIDTH");
479         int height = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_HEIGHT");
480 
481         if (width && height) {
482             size.setWidth(width);
483             size.setHeight(height);
484             return size;
485         }
486 
487         int w = -1;
488         int h = -1;
489         QSize screenResolution;
490 #ifdef Q_OS_LINUX
491         struct fb_var_screeninfo vinfo;
492 
493         if (framebufferDevice != -1) {
494             if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, &vinfo) == -1) {
495                 qWarning("eglconvenience: Could not query screen info");
496             } else {
497                 w = vinfo.width;
498                 h = vinfo.height;
499                 screenResolution = QSize(vinfo.xres, vinfo.yres);
500             }
501         } else
502 #endif
503         {
504             // Use the provided screen size, when available, since some platforms may have their own
505             // specific way to query it. Otherwise try querying it from the framebuffer.
506             screenResolution = screenSize.isEmpty() ? q_screenSizeFromFb(framebufferDevice) : screenSize;
507         }
508 
509         size.setWidth(w <= 0 ? screenResolution.width() * Q_MM_PER_INCH / defaultPhysicalDpi : qreal(w));
510         size.setHeight(h <= 0 ? screenResolution.height() * Q_MM_PER_INCH / defaultPhysicalDpi : qreal(h));
511 
512         if (w <= 0 || h <= 0)
513             qWarning("Unable to query physical screen size, defaulting to %d dpi.\n"
514                      "To override, set QT_QPA_EGLFS_PHYSICAL_WIDTH "
515                      "and QT_QPA_EGLFS_PHYSICAL_HEIGHT (in millimeters).", defaultPhysicalDpi);
516     }
517 
518     return size;
519 }
520 
q_screenSizeFromFb(int framebufferDevice)521 QSize q_screenSizeFromFb(int framebufferDevice)
522 {
523 #ifndef Q_OS_LINUX
524     Q_UNUSED(framebufferDevice)
525 #endif
526     const int defaultWidth = 800;
527     const int defaultHeight = 600;
528     static QSize size;
529 
530     if (size.isEmpty()) {
531         int width = qEnvironmentVariableIntValue("QT_QPA_EGLFS_WIDTH");
532         int height = qEnvironmentVariableIntValue("QT_QPA_EGLFS_HEIGHT");
533 
534         if (width && height) {
535             size.setWidth(width);
536             size.setHeight(height);
537             return size;
538         }
539 
540 #ifdef Q_OS_LINUX
541         struct fb_var_screeninfo vinfo;
542         int xres = -1;
543         int yres = -1;
544 
545         if (framebufferDevice != -1) {
546             if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, &vinfo) == -1) {
547                 qWarning("eglconvenience: Could not read screen info");
548             } else {
549                 xres = vinfo.xres;
550                 yres = vinfo.yres;
551             }
552         }
553 
554         size.setWidth(xres <= 0 ? defaultWidth : xres);
555         size.setHeight(yres <= 0 ? defaultHeight : yres);
556 #else
557         size.setWidth(defaultWidth);
558         size.setHeight(defaultHeight);
559 #endif
560     }
561 
562     return size;
563 }
564 
q_screenDepthFromFb(int framebufferDevice)565 int q_screenDepthFromFb(int framebufferDevice)
566 {
567 #ifndef Q_OS_LINUX
568     Q_UNUSED(framebufferDevice)
569 #endif
570     const int defaultDepth = 32;
571     static int depth = qEnvironmentVariableIntValue("QT_QPA_EGLFS_DEPTH");
572 
573     if (depth == 0) {
574 #ifdef Q_OS_LINUX
575         struct fb_var_screeninfo vinfo;
576 
577         if (framebufferDevice != -1) {
578             if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, &vinfo) == -1)
579                 qWarning("eglconvenience: Could not query screen info");
580             else
581                 depth = vinfo.bits_per_pixel;
582         }
583 
584         if (depth <= 0)
585             depth = defaultDepth;
586 #else
587         depth = defaultDepth;
588 #endif
589     }
590 
591     return depth;
592 }
593 
q_refreshRateFromFb(int framebufferDevice)594 qreal q_refreshRateFromFb(int framebufferDevice)
595 {
596 #ifndef Q_OS_LINUX
597     Q_UNUSED(framebufferDevice)
598 #endif
599 
600     static qreal rate = 0;
601 
602 #ifdef Q_OS_LINUX
603     if (rate == 0) {
604         if (framebufferDevice != -1) {
605             struct fb_var_screeninfo vinfo;
606             if (ioctl(framebufferDevice, FBIOGET_VSCREENINFO, &vinfo) != -1) {
607                 const quint64 quot = quint64(vinfo.left_margin + vinfo.right_margin + vinfo.xres + vinfo.hsync_len)
608                     * quint64(vinfo.upper_margin + vinfo.lower_margin + vinfo.yres + vinfo.vsync_len)
609                     * vinfo.pixclock;
610                 if (quot)
611                     rate = 1000000000000LLU / quot;
612             } else {
613                 qWarning("eglconvenience: Could not query screen info");
614             }
615         }
616     }
617 #endif
618 
619     if (rate == 0)
620         rate = 60;
621 
622     return rate;
623 }
624 
625 #endif // Q_OS_UNIX
626 
627 QT_END_NAMESPACE
628