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