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