1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: sw=2 ts=8 et :
3  */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5  * License, v. 2.0. If a copy of the MPL was not distributed with this
6  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 
8 //////////////////////////////////////////////////////////////////////////////
9 //
10 // Explanation: See bug 639842. Safely getting GL driver info on X11 is hard,
11 // because the only way to do that is to create a GL context and call
12 // glGetString(), but with bad drivers, just creating a GL context may crash.
13 //
14 // This file implements the idea to do that in a separate process.
15 //
16 // The only non-static function here is fire_glxtest_process(). It creates a
17 // pipe, publishes its 'read' end as the mozilla::widget::glxtest_pipe global
18 // variable, forks, and runs that GLX probe in the child process, which runs the
19 // childgltest() static function. This creates a X connection, a GLX context,
20 // calls glGetString, and writes that to the 'write' end of the pipe.
21 
22 #include <cstdio>
23 #include <cstdlib>
24 #include <dlfcn.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 
28 #include "mozilla/Unused.h"
29 #include "nsAppRunner.h"  // for IsWaylandEnabled on IsX11EGLEnabled
30 #include "stdint.h"
31 
32 #ifdef __SUNPRO_CC
33 #  include <stdio.h>
34 #endif
35 
36 #ifdef MOZ_X11
37 #  include "X11/Xlib.h"
38 #  include "X11/Xutil.h"
39 #  include <X11/extensions/Xrandr.h>
40 #endif
41 
42 #ifdef MOZ_WAYLAND
43 #  include "mozilla/widget/mozwayland.h"
44 #  include "mozilla/widget/xdg-output-unstable-v1-client-protocol.h"
45 #endif
46 
47 #ifdef MOZ_X11
48 // stuff from glx.h
49 typedef struct __GLXcontextRec* GLXContext;
50 typedef XID GLXPixmap;
51 typedef XID GLXDrawable;
52 /* GLX 1.3 and later */
53 typedef struct __GLXFBConfigRec* GLXFBConfig;
54 typedef XID GLXFBConfigID;
55 typedef XID GLXContextID;
56 typedef XID GLXWindow;
57 typedef XID GLXPbuffer;
58 #  define GLX_RGBA 4
59 #  define GLX_RED_SIZE 8
60 #  define GLX_GREEN_SIZE 9
61 #  define GLX_BLUE_SIZE 10
62 #  define GLX_DOUBLEBUFFER 5
63 #endif
64 
65 // stuff from gl.h
66 typedef uint8_t GLubyte;
67 typedef uint32_t GLenum;
68 #define GL_VENDOR 0x1F00
69 #define GL_RENDERER 0x1F01
70 #define GL_VERSION 0x1F02
71 
72 // GLX_MESA_query_renderer
73 // clang-format off
74 #define GLX_RENDERER_VENDOR_ID_MESA                            0x8183
75 #define GLX_RENDERER_DEVICE_ID_MESA                            0x8184
76 #define GLX_RENDERER_VERSION_MESA                              0x8185
77 #define GLX_RENDERER_ACCELERATED_MESA                          0x8186
78 #define GLX_RENDERER_VIDEO_MEMORY_MESA                         0x8187
79 #define GLX_RENDERER_UNIFIED_MEMORY_ARCHITECTURE_MESA          0x8188
80 #define GLX_RENDERER_PREFERRED_PROFILE_MESA                    0x8189
81 #define GLX_RENDERER_OPENGL_CORE_PROFILE_VERSION_MESA          0x818A
82 #define GLX_RENDERER_OPENGL_COMPATIBILITY_PROFILE_VERSION_MESA 0x818B
83 #define GLX_RENDERER_OPENGL_ES_PROFILE_VERSION_MESA            0x818C
84 #define GLX_RENDERER_OPENGL_ES2_PROFILE_VERSION_MESA           0x818D
85 #define GLX_RENDERER_ID_MESA                                   0x818E
86 // clang-format on
87 
88 // stuff from egl.h
89 typedef intptr_t EGLAttrib;
90 typedef int EGLBoolean;
91 typedef void* EGLConfig;
92 typedef void* EGLContext;
93 typedef void* EGLDeviceEXT;
94 typedef void* EGLDisplay;
95 typedef int EGLint;
96 typedef void* EGLNativeDisplayType;
97 typedef void* EGLSurface;
98 typedef void* (*PFNEGLGETPROCADDRESS)(const char*);
99 
100 #define EGL_NO_CONTEXT nullptr
101 #define EGL_NO_SURFACE nullptr
102 #define EGL_FALSE 0
103 #define EGL_TRUE 1
104 #define EGL_BLUE_SIZE 0x3022
105 #define EGL_GREEN_SIZE 0x3023
106 #define EGL_RED_SIZE 0x3024
107 #define EGL_NONE 0x3038
108 #define EGL_VENDOR 0x3053
109 #define EGL_EXTENSIONS 0x3055
110 #define EGL_CONTEXT_CLIENT_VERSION 0x3098
111 #define EGL_OPENGL_API 0x30A2
112 #define EGL_DEVICE_EXT 0x322C
113 #define EGL_DRM_DEVICE_FILE_EXT 0x3233
114 #define EGL_DRM_RENDER_NODE_FILE_EXT 0x3377
115 
116 // stuff from xf86drm.h
117 #define DRM_NODE_RENDER 2
118 #define DRM_NODE_MAX 3
119 
120 typedef struct _drmPciDeviceInfo {
121   uint16_t vendor_id;
122   uint16_t device_id;
123   uint16_t subvendor_id;
124   uint16_t subdevice_id;
125   uint8_t revision_id;
126 } drmPciDeviceInfo, *drmPciDeviceInfoPtr;
127 
128 typedef struct _drmDevice {
129   char** nodes;
130   int available_nodes;
131   int bustype;
132   union {
133     void* pci;
134     void* usb;
135     void* platform;
136     void* host1x;
137   } businfo;
138   union {
139     drmPciDeviceInfoPtr pci;
140     void* usb;
141     void* platform;
142     void* host1x;
143   } deviceinfo;
144 } drmDevice, *drmDevicePtr;
145 
146 // Open libGL and load needed symbols
147 #if defined(__OpenBSD__) || defined(__NetBSD__)
148 #  define LIBGL_FILENAME "libGL.so"
149 #  define LIBGLES_FILENAME "libGLESv2.so"
150 #  define LIBEGL_FILENAME "libEGL.so"
151 #  define LIBDRM_FILENAME "libdrm.so"
152 #else
153 #  define LIBGL_FILENAME "libGL.so.1"
154 #  define LIBGLES_FILENAME "libGLESv2.so.2"
155 #  define LIBEGL_FILENAME "libEGL.so.1"
156 #  define LIBDRM_FILENAME "libdrm.so.2"
157 #endif
158 
159 #define EXIT_FAILURE_BUFFER_TOO_SMALL 2
160 
161 namespace mozilla {
162 namespace widget {
163 // the read end of the pipe, which will be used by GfxInfo
164 extern int glxtest_pipe;
165 // the PID of the glxtest process, to pass to waitpid()
166 extern pid_t glxtest_pid;
167 }  // namespace widget
168 }  // namespace mozilla
169 
170 // the write end of the pipe, which we're going to write to
171 static int write_end_of_the_pipe = -1;
172 
173 // our buffer, size and used length
174 static char* glxtest_buf = nullptr;
175 static int glxtest_bufsize = 0;
176 static int glxtest_length = 0;
177 
178 // C++ standard collides with C standard in that it doesn't allow casting void*
179 // to function pointer types. So the work-around is to convert first to size_t.
180 // http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
181 template <typename func_ptr_type>
cast(void * ptr)182 static func_ptr_type cast(void* ptr) {
183   return reinterpret_cast<func_ptr_type>(reinterpret_cast<size_t>(ptr));
184 }
185 
record_value(const char * format,...)186 static void record_value(const char* format, ...) {
187   // Don't add more if the buffer is full.
188   if (glxtest_bufsize <= glxtest_length) {
189     return;
190   }
191 
192   // Append the new values to the buffer, not to exceed the remaining space.
193   int remaining = glxtest_bufsize - glxtest_length;
194   va_list args;
195   va_start(args, format);
196   int max_added =
197       vsnprintf(glxtest_buf + glxtest_length, remaining, format, args);
198   va_end(args);
199 
200   // snprintf returns how many char it could have added, not how many it added.
201   // It is important to get this right since it will control how many chars we
202   // will attempt to write to the pipe fd.
203   if (max_added > remaining) {
204     glxtest_length += remaining;
205   } else {
206     glxtest_length += max_added;
207   }
208 }
209 
record_error(const char * str)210 static void record_error(const char* str) { record_value("ERROR\n%s\n", str); }
211 
record_warning(const char * str)212 static void record_warning(const char* str) {
213   record_value("WARNING\n%s\n", str);
214 }
215 
record_flush()216 static void record_flush() {
217   mozilla::Unused << write(write_end_of_the_pipe, glxtest_buf, glxtest_length);
218 }
219 
220 #ifdef MOZ_X11
x_error_handler(Display *,XErrorEvent * ev)221 static int x_error_handler(Display*, XErrorEvent* ev) {
222   record_value(
223       "ERROR\nX error, error_code=%d, "
224       "request_code=%d, minor_code=%d\n",
225       ev->error_code, ev->request_code, ev->minor_code);
226   record_flush();
227   _exit(EXIT_FAILURE);
228   return 0;
229 }
230 #endif
231 
232 // childgltest is declared inside extern "C" so that the name is not mangled.
233 // The name is used in build/valgrind/x86_64-pc-linux-gnu.sup to suppress
234 // memory leak errors because we run it inside a short lived fork and we don't
235 // care about leaking memory
236 extern "C" {
237 
close_logging()238 static void close_logging() {
239   // we want to redirect to /dev/null stdout, stderr, and while we're at it,
240   // any PR logging file descriptors. To that effect, we redirect all positive
241   // file descriptors up to what open() returns here. In particular, 1 is stdout
242   // and 2 is stderr.
243   int fd = open("/dev/null", O_WRONLY);
244   for (int i = 1; i < fd; i++) {
245     dup2(fd, i);
246   }
247   close(fd);
248 
249   if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER")) {
250     const char* msg = "ERROR\nMOZ_AVOID_OPENGL_ALTOGETHER envvar set";
251     mozilla::Unused << write(write_end_of_the_pipe, msg, strlen(msg));
252     exit(EXIT_FAILURE);
253   }
254 }
255 
256 #define PCI_FILL_IDENT 0x0001
257 #define PCI_FILL_CLASS 0x0020
258 #define PCI_BASE_CLASS_DISPLAY 0x03
259 
get_pci_status()260 static int get_pci_status() {
261   void* libpci = dlopen("libpci.so.3", RTLD_LAZY);
262   if (!libpci) {
263     libpci = dlopen("libpci.so", RTLD_LAZY);
264   }
265   if (!libpci) {
266     record_warning("libpci missing");
267     return 0;
268   }
269 
270   typedef struct pci_dev {
271     struct pci_dev* next;
272     uint16_t domain_16;
273     uint8_t bus, dev, func;
274     unsigned int known_fields;
275     uint16_t vendor_id, device_id;
276     uint16_t device_class;
277   } pci_dev;
278 
279   typedef struct pci_access {
280     unsigned int method;
281     int writeable;
282     int buscentric;
283     char* id_file_name;
284     int free_id_name;
285     int numeric_ids;
286     unsigned int id_lookup_mode;
287     int debugging;
288     void* error;
289     void* warning;
290     void* debug;
291     pci_dev* devices;
292   } pci_access;
293 
294   typedef pci_access* (*PCIALLOC)(void);
295   PCIALLOC pci_alloc = cast<PCIALLOC>(dlsym(libpci, "pci_alloc"));
296 
297   typedef void (*PCIINIT)(pci_access*);
298   PCIINIT pci_init = cast<PCIINIT>(dlsym(libpci, "pci_init"));
299 
300   typedef void (*PCICLEANUP)(pci_access*);
301   PCICLEANUP pci_cleanup = cast<PCICLEANUP>(dlsym(libpci, "pci_cleanup"));
302 
303   typedef void (*PCISCANBUS)(pci_access*);
304   PCISCANBUS pci_scan_bus = cast<PCISCANBUS>(dlsym(libpci, "pci_scan_bus"));
305 
306   typedef void (*PCIFILLINFO)(pci_dev*, int);
307   PCIFILLINFO pci_fill_info = cast<PCIFILLINFO>(dlsym(libpci, "pci_fill_info"));
308 
309   if (!pci_alloc || !pci_cleanup || !pci_scan_bus || !pci_fill_info) {
310     dlclose(libpci);
311     record_warning("libpci missing methods");
312     return 0;
313   }
314 
315   pci_access* pacc = pci_alloc();
316   if (!pacc) {
317     dlclose(libpci);
318     record_warning("libpci alloc failed");
319     return 0;
320   }
321 
322   pci_init(pacc);
323   pci_scan_bus(pacc);
324 
325   int count = 0;
326   for (pci_dev* dev = pacc->devices; dev; dev = dev->next) {
327     pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_CLASS);
328     if (dev->device_class >> 8 == PCI_BASE_CLASS_DISPLAY && dev->vendor_id &&
329         dev->device_id) {
330       ++count;
331       record_value("PCI_VENDOR_ID\n0x%04x\nPCI_DEVICE_ID\n0x%04x\n",
332                    dev->vendor_id, dev->device_id);
333     }
334   }
335 
336   pci_cleanup(pacc);
337   dlclose(libpci);
338   return count;
339 }
340 
341 #ifdef MOZ_WAYLAND
device_has_name(const drmDevice * device,const char * name)342 static bool device_has_name(const drmDevice* device, const char* name) {
343   for (size_t i = 0; i < DRM_NODE_MAX; i++) {
344     if (!(device->available_nodes & (1 << i))) {
345       continue;
346     }
347     if (strcmp(device->nodes[i], name) == 0) {
348       return true;
349     }
350   }
351   return false;
352 }
353 
get_render_name(const char * name)354 static bool get_render_name(const char* name) {
355   void* libdrm = dlopen(LIBDRM_FILENAME, RTLD_LAZY);
356   if (!libdrm) {
357     record_warning("Failed to open libdrm");
358     return false;
359   }
360 
361   typedef int (*DRMGETDEVICES2)(uint32_t, drmDevicePtr*, int);
362   DRMGETDEVICES2 drmGetDevices2 =
363       cast<DRMGETDEVICES2>(dlsym(libdrm, "drmGetDevices2"));
364 
365   typedef void (*DRMFREEDEVICE)(drmDevicePtr*);
366   DRMFREEDEVICE drmFreeDevice =
367       cast<DRMFREEDEVICE>(dlsym(libdrm, "drmFreeDevice"));
368 
369   if (!drmGetDevices2 || !drmFreeDevice) {
370     record_warning(
371         "libdrm missing methods for drmGetDevices2 or drmFreeDevice");
372     dlclose(libdrm);
373     return false;
374   }
375 
376   uint32_t flags = 0;
377   int devices_len = drmGetDevices2(flags, nullptr, 0);
378   if (devices_len < 0) {
379     record_warning("drmGetDevices2 failed");
380     dlclose(libdrm);
381     return false;
382   }
383   drmDevice** devices = (drmDevice**)calloc(devices_len, sizeof(drmDevice*));
384   if (!devices) {
385     record_warning("Allocation error");
386     dlclose(libdrm);
387     return false;
388   }
389   devices_len = drmGetDevices2(flags, devices, devices_len);
390   if (devices_len < 0) {
391     free(devices);
392     record_warning("drmGetDevices2 failed");
393     dlclose(libdrm);
394     return false;
395   }
396 
397   const drmDevice* match = nullptr;
398   for (int i = 0; i < devices_len; i++) {
399     if (device_has_name(devices[i], name)) {
400       match = devices[i];
401       break;
402     }
403   }
404 
405   bool result = false;
406   if (!match) {
407     record_warning("Cannot find DRM device");
408   } else if (!(match->available_nodes & (1 << DRM_NODE_RENDER))) {
409     record_warning("DRM device has no render node");
410   } else {
411     record_value("DRM_RENDERDEVICE\n%s\n", match->nodes[DRM_NODE_RENDER]);
412     record_value(
413         "MESA_VENDOR_ID\n0x%04x\n"
414         "MESA_DEVICE_ID\n0x%04x\n",
415         match->deviceinfo.pci->vendor_id, match->deviceinfo.pci->device_id);
416     result = true;
417   }
418 
419   for (int i = 0; i < devices_len; i++) {
420     drmFreeDevice(&devices[i]);
421   }
422   free(devices);
423 
424   dlclose(libdrm);
425   return result;
426 }
427 #endif
428 
get_gles_status(EGLDisplay dpy,PFNEGLGETPROCADDRESS eglGetProcAddress)429 static bool get_gles_status(EGLDisplay dpy,
430                             PFNEGLGETPROCADDRESS eglGetProcAddress) {
431   typedef EGLBoolean (*PFNEGLCHOOSECONFIGPROC)(
432       EGLDisplay dpy, EGLint const* attrib_list, EGLConfig* configs,
433       EGLint config_size, EGLint* num_config);
434   PFNEGLCHOOSECONFIGPROC eglChooseConfig =
435       cast<PFNEGLCHOOSECONFIGPROC>(eglGetProcAddress("eglChooseConfig"));
436 
437   typedef EGLBoolean (*PFNEGLBINDAPIPROC)(EGLint api);
438   PFNEGLBINDAPIPROC eglBindAPI =
439       cast<PFNEGLBINDAPIPROC>(eglGetProcAddress("eglBindAPI"));
440 
441   typedef EGLContext (*PFNEGLCREATECONTEXTPROC)(
442       EGLDisplay dpy, EGLConfig config, EGLContext share_context,
443       EGLint const* attrib_list);
444   PFNEGLCREATECONTEXTPROC eglCreateContext =
445       cast<PFNEGLCREATECONTEXTPROC>(eglGetProcAddress("eglCreateContext"));
446 
447   typedef EGLBoolean (*PFNEGLMAKECURRENTPROC)(
448       EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext context);
449   PFNEGLMAKECURRENTPROC eglMakeCurrent =
450       cast<PFNEGLMAKECURRENTPROC>(eglGetProcAddress("eglMakeCurrent"));
451 
452   typedef const char* (*PFNEGLQUERYDEVICESTRINGEXTPROC)(EGLDeviceEXT device,
453                                                         EGLint name);
454   PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT =
455       cast<PFNEGLQUERYDEVICESTRINGEXTPROC>(
456           eglGetProcAddress("eglQueryDeviceStringEXT"));
457 
458   typedef EGLBoolean (*PFNEGLQUERYDISPLAYATTRIBEXTPROC)(
459       EGLDisplay dpy, EGLint name, EGLAttrib * value);
460   PFNEGLQUERYDISPLAYATTRIBEXTPROC eglQueryDisplayAttribEXT =
461       cast<PFNEGLQUERYDISPLAYATTRIBEXTPROC>(
462           eglGetProcAddress("eglQueryDisplayAttribEXT"));
463 
464   if (!eglChooseConfig || !eglCreateContext || !eglMakeCurrent ||
465       !eglQueryDeviceStringEXT) {
466     record_warning("libEGL missing methods for GL test");
467     return false;
468   }
469 
470   typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
471   PFNGLGETSTRING glGetString =
472       cast<PFNGLGETSTRING>(eglGetProcAddress("glGetString"));
473 
474   EGLint config_attrs[] = {EGL_RED_SIZE,  8, EGL_GREEN_SIZE, 8,
475                            EGL_BLUE_SIZE, 8, EGL_NONE};
476 
477   EGLConfig config;
478   EGLint num_config;
479   if (eglChooseConfig(dpy, config_attrs, &config, 1, &num_config) ==
480       EGL_FALSE) {
481     record_warning("eglChooseConfig returned an error");
482     return false;
483   }
484 
485   if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
486     record_warning("eglBindAPI returned an error");
487     return false;
488   }
489 
490   EGLint ctx_attrs[] = {EGL_NONE};
491   EGLContext ectx = eglCreateContext(dpy, config, EGL_NO_CONTEXT, ctx_attrs);
492   if (!ectx) {
493     record_warning("eglCreateContext returned an error");
494     return false;
495   }
496 
497   if (eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ectx) == EGL_FALSE) {
498     record_warning("eglMakeCurrent returned an error");
499     return false;
500   }
501 
502   // Implementations disagree about whether eglGetProcAddress or dlsym
503   // should be used for getting functions from the actual API, see
504   // https://github.com/anholt/libepoxy/commit/14f24485e33816139398d1bd170d617703473738
505   void* libgl = nullptr;
506   if (!glGetString) {
507     libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
508     if (!libgl) {
509       libgl = dlopen(LIBGLES_FILENAME, RTLD_LAZY);
510       if (!libgl) {
511         record_warning(LIBGL_FILENAME " and " LIBGLES_FILENAME " missing");
512         return false;
513       }
514     }
515 
516     glGetString = cast<PFNGLGETSTRING>(dlsym(libgl, "glGetString"));
517     if (!glGetString) {
518       dlclose(libgl);
519       record_warning("libEGL, libGL and libGLESv2 are missing glGetString");
520       return false;
521     }
522   }
523 
524   const GLubyte* versionString = glGetString(GL_VERSION);
525   const GLubyte* vendorString = glGetString(GL_VENDOR);
526   const GLubyte* rendererString = glGetString(GL_RENDERER);
527 
528   if (versionString && vendorString && rendererString) {
529     record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\nTRUE\n",
530                  vendorString, rendererString, versionString);
531   } else {
532     record_warning("EGL glGetString returned null");
533     if (libgl) {
534       dlclose(libgl);
535     }
536     return false;
537   }
538 
539   EGLDeviceEXT device;
540   if (eglQueryDisplayAttribEXT(dpy, EGL_DEVICE_EXT, (EGLAttrib*)&device) ==
541       EGL_TRUE) {
542     const char* deviceExtensions =
543         eglQueryDeviceStringEXT(device, EGL_EXTENSIONS);
544     if (deviceExtensions &&
545         strstr(deviceExtensions, "EGL_MESA_device_software")) {
546       record_value("MESA_ACCELERATED\nFALSE\n");
547     } else {
548 #ifdef MOZ_WAYLAND
549       const char* deviceString =
550           eglQueryDeviceStringEXT(device, EGL_DRM_DEVICE_FILE_EXT);
551       if (!deviceString || !get_render_name(deviceString)) {
552         const char* renderNodeString =
553             eglQueryDeviceStringEXT(device, EGL_DRM_RENDER_NODE_FILE_EXT);
554         if (renderNodeString) {
555           record_value("DRM_RENDERDEVICE\n%s\n", renderNodeString);
556         }
557       }
558 #endif
559     }
560   }
561 
562   if (libgl) {
563     dlclose(libgl);
564   }
565   return true;
566 }
567 
get_egl_status(EGLNativeDisplayType native_dpy,bool gles_test,bool require_driver)568 static bool get_egl_status(EGLNativeDisplayType native_dpy, bool gles_test,
569                            bool require_driver) {
570   void* libegl = dlopen(LIBEGL_FILENAME, RTLD_LAZY);
571   if (!libegl) {
572     record_warning("libEGL missing");
573     return false;
574   }
575 
576   PFNEGLGETPROCADDRESS eglGetProcAddress =
577       cast<PFNEGLGETPROCADDRESS>(dlsym(libegl, "eglGetProcAddress"));
578 
579   if (!eglGetProcAddress) {
580     dlclose(libegl);
581     record_warning("no eglGetProcAddress");
582     return false;
583   }
584 
585   typedef EGLDisplay (*PFNEGLGETDISPLAYPROC)(void* native_display);
586   PFNEGLGETDISPLAYPROC eglGetDisplay =
587       cast<PFNEGLGETDISPLAYPROC>(eglGetProcAddress("eglGetDisplay"));
588 
589   typedef EGLBoolean (*PFNEGLINITIALIZEPROC)(EGLDisplay dpy, EGLint * major,
590                                              EGLint * minor);
591   PFNEGLINITIALIZEPROC eglInitialize =
592       cast<PFNEGLINITIALIZEPROC>(eglGetProcAddress("eglInitialize"));
593 
594   typedef EGLBoolean (*PFNEGLTERMINATEPROC)(EGLDisplay dpy);
595   PFNEGLTERMINATEPROC eglTerminate =
596       cast<PFNEGLTERMINATEPROC>(eglGetProcAddress("eglTerminate"));
597 
598   if (!eglGetDisplay || !eglInitialize || !eglTerminate) {
599     dlclose(libegl);
600     record_warning("libEGL missing methods");
601     return false;
602   }
603 
604   EGLDisplay dpy = eglGetDisplay(native_dpy);
605   if (!dpy) {
606     dlclose(libegl);
607     record_warning("libEGL no display");
608     return false;
609   }
610 
611   EGLint major, minor;
612   if (!eglInitialize(dpy, &major, &minor)) {
613     dlclose(libegl);
614     record_warning("libEGL initialize failed");
615     return false;
616   }
617 
618   typedef const char* (*PFNEGLGETDISPLAYDRIVERNAMEPROC)(EGLDisplay dpy);
619   PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName =
620       cast<PFNEGLGETDISPLAYDRIVERNAMEPROC>(
621           eglGetProcAddress("eglGetDisplayDriverName"));
622   if (eglGetDisplayDriverName) {
623     // TODO(aosmond): If the driver name is empty, we probably aren't using Mesa
624     // and instead a proprietary GL, most likely NVIDIA's. The PCI device list
625     // in combination with the vendor name is very likely sufficient to identify
626     // the device.
627     const char* driDriver = eglGetDisplayDriverName(dpy);
628     if (driDriver) {
629       record_value("DRI_DRIVER\n%s\n", driDriver);
630     }
631   } else if (require_driver) {
632     record_warning("libEGL missing eglGetDisplayDriverName");
633     eglTerminate(dpy);
634     dlclose(libegl);
635     return false;
636   }
637 
638   if (gles_test && !get_gles_status(dpy, eglGetProcAddress)) {
639     eglTerminate(dpy);
640     dlclose(libegl);
641     return false;
642   }
643 
644   eglTerminate(dpy);
645   dlclose(libegl);
646   return true;
647 }
648 
649 #ifdef MOZ_X11
get_xrandr_info(Display * dpy)650 static void get_xrandr_info(Display* dpy) {
651   // When running on remote X11 the xrandr version may be stuck on an ancient
652   // version. There are still setups using remote X11 out there, so make sure we
653   // don't crash.
654   int eventBase, errorBase, major, minor;
655   if (!XRRQueryExtension(dpy, &eventBase, &errorBase) ||
656       !XRRQueryVersion(dpy, &major, &minor) ||
657       !(major > 1 || (major == 1 && minor >= 4))) {
658     return;
659   }
660 
661   Window root = RootWindow(dpy, DefaultScreen(dpy));
662   XRRProviderResources* pr = XRRGetProviderResources(dpy, root);
663   XRRScreenResources* res = XRRGetScreenResourcesCurrent(dpy, root);
664   RROutput primary = XRRGetOutputPrimary(dpy, root);
665 
666   if (res->noutput != 0) {
667     bool foundCRTC = false;
668 
669     for (int i = 0; i < res->noutput; i++) {
670       XRROutputInfo* outputInfo = XRRGetOutputInfo(dpy, res, res->outputs[i]);
671       if (!outputInfo->crtc) {
672         continue;
673       }
674 
675       if (!foundCRTC) {
676         record_value("SCREEN_INFO\n");
677         foundCRTC = true;
678       }
679 
680       XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(dpy, res, outputInfo->crtc);
681       record_value("%dx%d:%d;", crtcInfo->width, crtcInfo->height,
682                    res->outputs[i] == primary ? 1 : 0);
683     }
684 
685     if (foundCRTC) {
686       record_value("\n");
687     }
688   }
689 
690   if (pr->nproviders != 0) {
691     record_value("DDX_DRIVER\n");
692     for (int i = 0; i < pr->nproviders; i++) {
693       XRRProviderInfo* info = XRRGetProviderInfo(dpy, res, pr->providers[i]);
694       record_value("%s%s", info->name, i == pr->nproviders - 1 ? ";\n" : ";");
695     }
696   }
697 }
698 
get_glx_status(int * gotGlxInfo,int * gotDriDriver)699 static void get_glx_status(int* gotGlxInfo, int* gotDriDriver) {
700   void* libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
701   if (!libgl) {
702     record_error(LIBGL_FILENAME " missing");
703     return;
704   }
705 
706   typedef void* (*PFNGLXGETPROCADDRESS)(const char*);
707   PFNGLXGETPROCADDRESS glXGetProcAddress =
708       cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
709 
710   if (!glXGetProcAddress) {
711     record_error("no glXGetProcAddress");
712     return;
713   }
714 
715   typedef GLXFBConfig* (*PFNGLXQUERYEXTENSION)(Display*, int*, int*);
716   PFNGLXQUERYEXTENSION glXQueryExtension =
717       cast<PFNGLXQUERYEXTENSION>(glXGetProcAddress("glXQueryExtension"));
718 
719   typedef GLXFBConfig* (*PFNGLXQUERYVERSION)(Display*, int*, int*);
720   PFNGLXQUERYVERSION glXQueryVersion =
721       cast<PFNGLXQUERYVERSION>(dlsym(libgl, "glXQueryVersion"));
722 
723   typedef XVisualInfo* (*PFNGLXCHOOSEVISUAL)(Display*, int, int*);
724   PFNGLXCHOOSEVISUAL glXChooseVisual =
725       cast<PFNGLXCHOOSEVISUAL>(glXGetProcAddress("glXChooseVisual"));
726 
727   typedef GLXContext (*PFNGLXCREATECONTEXT)(Display*, XVisualInfo*, GLXContext,
728                                             Bool);
729   PFNGLXCREATECONTEXT glXCreateContext =
730       cast<PFNGLXCREATECONTEXT>(glXGetProcAddress("glXCreateContext"));
731 
732   typedef Bool (*PFNGLXMAKECURRENT)(Display*, GLXDrawable, GLXContext);
733   PFNGLXMAKECURRENT glXMakeCurrent =
734       cast<PFNGLXMAKECURRENT>(glXGetProcAddress("glXMakeCurrent"));
735 
736   typedef void (*PFNGLXDESTROYCONTEXT)(Display*, GLXContext);
737   PFNGLXDESTROYCONTEXT glXDestroyContext =
738       cast<PFNGLXDESTROYCONTEXT>(glXGetProcAddress("glXDestroyContext"));
739 
740   typedef GLubyte* (*PFNGLGETSTRING)(GLenum);
741   PFNGLGETSTRING glGetString =
742       cast<PFNGLGETSTRING>(glXGetProcAddress("glGetString"));
743 
744   if (!glXQueryExtension || !glXQueryVersion || !glXChooseVisual ||
745       !glXCreateContext || !glXMakeCurrent || !glXDestroyContext ||
746       !glGetString) {
747     record_error(LIBGL_FILENAME " missing methods");
748     return;
749   }
750 
751   ///// Open a connection to the X server /////
752   Display* dpy = XOpenDisplay(nullptr);
753   if (!dpy) {
754     record_error("Unable to open a connection to the X server");
755     return;
756   }
757 
758   ///// Check that the GLX extension is present /////
759   if (!glXQueryExtension(dpy, nullptr, nullptr)) {
760     record_error("GLX extension missing");
761     return;
762   }
763 
764   XSetErrorHandler(x_error_handler);
765 
766   ///// Get a visual /////
767   int attribs[] = {GLX_RGBA, GLX_RED_SIZE,  1, GLX_GREEN_SIZE,
768                    1,        GLX_BLUE_SIZE, 1, None};
769   XVisualInfo* vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
770   if (!vInfo) {
771     int attribs2[] = {GLX_RGBA, GLX_RED_SIZE,  1, GLX_GREEN_SIZE,
772                       1,        GLX_BLUE_SIZE, 1, GLX_DOUBLEBUFFER,
773                       None};
774     vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs2);
775     if (!vInfo) {
776       record_error("No visuals found");
777       return;
778     }
779   }
780 
781   // using a X11 Window instead of a GLXPixmap does not crash
782   // fglrx in indirect rendering. bug 680644
783   Window window;
784   XSetWindowAttributes swa;
785   swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vInfo->screen),
786                                  vInfo->visual, AllocNone);
787 
788   swa.border_pixel = 0;
789   window = XCreateWindow(dpy, RootWindow(dpy, vInfo->screen), 0, 0, 16, 16, 0,
790                          vInfo->depth, InputOutput, vInfo->visual,
791                          CWBorderPixel | CWColormap, &swa);
792 
793   ///// Get a GL context and make it current //////
794   GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
795   glXMakeCurrent(dpy, window, context);
796 
797   ///// Look for this symbol to determine texture_from_pixmap support /////
798   void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT");
799 
800   ///// Get GL vendor/renderer/versions strings /////
801   const GLubyte* versionString = glGetString(GL_VERSION);
802   const GLubyte* vendorString = glGetString(GL_VENDOR);
803   const GLubyte* rendererString = glGetString(GL_RENDERER);
804 
805   if (versionString && vendorString && rendererString) {
806     record_value("VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
807                  vendorString, rendererString, versionString,
808                  glXBindTexImageEXT ? "TRUE" : "FALSE");
809     *gotGlxInfo = 1;
810   } else {
811     record_error("glGetString returned null");
812   }
813 
814   // If GLX_MESA_query_renderer is available, populate additional data.
815   typedef Bool (*PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC)(
816       int attribute, unsigned int* value);
817   PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC
818   glXQueryCurrentRendererIntegerMESAProc =
819       cast<PFNGLXQUERYCURRENTRENDERERINTEGERMESAPROC>(
820           glXGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
821   if (glXQueryCurrentRendererIntegerMESAProc) {
822     unsigned int vendorId, deviceId, accelerated, videoMemoryMB;
823     glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VENDOR_ID_MESA,
824                                            &vendorId);
825     glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_DEVICE_ID_MESA,
826                                            &deviceId);
827     glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_ACCELERATED_MESA,
828                                            &accelerated);
829     glXQueryCurrentRendererIntegerMESAProc(GLX_RENDERER_VIDEO_MEMORY_MESA,
830                                            &videoMemoryMB);
831 
832     // Truncate IDs to 4 digits- that's all PCI IDs are.
833     vendorId &= 0xFFFF;
834     deviceId &= 0xFFFF;
835 
836     record_value(
837         "MESA_VENDOR_ID\n0x%04x\n"
838         "MESA_DEVICE_ID\n0x%04x\n"
839         "MESA_ACCELERATED\n%s\n"
840         "MESA_VRAM\n%dMB\n",
841         vendorId, deviceId, accelerated ? "TRUE" : "FALSE", videoMemoryMB);
842   }
843 
844   // From Mesa's GL/internal/dri_interface.h, to be used by DRI clients.
845   typedef const char* (*PFNGLXGETSCREENDRIVERPROC)(Display * dpy, int scrNum);
846   PFNGLXGETSCREENDRIVERPROC glXGetScreenDriverProc =
847       cast<PFNGLXGETSCREENDRIVERPROC>(glXGetProcAddress("glXGetScreenDriver"));
848   if (glXGetScreenDriverProc) {
849     const char* driDriver = glXGetScreenDriverProc(dpy, DefaultScreen(dpy));
850     if (driDriver) {
851       *gotDriDriver = 1;
852       record_value("DRI_DRIVER\n%s\n", driDriver);
853     }
854   }
855 
856   // Get monitor and DDX driver information
857   get_xrandr_info(dpy);
858 
859   ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it
860   ///// doesn't need to check GL info) so we might be staying alive for longer
861   ///// than expected, so it's important to consume as little memory as
862   ///// possible. Also we want to check that we're able to do that too without
863   ///// generating X errors.
864   glXMakeCurrent(dpy, None,
865                  nullptr);  // must release the GL context before destroying it
866   glXDestroyContext(dpy, context);
867   XDestroyWindow(dpy, window);
868   XFreeColormap(dpy, swa.colormap);
869 
870 #  ifdef NS_FREE_PERMANENT_DATA  // conditionally defined in nscore.h, don't
871                                  // forget to #include it above
872   XCloseDisplay(dpy);
873 #  else
874   // This XSync call wanted to be instead:
875   //   XCloseDisplay(dpy);
876   // but this can cause 1-minute stalls on certain setups using Nouveau, see bug
877   // 973192
878   XSync(dpy, False);
879 #  endif
880 
881   dlclose(libgl);
882 }
883 
x11_egltest(int pci_count)884 static bool x11_egltest(int pci_count) {
885   Display* dpy = XOpenDisplay(nullptr);
886   if (!dpy) {
887     return false;
888   }
889   XSetErrorHandler(x_error_handler);
890 
891   // Bug 1667621: 30bit "Deep Color" is broken on EGL on Mesa (as of 2021/10).
892   // Disable all non-standard depths for the initial EGL roleout.
893   int screenCount = ScreenCount(dpy);
894   for (int idx = 0; idx < screenCount; idx++) {
895     if (DefaultDepth(dpy, idx) != 24) {
896       return false;
897     }
898   }
899 
900   // On at least amdgpu open source driver, eglInitialize fails unless
901   // a valid XDisplay pointer is passed as the native display.
902   if (!get_egl_status(dpy, true, pci_count != 1)) {
903     return false;
904   }
905 
906   // Get monitor and DDX driver information
907   get_xrandr_info(dpy);
908 
909   // Bug 1715245: Closing the display connection here crashes on NV prop.
910   // drivers. Just leave it open, the process will exit shortly after anyway.
911   // XCloseDisplay(dpy);
912 
913   record_value("TEST_TYPE\nEGL\n");
914   return true;
915 }
916 
glxtest()917 static void glxtest() {
918   int gotGlxInfo = 0;
919   int gotDriDriver = 0;
920 
921   get_glx_status(&gotGlxInfo, &gotDriDriver);
922   if (!gotGlxInfo) {
923     get_egl_status(nullptr, true, false);
924   } else if (!gotDriDriver) {
925     // If we failed to get the driver name from X, try via
926     // EGL_MESA_query_driver. We are probably using Wayland.
927     get_egl_status(nullptr, false, true);
928   }
929 
930   record_value("TEST_TYPE\nGLX\n");
931 }
932 #endif
933 
934 #ifdef MOZ_WAYLAND
935 typedef void (*print_info_t)(void* info);
936 typedef void (*destroy_info_t)(void* info);
937 
938 struct global_info {
939   struct wl_list link;
940 
941   uint32_t id;
942   uint32_t version;
943   char* interface;
944 
945   print_info_t print;
946   destroy_info_t destroy;
947 };
948 
949 struct output_info {
950   struct global_info global;
951   struct wl_list global_link;
952 
953   struct wl_output* output;
954 
955   int32_t version;
956 
957   int32_t scale;
958 };
959 
960 struct xdg_output_v1_info {
961   struct wl_list link;
962 
963   struct zxdg_output_v1* xdg_output;
964   struct output_info* output;
965 
966   struct {
967     int32_t width, height;
968   } logical;
969 };
970 
971 struct xdg_output_manager_v1_info {
972   struct global_info global;
973   struct zxdg_output_manager_v1* manager;
974   struct weston_info* info;
975 
976   struct wl_list outputs;
977 };
978 
979 struct weston_info {
980   struct wl_display* display;
981   struct wl_registry* registry;
982 
983   struct wl_list infos;
984   bool roundtrip_needed;
985 
986   struct wl_list outputs;
987   struct xdg_output_manager_v1_info* xdg_output_manager_v1_info;
988 };
989 
init_global_info(struct weston_info * info,struct global_info * global,uint32_t id,const char * interface,uint32_t version)990 static void init_global_info(struct weston_info* info,
991                              struct global_info* global, uint32_t id,
992                              const char* interface, uint32_t version) {
993   global->id = id;
994   global->version = version;
995   global->interface = strdup(interface);
996 
997   wl_list_insert(info->infos.prev, &global->link);
998 }
999 
print_output_info(void * data)1000 static void print_output_info(void* data) {}
1001 
destroy_xdg_output_v1_info(struct xdg_output_v1_info * info)1002 static void destroy_xdg_output_v1_info(struct xdg_output_v1_info* info) {
1003   wl_list_remove(&info->link);
1004   zxdg_output_v1_destroy(info->xdg_output);
1005   free(info);
1006 }
1007 
cmpOutputIds(const void * a,const void * b)1008 static int cmpOutputIds(const void* a, const void* b) {
1009   return (((struct xdg_output_v1_info*)a)->output->global.id -
1010           ((struct xdg_output_v1_info*)b)->output->global.id);
1011 }
1012 
print_xdg_output_manager_v1_info(void * data)1013 static void print_xdg_output_manager_v1_info(void* data) {
1014   struct xdg_output_manager_v1_info* info =
1015       (struct xdg_output_manager_v1_info*)data;
1016   struct xdg_output_v1_info* output;
1017 
1018   int screen_count = wl_list_length(&info->outputs);
1019   if (screen_count > 0) {
1020     struct xdg_output_v1_info* infos = (struct xdg_output_v1_info*)calloc(
1021         1, screen_count * sizeof(xdg_output_v1_info));
1022 
1023     int pos = 0;
1024     wl_list_for_each(output, &info->outputs, link) {
1025       infos[pos] = *output;
1026       pos++;
1027     }
1028 
1029     if (screen_count > 1) {
1030       qsort(infos, screen_count, sizeof(struct xdg_output_v1_info),
1031             cmpOutputIds);
1032     }
1033 
1034     record_value("SCREEN_INFO\n");
1035     for (int i = 0; i < screen_count; i++) {
1036       record_value("%dx%d:0;", infos[i].logical.width, infos[i].logical.height);
1037     }
1038     record_value("\n");
1039 
1040     free(infos);
1041   }
1042 }
1043 
destroy_xdg_output_manager_v1_info(void * data)1044 static void destroy_xdg_output_manager_v1_info(void* data) {
1045   struct xdg_output_manager_v1_info* info =
1046       (struct xdg_output_manager_v1_info*)data;
1047   struct xdg_output_v1_info *output, *tmp;
1048 
1049   zxdg_output_manager_v1_destroy(info->manager);
1050 
1051   wl_list_for_each_safe(output, tmp, &info->outputs, link) {
1052     destroy_xdg_output_v1_info(output);
1053   }
1054 }
1055 
handle_xdg_output_v1_logical_position(void * data,struct zxdg_output_v1 * output,int32_t x,int32_t y)1056 static void handle_xdg_output_v1_logical_position(void* data,
1057                                                   struct zxdg_output_v1* output,
1058                                                   int32_t x, int32_t y) {}
1059 
handle_xdg_output_v1_logical_size(void * data,struct zxdg_output_v1 * output,int32_t width,int32_t height)1060 static void handle_xdg_output_v1_logical_size(void* data,
1061                                               struct zxdg_output_v1* output,
1062                                               int32_t width, int32_t height) {
1063   struct xdg_output_v1_info* xdg_output = (struct xdg_output_v1_info*)data;
1064   xdg_output->logical.width = width;
1065   xdg_output->logical.height = height;
1066 }
1067 
handle_xdg_output_v1_done(void * data,struct zxdg_output_v1 * output)1068 static void handle_xdg_output_v1_done(void* data,
1069                                       struct zxdg_output_v1* output) {}
1070 
handle_xdg_output_v1_name(void * data,struct zxdg_output_v1 * output,const char * name)1071 static void handle_xdg_output_v1_name(void* data, struct zxdg_output_v1* output,
1072                                       const char* name) {}
1073 
handle_xdg_output_v1_description(void * data,struct zxdg_output_v1 * output,const char * description)1074 static void handle_xdg_output_v1_description(void* data,
1075                                              struct zxdg_output_v1* output,
1076                                              const char* description) {}
1077 
1078 static const struct zxdg_output_v1_listener xdg_output_v1_listener = {
1079     .logical_position = handle_xdg_output_v1_logical_position,
1080     .logical_size = handle_xdg_output_v1_logical_size,
1081     .done = handle_xdg_output_v1_done,
1082     .name = handle_xdg_output_v1_name,
1083     .description = handle_xdg_output_v1_description,
1084 };
1085 
add_xdg_output_v1_info(struct xdg_output_manager_v1_info * manager_info,struct output_info * output)1086 static void add_xdg_output_v1_info(
1087     struct xdg_output_manager_v1_info* manager_info,
1088     struct output_info* output) {
1089   struct xdg_output_v1_info* xdg_output =
1090       (struct xdg_output_v1_info*)calloc(1, sizeof *xdg_output);
1091 
1092   wl_list_insert(&manager_info->outputs, &xdg_output->link);
1093   xdg_output->xdg_output = zxdg_output_manager_v1_get_xdg_output(
1094       manager_info->manager, output->output);
1095   zxdg_output_v1_add_listener(xdg_output->xdg_output, &xdg_output_v1_listener,
1096                               xdg_output);
1097 
1098   xdg_output->output = output;
1099 
1100   manager_info->info->roundtrip_needed = true;
1101 }
1102 
add_xdg_output_manager_v1_info(struct weston_info * info,uint32_t id,uint32_t version)1103 static void add_xdg_output_manager_v1_info(struct weston_info* info,
1104                                            uint32_t id, uint32_t version) {
1105   struct output_info* output;
1106   struct xdg_output_manager_v1_info* manager =
1107       (struct xdg_output_manager_v1_info*)calloc(1, sizeof *manager);
1108 
1109   wl_list_init(&manager->outputs);
1110   manager->info = info;
1111 
1112   init_global_info(info, &manager->global, id,
1113                    zxdg_output_manager_v1_interface.name, version);
1114   manager->global.print = print_xdg_output_manager_v1_info;
1115   manager->global.destroy = destroy_xdg_output_manager_v1_info;
1116 
1117   manager->manager = (struct zxdg_output_manager_v1*)wl_registry_bind(
1118       info->registry, id, &zxdg_output_manager_v1_interface,
1119       version > 2 ? 2 : version);
1120 
1121   wl_list_for_each(output, &info->outputs, global_link) {
1122     add_xdg_output_v1_info(manager, output);
1123   }
1124 
1125   info->xdg_output_manager_v1_info = manager;
1126 }
1127 
output_handle_geometry(void * data,struct wl_output * wl_output,int32_t x,int32_t y,int32_t physical_width,int32_t physical_height,int32_t subpixel,const char * make,const char * model,int32_t output_transform)1128 static void output_handle_geometry(void* data, struct wl_output* wl_output,
1129                                    int32_t x, int32_t y, int32_t physical_width,
1130                                    int32_t physical_height, int32_t subpixel,
1131                                    const char* make, const char* model,
1132                                    int32_t output_transform) {}
1133 
output_handle_mode(void * data,struct wl_output * wl_output,uint32_t flags,int32_t width,int32_t height,int32_t refresh)1134 static void output_handle_mode(void* data, struct wl_output* wl_output,
1135                                uint32_t flags, int32_t width, int32_t height,
1136                                int32_t refresh) {}
1137 
output_handle_done(void * data,struct wl_output * wl_output)1138 static void output_handle_done(void* data, struct wl_output* wl_output) {}
1139 
output_handle_scale(void * data,struct wl_output * wl_output,int32_t scale)1140 static void output_handle_scale(void* data, struct wl_output* wl_output,
1141                                 int32_t scale) {
1142   struct output_info* output = (struct output_info*)data;
1143 
1144   output->scale = scale;
1145 }
1146 
1147 static const struct wl_output_listener output_listener = {
1148     output_handle_geometry,
1149     output_handle_mode,
1150     output_handle_done,
1151     output_handle_scale,
1152 };
1153 
destroy_output_info(void * data)1154 static void destroy_output_info(void* data) {
1155   struct output_info* output = (struct output_info*)data;
1156 
1157   wl_output_destroy(output->output);
1158 }
1159 
add_output_info(struct weston_info * info,uint32_t id,uint32_t version)1160 static void add_output_info(struct weston_info* info, uint32_t id,
1161                             uint32_t version) {
1162   struct output_info* output = (struct output_info*)calloc(1, sizeof *output);
1163 
1164   init_global_info(info, &output->global, id, "wl_output", version);
1165   output->global.print = print_output_info;
1166   output->global.destroy = destroy_output_info;
1167 
1168   output->version = MIN(version, 2);
1169   output->scale = 1;
1170 
1171   output->output = (struct wl_output*)wl_registry_bind(
1172       info->registry, id, &wl_output_interface, output->version);
1173   wl_output_add_listener(output->output, &output_listener, output);
1174 
1175   info->roundtrip_needed = true;
1176   wl_list_insert(&info->outputs, &output->global_link);
1177 
1178   if (info->xdg_output_manager_v1_info) {
1179     add_xdg_output_v1_info(info->xdg_output_manager_v1_info, output);
1180   }
1181 }
1182 
global_handler(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)1183 static void global_handler(void* data, struct wl_registry* registry,
1184                            uint32_t id, const char* interface,
1185                            uint32_t version) {
1186   struct weston_info* info = (struct weston_info*)data;
1187 
1188   if (!strcmp(interface, "wl_output")) {
1189     add_output_info(info, id, version);
1190   } else if (!strcmp(interface, zxdg_output_manager_v1_interface.name)) {
1191     add_xdg_output_manager_v1_info(info, id, version);
1192   }
1193 }
1194 
global_remove_handler(void * data,struct wl_registry * registry,uint32_t name)1195 static void global_remove_handler(void* data, struct wl_registry* registry,
1196                                   uint32_t name) {}
1197 
1198 static const struct wl_registry_listener registry_listener = {
1199     global_handler, global_remove_handler};
1200 
print_infos(struct wl_list * infos)1201 static void print_infos(struct wl_list* infos) {
1202   struct global_info* info;
1203 
1204   wl_list_for_each(info, infos, link) { info->print(info); }
1205 }
1206 
destroy_info(void * data)1207 static void destroy_info(void* data) {
1208   struct global_info* global = (struct global_info*)data;
1209 
1210   global->destroy(data);
1211   wl_list_remove(&global->link);
1212   free(global->interface);
1213   free(data);
1214 }
1215 
destroy_infos(struct wl_list * infos)1216 static void destroy_infos(struct wl_list* infos) {
1217   struct global_info *info, *tmp;
1218   wl_list_for_each_safe(info, tmp, infos, link) { destroy_info(info); }
1219 }
1220 
get_wayland_screen_info(struct wl_display * dpy)1221 static void get_wayland_screen_info(struct wl_display* dpy) {
1222   struct weston_info info = {0};
1223   info.display = dpy;
1224 
1225   info.xdg_output_manager_v1_info = NULL;
1226   wl_list_init(&info.infos);
1227   wl_list_init(&info.outputs);
1228 
1229   info.registry = wl_display_get_registry(info.display);
1230   wl_registry_add_listener(info.registry, &registry_listener, &info);
1231 
1232   do {
1233     info.roundtrip_needed = false;
1234     wl_display_roundtrip(info.display);
1235   } while (info.roundtrip_needed);
1236 
1237   print_infos(&info.infos);
1238   destroy_infos(&info.infos);
1239 
1240   wl_registry_destroy(info.registry);
1241 }
1242 
wayland_egltest()1243 static void wayland_egltest() {
1244   // NOTE: returns false to fall back to X11 when the Wayland socket doesn't
1245   // exist but fails with record_error if something actually went wrong
1246   struct wl_display* dpy = wl_display_connect(nullptr);
1247   if (!dpy) {
1248     record_error("Could not connect to wayland socket");
1249     return;
1250   }
1251 
1252   if (!get_egl_status((EGLNativeDisplayType)dpy, true, false)) {
1253     record_error("EGL test failed");
1254   }
1255   get_wayland_screen_info(dpy);
1256 
1257   wl_display_disconnect(dpy);
1258   record_value("TEST_TYPE\nEGL\n");
1259 }
1260 #endif
1261 
childgltest()1262 int childgltest() {
1263   enum { bufsize = 2048 };
1264   char buf[bufsize];
1265 
1266   // We save it as a global so that the X error handler can flush the buffer
1267   // before early exiting.
1268   glxtest_buf = buf;
1269   glxtest_bufsize = bufsize;
1270 
1271   // Get a list of all GPUs from the PCI bus.
1272   int pci_count = get_pci_status();
1273 
1274 #ifdef MOZ_WAYLAND
1275   if (IsWaylandEnabled()) {
1276     wayland_egltest();
1277   } else
1278 #endif
1279   {
1280 #ifdef MOZ_X11
1281     // TODO: --display command line argument is not properly handled
1282     if (!x11_egltest(pci_count)) {
1283       glxtest();
1284     }
1285 #endif
1286   }
1287 
1288   // Finally write buffered data to the pipe.
1289   record_flush();
1290 
1291   // If we completely filled the buffer, we need to tell the parent.
1292   if (glxtest_length >= glxtest_bufsize) {
1293     return EXIT_FAILURE_BUFFER_TOO_SMALL;
1294   }
1295 
1296   return EXIT_SUCCESS;
1297 }
1298 
1299 }  // extern "C"
1300 
1301 /** \returns true in the child glxtest process, false in the parent process */
fire_glxtest_process()1302 bool fire_glxtest_process() {
1303   int pfd[2];
1304   if (pipe(pfd) == -1) {
1305     perror("pipe");
1306     return false;
1307   }
1308   pid_t pid = fork();
1309   if (pid < 0) {
1310     perror("fork");
1311     close(pfd[0]);
1312     close(pfd[1]);
1313     return false;
1314   }
1315   // The child exits early to avoid running the full shutdown sequence and avoid
1316   // conflicting with threads we have already spawned (like the profiler).
1317   if (pid == 0) {
1318     close(pfd[0]);
1319     write_end_of_the_pipe = pfd[1];
1320     close_logging();
1321     int rv = childgltest();
1322     close(pfd[1]);
1323     _exit(rv);
1324   }
1325 
1326   close(pfd[1]);
1327   mozilla::widget::glxtest_pipe = pfd[0];
1328   mozilla::widget::glxtest_pid = pid;
1329   return false;
1330 }
1331