Back to index

nux  3.0.0
unity_support_test.c
Go to the documentation of this file.
00001 // Copyright © 2011 Canonical Ltd
00002 //
00003 // This program is free software: you can redistribute it and/or modify it under
00004 // the terms of the GNU General Public License version 3 as published by the
00005 // Free Software Foundation. This program is distributed in the hope that it
00006 // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
00007 // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
00008 // Public License for more details. You should have received a copy of the GNU
00009 // General Public License along with this program. If not, see
00010 // <http://www.gnu.org/licenses/>.
00011 //
00012 // Authored by:
00013 //     Loïc Molinari <loic.molinari@canonical.com>
00014 //     Jay Taoko <jay.taoko@canonical.com>
00015 
00016 // unity_support_test checks for Unity support on a X11 display. All the checks
00017 // are based on what's done in Compiz (core/plugins/opengl/src/screen.cpp).
00018 //
00019 // $ gcc -std=c99 unity_support_test.c -o unity_support_test `pkg-config
00020 // > --cflags --libs gl x11 libpci xcomposite xdamage`
00021 
00022 #include <pci/pci.h>
00023 #include <X11/Xlib.h>
00024 #include <X11/Xutil.h>
00025 #include <X11/extensions/Xcomposite.h>
00026 #include <X11/extensions/Xdamage.h>
00027 #ifndef NUX_OPENGLES_20
00028 #include <GL/gl.h>
00029 #define GLX_GLXEXT_PROTOTYPES
00030 #include <GL/glx.h>
00031 #undef GLX_GLXEXT_PROTOTYPES
00032 #else
00033 #include <EGL/egl.h>
00034 #include <GLES2/gl2.h>
00035 #include <GLES2/gl2ext.h>
00036 #endif
00037 #include <stdio.h>
00038 #include <string.h>
00039 #include <stdlib.h>
00040 #include <ctype.h>
00041 #include <fcntl.h>
00042 #include <unistd.h>
00043 
00044 #ifndef NUX_OPENGLES_20
00045 typedef GLXContext NUXContext;
00046 #else
00047 typedef EGLConfig NUXContext;
00048 #endif
00049 
00050 // Enables colored console output at build time.
00051 #define COLORED_OUTPUT 1
00052 
00053 // Gets the number of elements in an array.
00054 #define ARRAY_SIZE(array) (sizeof (array) / sizeof ((array)[0]))
00055 
00056 enum {
00057   // Extension flags.
00058   FLAG_GLX_SGIX_FBCONFIG           = (1 << 0),
00059   FLAG_GLX_EXT_TEXTURE_FROM_PIXMAP = (1 << 1),
00060   FLAG_GL_ARB_NON_POWER_OF_TWO     = (1 << 2),
00061   FLAG_GL_NV_TEXTURE_RECTANGLE     = (1 << 3),
00062   FLAG_GL_EXT_TEXTURE_RECTANGLE    = (1 << 4),
00063   FLAG_GL_ARB_TEXTURE_RECTANGLE    = (1 << 5),
00064   FLAG_GL_ARB_VERTEX_PROGRAM       = (1 << 6),
00065   FLAG_GL_ARB_FRAGMENT_PROGRAM     = (1 << 7),
00066   FLAG_GL_ARB_VERTEX_BUFFER_OBJECT = (1 << 8),
00067   FLAG_GL_EXT_FRAMEBUFFER_OBJECT   = (1 << 9),
00068   FLAG_GL_ARB_FRAMEBUFFER_OBJECT   = (1 << 10),
00069   FLAG_SOFTWARE_RENDERING          = (1 << 11),
00070   FLAG_BLACKLISTED                 = (1 << 12),
00071   FLAG_GL_OES_EGL_IMAGE            = (1 << 13),
00072   FLAG_EGL_KHR_IMAGE_PIXMAP        = (1 << 14),
00073 
00074   // Extension masks.
00075   MASK_GL_NON_POWER_OF_TWO   = (FLAG_GL_ARB_NON_POWER_OF_TWO
00076                                 | FLAG_GL_NV_TEXTURE_RECTANGLE
00077                                 | FLAG_GL_EXT_TEXTURE_RECTANGLE
00078                                 | FLAG_GL_ARB_TEXTURE_RECTANGLE),
00079   MASK_GL_FRAMEBUFFER_OBJECT = (FLAG_GL_EXT_FRAMEBUFFER_OBJECT
00080                                 | FLAG_GL_ARB_FRAMEBUFFER_OBJECT)
00081 };
00082 
00083 // PCI device identity.
00084 struct PciDevice {
00085   unsigned short vendor;
00086   unsigned short device;
00087 };
00088 
00089 // Blacklists of GPUs for Compiz.
00090 struct PciDevice gpu_blacklist[] = {
00091   { 0x8086, 0x3577 },  // Intel : 82830M/MG
00092   { 0x8086, 0x2562 },  // Intel : 82845G aka Poulsbo
00093   { 0x1002, 0x4c57 },  // ATI : Radeon Mobility 7500
00094   { 0x10de, 0x0322 },  // nVidia: GeForce FX 5200
00095   { 0x10de, 0x0326 },  // nVidia: GeForce FX 5500
00096   { 0x10de, 0x0240 },  // nVidia: GeForce 6150
00097   { 0x10de, 0x01d3 },  // nVidia: GeForce Go 7300 SE / 7200 GS
00098   { 0x10de, 0x01d7 },  // nVidia: GeForce Go 7300 / Quadro NVS 110M
00099   { 0x10de, 0x01d8 }   // nVidia: GeForce Go 7400
00100 };
00101 
00102 typedef struct _TestResults {
00103   char *vendor, *renderer, *version;
00104   int result, major, minor;
00105   int indirect;
00106   int compiz;
00107   unsigned int flags;
00108   char        *error;
00109 } TestResults;
00110 
00111 // Checks whether an extension is supported by the GLX/OpenGL implementation
00112 // given the extension name and the list of supported extensions.
00113 static int is_extension_supported (const char* extensions,
00114                                    const char* extension) {
00115   if (extensions != NULL && extension != NULL) {
00116     const size_t len = strlen (extension);
00117     char* p = (char*) extensions;
00118     char* end = p + strlen (p);
00119     while (p < end) {
00120       const size_t size = strcspn (p, " ");
00121       if (len == size && strncmp (extension, p, size) == 0)
00122         return 1;
00123       p += size + 1;
00124     }
00125   }
00126   return 0;
00127 }
00128 
00129 // Gets the OpenGL version number given the string.
00130 static void get_opengl_version (const char *version, int* major, int* minor) {
00131   int tmp = 0, i;
00132 
00133   if (!version)
00134   {
00135     *major = 0;
00136     *minor = 0;
00137     return;
00138   }
00139 
00140   for (i = 0; isdigit (version[i]); i++)
00141     tmp = tmp * 10 + (version[i] - 48);
00142   if (version[i++] == '.') {
00143     *major = tmp;
00144     *minor = (version[i] - 48);
00145   } else {
00146     *major = 0;
00147     *minor = 0;
00148   }
00149 }
00150 
00151 static void print_help () {
00152   fprintf (stdout,
00153            "Check for Unity support on a X11 display.\n"
00154            "\n"
00155            "Usage:\n"
00156            "  unity-support-test [ options ]\n"
00157            "    -d, --display name: Specify the X11 display\n"
00158            "    -i, --indirect:     Force an indirect rendering context\n"
00159            "    -p, --print:        Print detection results on stdout\n"
00160            "    -c, --compiz:       Only check for Compiz support\n"
00161            "    -h, --help:         Show help\n");
00162 }
00163 
00164 static void print_report (const char* vendor, const char* renderer,
00165                           const char* version, int major, int minor,
00166                           unsigned int flags, int compiz, int result,
00167                           const char* error) {
00168   if (error == NULL) {
00169 #if COLORED_OUTPUT == 1
00170     const char* yes = "\033[32;01myes\033[00m";
00171     const char* no = "\033[31;01mno\033[00m";
00172 #else
00173     const char* yes = "yes";
00174     const char* no = "no";
00175 #endif
00176     fprintf (stdout,
00177              "OpenGL vendor string:   %s\n"
00178              "OpenGL renderer string: %s\n"
00179              "OpenGL version string:  %s\n"
00180              "\n"
00181              "Not software rendered:    %s\n"
00182              "Not blacklisted:          %s\n"
00183              "GLX fbconfig:             %s\n"
00184              "GLX texture from pixmap:  %s\n"
00185              "GL npot or rect textures: %s\n",
00186              vendor, renderer, version,
00187              flags & FLAG_SOFTWARE_RENDERING ? no : yes,
00188              flags & FLAG_BLACKLISTED ? no : yes,
00189              flags & FLAG_GLX_SGIX_FBCONFIG ? yes : no,
00190              flags & FLAG_GLX_EXT_TEXTURE_FROM_PIXMAP ? yes : no,
00191              flags & MASK_GL_NON_POWER_OF_TWO ? yes : no);
00192     if (compiz == 0) {
00193 #ifndef NUX_OPENGLES_20
00194       fprintf (stdout,
00195                "GL vertex program:        %s\n"
00196                "GL fragment program:      %s\n"
00197                "GL vertex buffer object:  %s\n"
00198                "GL framebuffer object:    %s\n"
00199                "GL version is 1.4+:       %s\n"
00200                "\n"
00201                "Unity 3D supported:       %s\n",
00202                flags & FLAG_GL_ARB_VERTEX_PROGRAM ? yes : no,
00203                flags & FLAG_GL_ARB_FRAGMENT_PROGRAM ? yes : no,
00204                flags & FLAG_GL_ARB_VERTEX_BUFFER_OBJECT ? yes : no,
00205                flags & MASK_GL_FRAMEBUFFER_OBJECT ? yes : no,
00206                (major >= 2 || (major == 1 && minor >= 4)) ? yes : no,
00207                result == 0 ? yes : no);
00208 #else
00209       fprintf (stdout,
00210                "GL OES EGL image:         %s\n"
00211                "EGL KHR image pixmap:     %s\n"
00212                "EGL version is 1.4+:      %s\n"
00213                "\n"
00214                "Unity supported:          %s\n",
00215                flags & FLAG_GL_OES_EGL_IMAGE ? yes : no,
00216                flags & FLAG_EGL_KHR_IMAGE_PIXMAP ? yes : no,
00217                (major >= 2 || (major == 1 && minor >= 4)) ? yes : no,
00218                result == 0 ? yes : no);
00219 #endif
00220     } else {
00221       fprintf (stdout, "\nCompiz supported:         %s\n",
00222                result == 0 ? yes : no);
00223     }
00224   } else {
00225     fprintf (stdout, "Error: %s\n", error);
00226   }
00227 }
00228 
00229 static int check_root_visual (Display     *display,
00230                            unsigned int screen,
00231                            Window      root,
00232                            NUXContext  *context,
00233                            XVisualInfo **vinfos,
00234                            TestResults  *results)
00235 {
00236   // Retrieve root window visual.
00237   XWindowAttributes attr;
00238   XVisualInfo vinfo_template;
00239   int nr_vinfos;
00240   if (XGetWindowAttributes (display, root, &attr) == 0) {
00241     results->error = strdup ("unable to get root window attributes");
00242     results->result = 1;
00243     return 0;
00244   }
00245   vinfo_template.visualid = XVisualIDFromVisual (attr.visual);
00246   *vinfos = XGetVisualInfo (display, VisualIDMask, &vinfo_template, &nr_vinfos);
00247   if (nr_vinfos == 0) {
00248     results->error = strdup ("unable to get visual informations for default visual");
00249     results->result = 1;
00250     return 0;
00251   }
00252 
00253   return 1;
00254 }
00255 
00256 static int check_xcomposite (Display     *display,
00257                            unsigned int screen,
00258                            Window      root,
00259                            NUXContext  *context,
00260                            XVisualInfo **vinfos,
00261                            TestResults  *results)
00262 {
00263 // Check for XComposite
00264   int composite_major, composite_minor;
00265   unsigned int composite_tmp;
00266 
00267   if (!XQueryExtension (display, COMPOSITE_NAME, &composite_tmp, &composite_tmp, &composite_tmp))
00268   {
00269     results->error = strdup ("no composite extension");
00270     results->result = 1;
00271     return 0;
00272   }
00273 
00274   XCompositeQueryVersion (display, &composite_major, &composite_minor);
00275 
00276   if (composite_major == 0 && composite_minor < 2)
00277   {
00278     results->error = strdup ("old composite extension");
00279     results->result = 1;
00280     return 0;
00281   }
00282 
00283   return 1;
00284 }
00285 
00286 static int check_damage_extension (Display     *display,
00287                                  unsigned int screen,
00288                                 Window      root,
00289                                 NUXContext  *context,
00290                                 XVisualInfo **vinfos,
00291                                 TestResults  *results)
00292 {
00293   int damage_tmp;
00294 
00295   if (!XDamageQueryExtension (display, &damage_tmp, &damage_tmp))
00296   {
00297     results->error = strdup ("no damage extension");
00298     results->result = 1;
00299     return 0;
00300   }
00301 
00302   return 1;
00303 }
00304 
00305 static int check_fixes_extension (Display     *display,
00306                                 unsigned int screen,
00307                                Window      root,
00308                                NUXContext  *context,
00309                                XVisualInfo **vinfos,
00310                                TestResults  *results)
00311 {
00312   int fixes_tmp;
00313 
00314   if (!XFixesQueryExtension (display, &fixes_tmp, &fixes_tmp))
00315   {
00316     results->error = strdup ("no fixes extension");
00317     results->result = 1;
00318     return 0;
00319   }
00320 
00321   return 1;
00322 }
00323 
00324 #ifdef NUX_OPENGLES_20
00325 
00326 static int check_egl_config (Display     *display,
00327                            unsigned int screen,
00328                            Window      root,
00329                            NUXContext  *context,
00330                            XVisualInfo **vinfos,
00331                            TestResults  *results)
00332 {
00333   EGLint attribs[] = {
00334     EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
00335     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
00336     EGL_RED_SIZE, 1,
00337     EGL_GREEN_SIZE, 1,
00338     EGL_BLUE_SIZE, 1,
00339     EGL_NONE
00340   };
00341   EGLint ctx_attribs[] = {
00342     EGL_CONTEXT_CLIENT_VERSION, 2,
00343     EGL_NONE
00344   };
00345 
00346   XVisualInfo *visInfo, visTemplate;
00347   XSetWindowAttributes attr;
00348   Window win;
00349   int num_visuals;
00350   unsigned long mask;
00351   const int width = 400, height = 300;
00352   const char* name = "Unity Support Test";
00353   EGLDisplay egl_dpy;
00354   EGLSurface egl_surf;
00355   EGLContext egl_ctx;
00356   EGLConfig config;
00357   EGLint num_configs;
00358   EGLint vid;
00359 
00360   egl_dpy = eglGetDisplay(display);
00361 
00362   if (!eglChooseConfig (egl_dpy, attribs, &config, 1, &num_configs)) {
00363     results->error = strdup ("OpenGLES: couldn't get an EGL visual config");
00364     results->result = 1;
00365     return 0;
00366   }
00367 
00368   if (num_configs <= 0) {
00369     results->error = strdup ("OpenGLES: no valid config found (!num_configs)");
00370     results->result = 1;
00371     return 0;
00372   }
00373 
00374   if (!eglGetConfigAttrib (egl_dpy, config, EGL_NATIVE_VISUAL_ID, &vid)) {
00375     results->error = strdup ("OpenGLES: eglGetConfigAttrib() failed");
00376     results->result = 1;
00377     return 0;
00378   }
00379 
00380   /* The X window visual must match the EGL config */
00381   visTemplate.visualid = vid;
00382   visInfo = XGetVisualInfo (display, VisualIDMask, &visTemplate, &num_visuals);
00383   if (!visInfo) {
00384     results->error = strdup ("OpenGLES: unable to get a matching X visual");
00385     results->result = 1;
00386     return 0;
00387   }
00388 
00389   /* window attributes */
00390   attr.background_pixel = 0;
00391   attr.border_pixel = 0;
00392   attr.colormap = XCreateColormap (display, root, visInfo->visual, AllocNone);
00393   attr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask;
00394   mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
00395 
00396   win = XCreateWindow (display, root, 0, 0, width, height,
00397                        0, visInfo->depth, InputOutput,
00398                        visInfo->visual, mask, &attr);
00399 
00400   /* set hints and properties */
00401   {
00402     XSizeHints sizehints;
00403     sizehints.x = 0;
00404     sizehints.y = 0;
00405     sizehints.width  = width;
00406     sizehints.height = height;
00407     sizehints.flags = USSize | USPosition;
00408     XSetNormalHints (display, win, &sizehints);
00409     XSetStandardProperties (display, win, name, name,
00410                             None, (char **)NULL, 0, &sizehints);
00411   }
00412 
00413   eglBindAPI (EGL_OPENGL_ES_API);
00414 
00415   egl_surf = eglCreateWindowSurface (egl_dpy, config, win, NULL);
00416   if (egl_surf == EGL_NO_SURFACE) {
00417     results->error = strdup ("OpenGLES: eglCreateWindowSurface failed");
00418     results->result = 1;
00419     return 0;
00420   }
00421 
00422   context = eglCreateContext (egl_dpy, config, EGL_NO_CONTEXT, ctx_attribs);
00423   if (context == EGL_NO_CONTEXT) {
00424     results->error = strdup ("OpenGLES: eglCreateContext failed");
00425     results->result = 1;
00426     return 0;
00427   }
00428 
00429   if (!eglMakeCurrent (egl_dpy, egl_surf, egl_surf, context)) {
00430     results->error = strdup ("OpenGLES: eglMakeCurrent() failed");
00431     results->result = 1;
00432     return 0;
00433   }
00434 
00435   XFree(visInfo);
00436   eglDestroySurface (egl_dpy, egl_surf);
00437   XDestroyWindow (display, win);
00438 
00439   return 1;
00440 }
00441 
00442 #else
00443 
00444 static int check_glx_config (Display     *display,
00445                            unsigned int screen,
00446                            Window      root,
00447                            NUXContext  *context,
00448                            XVisualInfo **vinfos,
00449                            TestResults  *results)
00450 {
00451   // Check root window visual capabilities.
00452   int value;
00453   glXGetConfig (display, *vinfos, GLX_USE_GL, &value);
00454   if (value == 0) {
00455     results->error = strdup ("OpenGL rendering is not supported by the root visual");
00456     results->result = 1;
00457     return 0;
00458   }
00459 
00460   return 1;
00461 }
00462 
00463 static int check_colorbuffers (Display     *display,
00464                              unsigned int screen,
00465                             Window      root,
00466                             NUXContext  *context,
00467                             XVisualInfo **vinfos,
00468                             TestResults  *results)
00469 {
00470   int value;
00471   glXGetConfig (display,*vinfos, GLX_DOUBLEBUFFER, &value);
00472   if (value == 0) {
00473     results->error = strdup ("color buffers of the root visual are not double-buffered");
00474     results->result = 1;
00475     return 0;
00476   }
00477 
00478   return 1;
00479 }
00480 #endif
00481 
00482 static int check_context (Display     *display,
00483                         unsigned int screen,
00484                         Window      root,
00485                         NUXContext  *context,
00486                         XVisualInfo **vinfos,
00487                         TestResults  *results)
00488 {
00489 #ifndef NUX_OPENGLES_20
00490   // Create and map the OpenGL context to the root window and get the strings.
00491   *context = glXCreateContext (display, *vinfos, NULL, !results->indirect);
00492   if (*context == NULL) {
00493     results->error = strdup ("unable to create the OpenGL context");
00494     results->result = 1;
00495     return 0;
00496   }
00497   glXMakeCurrent (display, root, *context);
00498 #endif
00499 
00500   results->vendor = (char*) glGetString (GL_VENDOR);
00501   results->renderer = (char*) glGetString (GL_RENDERER);
00502   results->version = (char*) glGetString (GL_VERSION);
00503 
00504   return 1;
00505 }
00506 
00507 static int check_extensions (Display     *display,
00508                           unsigned int screen,
00509                           Window      root,
00510                           NUXContext  *context,
00511                           XVisualInfo **vinfos,
00512                           TestResults  *results)
00513 {
00514   // Fill flags with the supported OpenGL extensions.
00515   const char* gl_extensions = glGetString (GL_EXTENSIONS);
00516   if (gl_extensions == NULL) {
00517     results->error = strdup ("invalid OpenGL extensions string");
00518     results->result = 1;
00519     return 0;
00520   }
00521   const struct { const char* name; const unsigned int flag; } gl_extension[] = {
00522     { "GL_ARB_texture_non_power_of_two", FLAG_GL_ARB_NON_POWER_OF_TWO },
00523     { "GL_NV_texture_rectangle", FLAG_GL_NV_TEXTURE_RECTANGLE },
00524     { "GL_EXT_texture_rectangle", FLAG_GL_EXT_TEXTURE_RECTANGLE },
00525     { "GL_ARB_texture_rectangle", FLAG_GL_ARB_TEXTURE_RECTANGLE },
00526     { "GL_ARB_vertex_program", FLAG_GL_ARB_VERTEX_PROGRAM },
00527     { "GL_ARB_fragment_program", FLAG_GL_ARB_FRAGMENT_PROGRAM },
00528     { "GL_ARB_vertex_buffer_object", FLAG_GL_ARB_VERTEX_BUFFER_OBJECT },
00529     { "GL_EXT_framebuffer_object", FLAG_GL_EXT_FRAMEBUFFER_OBJECT },
00530     { "GL_ARB_framebuffer_object", FLAG_GL_ARB_FRAMEBUFFER_OBJECT },
00531     { "GL_OES_EGL_image", FLAG_GL_OES_EGL_IMAGE },
00532     { NULL, 0 }
00533   };
00534   for (int i = 0; gl_extension[i].name != NULL; i++)
00535     if (is_extension_supported (gl_extensions, gl_extension[i].name) == 1)
00536       results->flags |= gl_extension[i].flag;
00537 
00538 #ifndef NUX_OPENGLES_20
00539   // Fill results->flags with the supported GLX extensions.
00540   const char* glx_extensions = glXQueryExtensionsString (display, screen);
00541   const struct { const char* name; const unsigned int flag; } glx_extension[] = {
00542     { "GLX_SGIX_fbconfig", FLAG_GLX_SGIX_FBCONFIG },
00543     { "GLX_EXT_texture_from_pixmap", FLAG_GLX_EXT_TEXTURE_FROM_PIXMAP },
00544     { NULL, 0 }
00545   };
00546   for (int i = 0; glx_extension[i].name != NULL; i++)
00547     if (is_extension_supported (glx_extensions, glx_extension[i].name) == 1)
00548       results->flags |= glx_extension[i].flag;
00549   if (results->flags & FLAG_GLX_SGIX_FBCONFIG) {
00550     if (glXGetProcAddressARB ("glXQueryDrawable") == NULL ||
00551         glXGetProcAddressARB ("glXGetFBConfigs") == NULL ||
00552         glXGetProcAddressARB ("glXGetFBConfigAttrib") == NULL ||
00553         glXGetProcAddressARB ("glXCreatePixmap") == NULL ||
00554         glXGetProcAddressARB ("glXDestroyPixmap") == NULL) {
00555       results->flags &= ~FLAG_GLX_SGIX_FBCONFIG;
00556     }
00557   }
00558   if (results->flags & FLAG_GLX_EXT_TEXTURE_FROM_PIXMAP) {
00559     if (glXGetProcAddressARB ("glXBindTexImageEXT") == NULL ||
00560         glXGetProcAddressARB ("glXReleaseTexImageEXT") == NULL) {
00561       results->flags &= ~FLAG_GLX_EXT_TEXTURE_FROM_PIXMAP;
00562     }
00563   }
00564 #else
00565   EGLDisplay egl_dpy = eglGetDisplay(display);
00566   const char* egl_extensions = eglQueryString (egl_dpy, EGL_EXTENSIONS);
00567   const struct { const char* name; const unsigned int flag; } egl_extension[] = {
00568     { "EGL_KHR_image_pixmap", FLAG_EGL_KHR_IMAGE_PIXMAP },
00569     { NULL, 0 }
00570   };
00571   for (int i = 0; egl_extension[i].name != NULL; i++)
00572     if (is_extension_supported (egl_extensions, egl_extension[i].name) == 1)
00573       results->flags |= egl_extension[i].flag;
00574 #endif
00575 
00576   return 1;
00577 }
00578 
00579 static int check_blacklist (Display     *display,
00580                          unsigned int screen,
00581                          Window      root,
00582                          NUXContext  *context,
00583                          XVisualInfo **vinfos,
00584                          TestResults  *results)
00585 {
00586   // Check for software rendering.
00587   if (results->renderer != NULL &&
00588       (strncmp (results->renderer, "Software Rasterizer", 19) == 0 ||
00589        strncmp (results->renderer, "Mesa X11", 8) == 0 ||
00590        strstr (results->renderer, "on softpipe") != NULL)) {
00591     results->flags |= FLAG_SOFTWARE_RENDERING;
00592   }
00593 
00594   // jaytaoko: Balcklist the Geforce FX cards
00595   if (results->renderer != NULL) {
00596     char* str = strstr (results->renderer, "GeForce FX");
00597     if (str != NULL) {
00598       results->flags |= FLAG_BLACKLISTED;
00599     }
00600   }
00601 
00602   // FIXME(loicm): Compiz does a last check to test whether there's a fbconfig
00603   //     available for the default depth or not.
00604 
00605   // Scan the PCI devices searching for blacklisted GPUs.
00606 // FIXME: pci or not is not actually related with PCI, it's just that if pci_init
00607 // fails it exit directly :-(
00608 #ifndef NUX_OPENGLES_20
00609   const int gpu_blacklist_size = ARRAY_SIZE (gpu_blacklist);
00610   struct pci_access* access;
00611   struct pci_dev* dev;
00612   access = pci_alloc ();
00613   pci_init (access);
00614   pci_scan_bus (access);
00615   dev = access->devices;
00616   while (dev != NULL) {
00617     pci_fill_info (dev, PCI_FILL_IDENT);
00618     for (int i = 0; i < gpu_blacklist_size; i++) {
00619       if (dev->vendor_id == gpu_blacklist[i].vendor &&
00620           dev->device_id == gpu_blacklist[i].device) {
00621         results->flags |= FLAG_BLACKLISTED;
00622       }
00623     }
00624     dev = dev->next;
00625   }
00626   pci_cleanup (access);
00627 #endif
00628 
00629   return 1;
00630 }
00631 
00632 int (*tests[]) (Display     *display,
00633                     unsigned int screen,
00634                     Window      root,
00635                     NUXContext  *context,
00636                     XVisualInfo **vinfos,
00637                     TestResults  *results) = {
00638   check_root_visual,
00639   check_xcomposite,
00640   check_damage_extension,
00641   check_fixes_extension,
00642 #ifndef NUX_OPENGLES_20
00643   check_glx_config,
00644   check_colorbuffers,
00645 #else
00646   check_egl_config,
00647 #endif
00648   check_context,
00649   check_extensions,
00650   check_blacklist
00651 };
00652 
00653 #ifndef NUX_OPENGLES_20
00654 const unsigned int c_num_tests = 9;
00655 #else
00656 const unsigned int c_num_tests = 8;
00657 #endif
00658 
00659 int main (int argc, char* argv[]) {
00660   char         *display_name = NULL;
00661   int          screen;
00662   unsigned int print = 0;
00663   Window       root;
00664   XVisualInfo  *vinfos = NULL;
00665   Display      *display = NULL;
00666   NUXContext   context = NULL;
00667   TestResults  results;
00668 #ifdef NUX_OPENGLES_20
00669   EGLDisplay   egl_dpy;
00670 #endif
00671   char         resultfilename[30];
00672   int          resultfile;
00673   int          forcecheck = 0;
00674 
00675   results.indirect = 0;
00676   results.compiz = 0;
00677   results.flags = 0;
00678   results.error = NULL;
00679 
00680   // Basic command-line parsing.
00681   for (int i = 1; i < argc; i++) {
00682     if (((strncmp (argv[i], "-d", 2) == 0) ||
00683          (strncmp (argv[i], "--display", 9) == 0)) &&
00684         (i + 1 < argc)) {
00685       display_name = argv[i + 1];
00686       i++;
00687     } else if ((strncmp (argv[i], "-i", 2) == 0) ||
00688                (strncmp (argv[i], "--indirect", 10) == 0)) {
00689       results.indirect = 1;
00690     } else if ((strncmp (argv[i], "-p", 2) == 0) ||
00691                (strncmp (argv[i], "--print", 7) == 0)) {
00692       print = 1;
00693     } else if ((strncmp (argv[i], "-c", 2) == 0) ||
00694                (strncmp (argv[i], "--compiz", 8) == 0)) {
00695       results.compiz = 1;
00696     } else if ((strncmp (argv[i], "-f", 2) == 0) ||
00697                (strncmp (argv[i], "--force-check", 13) == 0)) {
00698       forcecheck = 1;
00699     } else if ((strncmp (argv[i], "-h", 2) == 0) ||
00700                (strncmp (argv[i], "--help", 6) == 0)) {
00701       print_help ();
00702       return 2;
00703     } else {
00704       fprintf (stderr, "Error: unknown command-line option `%s'\n\n", argv[i]);
00705       print_help ();
00706       return 2;
00707     }
00708   }
00709 
00710   // can skip some tests if not forced
00711   if (!forcecheck && !print) {
00712       if (access("/tmp/unity_support_test.0", F_OK) == 0) {
00713           return 0;
00714       }
00715       if (getenv ("UNITY_FORCE_START")) {
00716           fprintf (stdout, "Warning: UNITY_FORCE_START enabled, no check for unity or compiz support.\n");
00717           return 0;
00718       }
00719   }
00720 
00721   // Open a X11 connection and get the root window.
00722   display = XOpenDisplay (display_name);
00723   
00724 #ifndef NUX_OPENGLES_20  
00725   // Before doing anything with GLX, check that it is supported on the system.
00726   Bool glx_supported = False;
00727   int dummy0, dummy1;
00728   if (display)
00729     glx_supported = glXQueryExtension(display, &dummy0, &dummy1);
00730 
00731 #endif
00732 
00733   if (!display) {
00734     results.error = strdup ("unable to open display");
00735     // exit with 5, to tell "it's not an error we should cache"
00736     results.result = 5;
00737   }
00738 #ifndef NUX_OPENGLES_20
00739   else if (!glx_supported) {
00740     results.error = strdup ("GLX is not available on the system");
00741     // exit with 5, to tell "it's not an error we should cache"
00742     results.result = 5;
00743   }
00744 #endif
00745   else
00746   {
00747     screen = DefaultScreen (display);
00748     root = XRootWindow (display, screen);
00749 
00750 #ifndef NUX_OPENGLES_20
00751     // Do the tests, if one of them fails break out of the loop
00752 
00753     for (unsigned int i = 0; i < c_num_tests; i++)
00754       if (!(*tests[i]) (display, screen, root, &context, &vinfos, &results))
00755         break;
00756 
00757     if (results.compiz == 0) {
00758       // Unity compatibility checks.
00759       get_opengl_version (results.version, &results.major, &results.minor);
00760       if ((results.major >= 2 || (results.major == 1 && results.minor >= 4)) &&
00761           (results.flags & FLAG_GLX_SGIX_FBCONFIG) &&
00762           (results.flags & FLAG_GLX_EXT_TEXTURE_FROM_PIXMAP) &&
00763           (results.flags & MASK_GL_NON_POWER_OF_TWO) &&
00764           (results.flags & FLAG_GL_ARB_VERTEX_PROGRAM) &&
00765           (results.flags & FLAG_GL_ARB_FRAGMENT_PROGRAM) &&
00766           (results.flags & FLAG_GL_ARB_VERTEX_BUFFER_OBJECT) &&
00767           (results.flags & MASK_GL_FRAMEBUFFER_OBJECT) &&
00768           (~results.flags & FLAG_SOFTWARE_RENDERING) &&
00769           (~results.flags & FLAG_BLACKLISTED)) {
00770         results.result = 0;
00771       } else {
00772         results.result = 1;
00773       }
00774     } else {
00775       // Compiz compatibility checks.
00776       if ((results.flags & FLAG_GLX_SGIX_FBCONFIG) &&
00777           (results.flags & FLAG_GLX_EXT_TEXTURE_FROM_PIXMAP) &&
00778           (results.flags & MASK_GL_NON_POWER_OF_TWO) &&
00779           (~results.flags & FLAG_SOFTWARE_RENDERING) &&
00780           (~results.flags & FLAG_BLACKLISTED)) {
00781         results.result = 0;
00782       } else {
00783         results.result = 1;
00784       }
00785     }
00786 #else
00787     egl_dpy = eglGetDisplay(display);
00788     if (!eglInitialize (egl_dpy, &results.major, &results.minor)) {
00789       results.error = strdup ("OpenGLES: eglInitialize() failed");
00790       results.result = 1;
00791     } else {
00792       // Do the tests, if one of them fails break out of the loop
00793       for (unsigned int i = 0; i < c_num_tests; i++)
00794         if (!(*tests[i]) (display, screen, root, &context, &vinfos, &results))
00795           break;
00796 
00797       if (results.compiz == 0) {
00798         // Unity compatibility checks.
00799         if ((results.major >= 2 || (results.major == 1 && results.minor >= 4)) &&
00800             (results.flags & FLAG_GL_OES_EGL_IMAGE) &&
00801             (results.flags & FLAG_EGL_KHR_IMAGE_PIXMAP) &&
00802             (~results.flags & FLAG_SOFTWARE_RENDERING) &&
00803             (~results.flags & FLAG_BLACKLISTED)) {
00804           results.result = 0;
00805         } else {
00806           results.result = 1;
00807         }
00808       } else {
00809         // Compiz compatibility checks.
00810         if ((results.flags & FLAG_GL_OES_EGL_IMAGE) &&
00811             (results.flags & FLAG_EGL_KHR_IMAGE_PIXMAP) &&
00812             (~results.flags & FLAG_SOFTWARE_RENDERING) &&
00813             (~results.flags & FLAG_BLACKLISTED)) {
00814           results.result = 0;
00815         } else {
00816           results.result = 1;
00817         }
00818       }
00819     }
00820 #endif
00821   }
00822 
00823   if (print == 1) {
00824     print_report (results.vendor,  results.renderer,
00825                 results.version, results.major,
00826                 results.minor,   results.flags,
00827                 results.compiz,  results.result, results.error);
00828   }
00829 
00830   if (vinfos != NULL)
00831     XFree (vinfos);
00832 #ifndef NUX_OPENGLES_20
00833   if (context != NULL)
00834     glXDestroyContext (display, context);
00835 #else
00836   if (context == EGL_NO_CONTEXT)
00837     eglDestroyContext (egl_dpy, context);
00838   if (egl_dpy)
00839     eglTerminate (egl_dpy);
00840 #endif
00841   if (display != NULL)
00842     XCloseDisplay (display);
00843   if (results.error != NULL)
00844     free (results.error);
00845 
00846   // drop result file
00847   if (results.result != 5) {
00848     snprintf(resultfilename, sizeof(resultfilename), "/tmp/unity_support_test.%i", results.result);
00849     resultfile = open(resultfilename, O_CREAT|O_WRONLY|O_EXCL, 0666);
00850     if (resultfile > 0)
00851       close(resultfile);
00852   }
00853 
00854   return results.result;
00855 }