glx: initial plumbing to let users force-enable/disable extensions

This can be useful for debugging or working around bugs such as
Mesa#106 where Wine is expecting to find a visual that isn't
available.

v2:
 - split the indirect GL extension override to its own commit
 - memset the bitfields to 0 in __glXExtensionsCtrScreen

Reviewed-by: Adam Jackson <ajax@redhat.com>

v3:
 - slight rework necessary after splitting the computation of usable
   extensions (Ian)

Reviewed-by: Ian Romanick <ian.d.romanick@intel.com>
Signed-off-by: Martin Peres <martin.peres@mupuf.org>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/7212>
diff --git a/src/glx/glxclient.h b/src/glx/glxclient.h
index f4ffff4..42285b9 100644
--- a/src/glx/glxclient.h
+++ b/src/glx/glxclient.h
@@ -546,6 +546,9 @@
    /*@{ */
    unsigned char direct_support[__GLX_EXT_BYTES];
    GLboolean ext_list_first_time;
+
+   unsigned char glx_force_enabled[__GLX_EXT_BYTES];
+   unsigned char glx_force_disabled[__GLX_EXT_BYTES];
    /*@} */
 
 };
diff --git a/src/glx/glxextensions.c b/src/glx/glxextensions.c
index ba14487..2b5280f 100644
--- a/src/glx/glxextensions.c
+++ b/src/glx/glxextensions.c
@@ -34,6 +34,7 @@
 #include <string.h>
 #include "glxextensions.h"
 
+#include "util/driconf.h"
 
 #define SET_BIT(m,b)   (m[ (b) / 8 ] |=  (1U << ((b) % 8)))
 #define CLR_BIT(m,b)   (m[ (b) / 8 ] &= ~(1U << ((b) % 8)))
@@ -455,6 +456,74 @@
                      name, strlen(name), GL_TRUE, psc->direct_support);
 }
 
+static void
+__ParseExtensionOverride(struct glx_screen *psc,
+                         const struct extension_info *ext_list,
+                         unsigned char *force_enable,
+                         unsigned char *force_disable,
+                         const char *override)
+{
+   const struct extension_info *ext;
+   char *env, *field;
+
+   if (override == NULL)
+       return;
+
+   /* Copy env_const because strtok() is destructive. */
+   env = strdup(override);
+   if (env == NULL)
+      return;
+
+   for (field = strtok(env, " "); field!= NULL; field = strtok(NULL, " ")) {
+      GLboolean enable;
+
+      switch (field[0]) {
+      case '+':
+         enable = GL_TRUE;
+         ++field;
+         break;
+      case '-':
+         enable = GL_FALSE;
+         ++field;
+         break;
+      default:
+         enable = GL_TRUE;
+         break;
+      }
+
+      ext = find_extension(ext_list, field, strlen(field));
+      if (ext) {
+         if (enable)
+            SET_BIT(force_enable, ext->bit);
+         else
+            SET_BIT(force_disable, ext->bit);
+      } else {
+         fprintf(stderr, "WARNING: Trying to %s the unknown extension '%s'\n",
+                 enable ? "enable" : "disable", field);
+      }
+   }
+}
+
+/**
+ * \brief Parse the list of GLX extensions that the user wants to
+ * force-enable/disable by using \c override, and write the results to the
+ * screen's context.
+ *
+ * \param psc        Pointer to GLX per-screen record.
+ * \param override   A space-separated list of extensions to enable or disable.
+ * The list is processed thus:
+ *    - Enable recognized extension names that are prefixed with '+'.
+ *    - Disable recognized extension names that are prefixed with '-'.
+ *    - Enable recognized extension names that are not prefixed.
+ */
+void
+__glXParseExtensionOverride(struct glx_screen *psc, const char *override)
+{
+    __ParseExtensionOverride(psc, known_glx_extensions, psc->glx_force_enabled,
+                             psc->glx_force_disabled, override);
+}
+
+
 /**
  * Initialize global extension support tables.
  */
@@ -530,6 +599,10 @@
       psc->ext_list_first_time = GL_FALSE;
       (void) memcpy(psc->direct_support, direct_glx_support,
                     sizeof(direct_glx_support));
+      (void) memset(psc->glx_force_enabled, 0,
+                    sizeof(psc->glx_force_enabled));
+      (void) memset(psc->glx_force_disabled, 0,
+                    sizeof(psc->glx_force_disabled));
    }
 }
 
@@ -707,8 +780,13 @@
          u |= client_glx_support[i] & psc->direct_support[i] &
                  (server_support[i] | direct_glx_only[i]);
 
-         usable[i] = u;
+         /* Finally, apply driconf options to force some extension bits either
+          * enabled or disabled.
+          */
+         u |= psc->glx_force_enabled[i];
+         u &= ~psc->glx_force_disabled[i];
 
+         usable[i] = u;
       }
    }
    else {
@@ -721,6 +799,12 @@
          /* Enable extensions that the client and server both support */
          u |= client_glx_support[i] & server_support[i];
 
+         /* Finally, apply driconf options to force some extension bits either
+          * enabled or disabled.
+          */
+         u |= psc->glx_force_enabled[i];
+         u &= ~psc->glx_force_disabled[i];
+
          usable[i] = u;
       }
    }
diff --git a/src/glx/glxextensions.h b/src/glx/glxextensions.h
index 96bc753..abcac95 100644
--- a/src/glx/glxextensions.h
+++ b/src/glx/glxextensions.h
@@ -265,6 +265,8 @@
                                            display_is_direct_capable,
                                            int server_minor_version);
 
+extern void __glXParseExtensionOverride(struct glx_screen *psc,
+                                        const char *override);
 extern void __glXCalculateUsableGLExtensions(struct glx_context *gc,
                                              const char *server_string,
                                              int major_version,