diff libinterp/corefcn/gl-render.cc @ 25862:e5a73a8c116c

use wrapper class to call OpenGL functions The motivation for this change is to allow calling OpenGL functions through the Qt wrapper classes (QOpenGLFunctions and related classes) so that we can fall back to a software implementation of OpenGL (at least on Windows systems when using Qt) as described here http://doc.qt.io/qt-5/windows-requirements.html in the section "Dynamically Loading Graphics Drivers". However, we can't use the Qt wrappers directly since we also need to use OpenGL functions from the FLTK graphics widget. The new opengl_functions base class and the qopengl_functions class derived from it allows both the Qt and FLTK graphics widgets do continue using a common set of classes (opengl_render, etc.) for most OpenGL rendering. * oct-opengl.h (opengl_functions): New class. Forward calls to OpenGL functions. Don't define anything unless HAVE_OPENGL is defined. * gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, gl-select.cc, gl-select.h: Fix constructors to accept opengl_functions object. Change all uses. Store reference to opengl_functions in all classes that call OpenGL functions. Use opengl_functions wrapper object to call all OpenGL functions. * gl-render.h, gl-render.cc (opengl_renderer::get_opengl_functions): New function. (opengl_renderer::m__max_lights): New data member. (opengl_renderer::init_maxlights): New member function to replace static function get_maxlights. (opengl_renderer::get_string): New member function to replace static function gl_get_string. * __init_fltk__.cc (OpenGL_fltk::m_glfcns): New opengl_functions data member. Use wrapper object to call all OpenGL functions. * libgui/graphics/qopengl-functions.h: New file. * libgui/graphics/module.mk: Update. * acinclude.m4 (OCTAVE_CHECK_QT_OPENGL_OK): Check for QGLFunctions_1_1 header file. * GLCanvas.cc, GLCanvas.h (GLCanvas::m_glfcns): New qopengl_functions data member. Use wrapper object to call all OpenGL functions. (GLCanvas::initializeGL): Initialize qopengl_functions object. (GLCanvas::drawZoomRect): New member function to replace static function glDrawZoomBox. Change all uses.
author John W. Eaton <jwe@octave.org>
date Thu, 06 Sep 2018 16:29:56 -0400
parents 8b548f2f8086
children 8a6bf76abf31
line wrap: on
line diff
--- a/libinterp/corefcn/gl-render.cc	Fri Sep 07 09:48:33 2018 -0700
+++ b/libinterp/corefcn/gl-render.cc	Thu Sep 06 16:29:56 2018 -0400
@@ -96,28 +96,31 @@
     class texture_rep
     {
     public:
-      texture_rep (void)
-        : id (), w (), h (), tw (), th (), tx (), ty (),
+      texture_rep (opengl_functions& glfcns)
+        : m_glfcns (glfcns), id (), w (), h (), tw (), th (), tx (), ty (),
           valid (false), count (1)
       { }
 
-      texture_rep (GLuint id_arg, int w_arg, int h_arg, int tw_arg, int th_arg)
-        : id (id_arg), w (w_arg), h (h_arg), tw (tw_arg), th (th_arg),
-          tx (double(w)/tw), ty (double(h)/th), valid (true),
-          count (1) { }
+      texture_rep (opengl_functions& glfcns, GLuint id_arg,
+                   int w_arg, int h_arg, int tw_arg, int th_arg)
+        : m_glfcns (glfcns), id (id_arg), w (w_arg), h (h_arg),
+          tw (tw_arg), th (th_arg), tx (double(w)/tw), ty (double(h)/th),
+          valid (true), count (1)
+      { }
 
       ~texture_rep (void)
       {
         if (valid)
-          glDeleteTextures (1, &id);
+          m_glfcns.glDeleteTextures (1, &id);
       }
 
       void bind (int mode) const
-      { if (valid) glBindTexture (mode, id); }
+      { if (valid) m_glfcns.glBindTexture (mode, id); }
 
       void tex_coord (double q, double r) const
-      { if (valid) glTexCoord2d (q*tx, r*ty); }
-
+      { if (valid) m_glfcns.glTexCoord2d (q*tx, r*ty); }
+
+      opengl_functions& m_glfcns;
       GLuint id;
       int w, h;
       int tw, th;
@@ -132,7 +135,8 @@
     opengl_texture (texture_rep *_rep) : rep (_rep) { }
 
   public:
-    opengl_texture (void) : rep (new texture_rep ()) { }
+    opengl_texture (opengl_functions& glfcns)
+      : rep (new texture_rep (glfcns)) { }
 
     opengl_texture (const opengl_texture& tx)
       : rep (tx.rep)
@@ -157,7 +161,8 @@
       return *this;
     }
 
-    static opengl_texture create (const octave_value& data);
+    static opengl_texture create (opengl_functions& glfcns,
+                                  const octave_value& data);
 
     void bind (int mode = GL_TEXTURE_2D) const
     { rep->bind (mode); }
@@ -170,9 +175,9 @@
   };
 
   opengl_texture
-  opengl_texture::create (const octave_value& data)
+  opengl_texture::create (opengl_functions& glfcns, const octave_value& data)
   {
-    opengl_texture retval;
+    opengl_texture retval (glfcns);
 
     dim_vector dv (data.dims ());
 
@@ -189,8 +194,8 @@
         tw = next_power_of_2 (w);
         th = next_power_of_2 (h);
 
-        glGenTextures (1, &id);
-        glBindTexture (GL_TEXTURE_2D, id);
+        glfcns.glGenTextures (1, &id);
+        glfcns.glBindTexture (GL_TEXTURE_2D, id);
 
         if (data.is_double_type ())
           {
@@ -208,7 +213,7 @@
                   }
               }
 
-            glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a);
+            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0, GL_RGB, GL_FLOAT, a);
           }
         else if (data.is_uint8_type ())
           {
@@ -226,8 +231,8 @@
                   }
               }
 
-            glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
-                          GL_RGB, GL_UNSIGNED_BYTE, a);
+            glfcns.glTexImage2D (GL_TEXTURE_2D, 0, 3, tw, th, 0,
+                                 GL_RGB, GL_UNSIGNED_BYTE, a);
           }
         else
           {
@@ -237,13 +242,13 @@
 
         if (ok)
           {
-            glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-            glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
-            if (glGetError () != GL_NO_ERROR)
+            glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+            glfcns.glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+            if (glfcns.glGetError () != GL_NO_ERROR)
               warning ("opengl_texture::create: OpenGL error while generating texture data");
             else
-              retval = opengl_texture (new texture_rep (id, w, h, tw, th));
+              retval = opengl_texture (new texture_rep (glfcns, id, w, h, tw, th));
           }
       }
     else
@@ -449,29 +454,35 @@
   protected:
     void begin (GLenum type)
     {
+      opengl_functions& glfcns = renderer->get_opengl_functions ();
+
       //printf ("patch_tesselator::begin (%d)\n", type);
       first = true;
 
       if (color_mode == INTERP || light_mode == GOURAUD)
-        glShadeModel (GL_SMOOTH);
+        glfcns.glShadeModel (GL_SMOOTH);
       else
-        glShadeModel (GL_FLAT);
+        glfcns.glShadeModel (GL_FLAT);
 
       if (is_filled ())
         renderer->set_polygon_offset (true, index);
 
-      glBegin (type);
+      glfcns.glBegin (type);
     }
 
     void end (void)
     {
+      opengl_functions& glfcns = renderer->get_opengl_functions ();
+
       //printf ("patch_tesselator::end\n");
-      glEnd ();
+      glfcns.glEnd ();
       renderer->set_polygon_offset (false);
     }
 
     void vertex (void *data)
     {
+      opengl_functions& glfcns = renderer->get_opengl_functions ();
+
       vertex_data::vertex_data_rep *v
         = reinterpret_cast<vertex_data::vertex_data_rep *> (data);
       //printf ("patch_tesselator::vertex (%g, %g, %g)\n", v->coords(0), v->coords(1), v->coords(2));
@@ -485,34 +496,34 @@
 
           if (col.numel () == 3)
             {
-              glColor4d (col(0), col(1), col(2), v->alpha);
+              glfcns.glColor4d (col(0), col(1), col(2), v->alpha);
               if (light_mode > 0)
                 {
                   float buf[4] = { 0, 0, 0, 1 };
 
                   for (int k = 0; k < 3; k++)
                     buf[k] = (v->ambient * col(k));
-                  glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
+                  glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, buf);
 
                   for (int k = 0; k < 3; k++)
                     buf[k] = (v->diffuse * col(k));
-                  glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf);
+                  glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, buf);
 
                   for (int k = 0; k < 3; k++)
                     buf[k] = v->specular * (v->specular_color_refl +
                                             (1 - v->specular_color_refl) * col(k));
-                  glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
+                  glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, buf);
 
                 }
             }
         }
 
       if (light_mode == FLAT && first)
-        glNormal3dv (v->face_normal.data ());
+        glfcns.glNormal3dv (v->face_normal.data ());
       else if (light_mode == GOURAUD)
-        glNormal3dv (v->vertex_normal.data ());
-
-      glVertex3dv (v->coords.data ());
+        glfcns.glNormal3dv (v->vertex_normal.data ());
+
+      glfcns.glVertex3dv (v->coords.data ());
 
       first = false;
     }
@@ -601,11 +612,12 @@
 
 #endif
 
-  opengl_renderer::opengl_renderer (void)
-    : toolkit (), xform (), xmin (), xmax (), ymin (), ymax (),
-      zmin (), zmax (), xZ1 (), xZ2 (), marker_id (), filled_marker_id (),
-      camera_pos (), camera_dir (), interpreter ("none"), txt_renderer (),
-      selecting (false)
+  opengl_renderer::opengl_renderer (opengl_functions& glfcns)
+    : m_glfcns (glfcns), toolkit (), xform (), xmin (), xmax (),
+      ymin (), ymax (), zmin (), zmax (), xZ1 (), xZ2 (),
+      marker_id (), filled_marker_id (), camera_pos (), camera_dir (),
+      view_vector (), interpreter ("none"), txt_renderer (),
+      m_current_light (0), m_max_lights (0), selecting (false)
   {
     // This constructor will fail if we don't have OpenGL or if the data
     // types we assumed in our public interface aren't compatible with the
@@ -679,7 +691,7 @@
 
 #if defined (HAVE_OPENGL)
 
-    GLenum gl_error = glGetError ();
+    GLenum gl_error = m_glfcns.glGetError ();
     if (gl_error)
       warning ("opengl_renderer: Error '%s' (%d) occurred drawing '%s' object",
                gluErrorString (gl_error), gl_error, props.graphics_object_name ().c_str ());
@@ -687,22 +699,6 @@
 #endif
   }
 
-#if defined (HAVE_OPENGL)
-
-  static std::string
-  gl_get_string (GLenum id)
-  {
-    // This is kind of ugly, but glGetString returns a pointer to GLubyte
-    // and there is no std::string constructor that matches.  Is there a
-    // better way?
-
-    std::ostringstream buf;
-    buf << glGetString (id);
-    return std::string (buf.str ());
-  }
-
-#endif
-
   void
   opengl_renderer::draw_figure (const figure::properties& props)
   {
@@ -712,10 +708,10 @@
 
 #if defined (HAVE_OPENGL)
 
-    props.set___gl_extensions__ (gl_get_string (GL_EXTENSIONS));
-    props.set___gl_renderer__ (gl_get_string (GL_RENDERER));
-    props.set___gl_vendor__ (gl_get_string (GL_VENDOR));
-    props.set___gl_version__ (gl_get_string (GL_VERSION));
+    props.set___gl_extensions__ (get_string (GL_EXTENSIONS));
+    props.set___gl_renderer__ (get_string (GL_RENDERER));
+    props.set___gl_vendor__ (get_string (GL_VENDOR));
+    props.set___gl_version__ (get_string (GL_VERSION));
 
 #endif
 
@@ -767,22 +763,22 @@
 
     // Initialize OpenGL context
 
-    glEnable (GL_DEPTH_TEST);
-    glDepthFunc (GL_LEQUAL);
-    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-    glAlphaFunc (GL_GREATER, 0.0f);
-    glEnable (GL_NORMALIZE);
+    m_glfcns.glEnable (GL_DEPTH_TEST);
+    m_glfcns.glDepthFunc (GL_LEQUAL);
+    m_glfcns.glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+    m_glfcns.glAlphaFunc (GL_GREATER, 0.0f);
+    m_glfcns.glEnable (GL_NORMALIZE);
 
     if (enhanced)
       {
-        glEnable (GL_BLEND);
-        glEnable (GL_MULTISAMPLE);
+        m_glfcns.glEnable (GL_BLEND);
+        m_glfcns.glEnable (GL_MULTISAMPLE);
         bool has_multisample = false;
-        if (! glGetError ())
+        if (! m_glfcns.glGetError ())
           {
             GLint iMultiSample, iNumSamples;
-            glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample);
-            glGetIntegerv (GL_SAMPLES, &iNumSamples);
+            m_glfcns.glGetIntegerv (GL_SAMPLE_BUFFERS, &iMultiSample);
+            m_glfcns.glGetIntegerv (GL_SAMPLES, &iNumSamples);
             if (iMultiSample == GL_TRUE && iNumSamples > 0)
               has_multisample = true;
           }
@@ -790,30 +786,30 @@
         if (! has_multisample)
           {
             // MultiSample not implemented.  Use old-style anti-aliasing
-            glDisable (GL_MULTISAMPLE);
+            m_glfcns.glDisable (GL_MULTISAMPLE);
             // Disabling GL_MULTISAMPLE will raise a gl error if it is not
             // implemented.  Thus, call glGetError to reset the error state.
-            glGetError ();
-
-            glEnable (GL_LINE_SMOOTH);
-            glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
+            m_glfcns.glGetError ();
+
+            m_glfcns.glEnable (GL_LINE_SMOOTH);
+            m_glfcns.glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
           }
       }
     else
       {
-        glDisable (GL_BLEND);
-        glDisable (GL_LINE_SMOOTH);
+        m_glfcns.glDisable (GL_BLEND);
+        m_glfcns.glDisable (GL_LINE_SMOOTH);
       }
 
     // Clear background
 
     if (c.numel () >= 3)
       {
-        glClearColor (c(0), c(1), c(2), 1);
-        glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+        m_glfcns.glClearColor (c(0), c(1), c(2), 1);
+        m_glfcns.glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
       }
 
-    GLenum gl_error = glGetError ();
+    GLenum gl_error = m_glfcns.glGetError ();
     if (gl_error)
       warning ("opengl_renderer: Error '%s' (%d) occurred in init_gl_context",
                gluErrorString (gl_error), gl_error);
@@ -841,9 +837,9 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha);
+    m_glfcns.glColor4d (gridcolor(0), gridcolor(1), gridcolor(2), gridalpha);
     set_linestyle (gridstyle, true, linewidth);
-    glBegin (GL_LINES);
+    m_glfcns.glBegin (GL_LINES);
     for (int i = 0; i < ticks.numel (); i++)
       {
         double val = ticks(i);
@@ -851,37 +847,37 @@
           {
             if (xyz == X_AXIS)
               {
-                glVertex3d (val, p1N, p2);
-                glVertex3d (val, p1, p2);
+                m_glfcns.glVertex3d (val, p1N, p2);
+                m_glfcns.glVertex3d (val, p1, p2);
                 if (is_3D)
                   {
-                    glVertex3d (val, p1, p2N);
-                    glVertex3d (val, p1, p2);
+                    m_glfcns.glVertex3d (val, p1, p2N);
+                    m_glfcns.glVertex3d (val, p1, p2);
                   }
               }
             else if (xyz == Y_AXIS)
               {
-                glVertex3d (p1N, val, p2);
-                glVertex3d (p1, val, p2);
+                m_glfcns.glVertex3d (p1N, val, p2);
+                m_glfcns.glVertex3d (p1, val, p2);
                 if (is_3D)
                   {
-                    glVertex3d (p1, val, p2N);
-                    glVertex3d (p1, val, p2);
+                    m_glfcns.glVertex3d (p1, val, p2N);
+                    m_glfcns.glVertex3d (p1, val, p2);
                   }
               }
             else if (xyz == Z_AXIS)
               {
-                glVertex3d (p1N, p2, val);
-                glVertex3d (p1, p2, val);
-                glVertex3d (p1, p2N, val);
-                glVertex3d (p1, p2, val);
+                m_glfcns.glVertex3d (p1N, p2, val);
+                m_glfcns.glVertex3d (p1, p2, val);
+                m_glfcns.glVertex3d (p1, p2N, val);
+                m_glfcns.glVertex3d (p1, p2, val);
               }
           }
       }
-    glEnd ();
+    m_glfcns.glEnd ();
     set_linestyle ("-");  // Disable LineStipple
     double black[3] = {0, 0, 0};
-    glColor3dv (black);
+    m_glfcns.glColor3dv (black);
 
 #else
 
@@ -917,7 +913,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glBegin (GL_LINES);
+    m_glfcns.glBegin (GL_LINES);
 
     for (int i = 0; i < ticks.numel (); i++)
       {
@@ -927,38 +923,38 @@
           {
             if (xyz == X_AXIS)
               {
-                glVertex3d (val, p1, p2);
-                glVertex3d (val, p1+dy, p2+dz);
+                m_glfcns.glVertex3d (val, p1, p2);
+                m_glfcns.glVertex3d (val, p1+dy, p2+dz);
                 if (mirror)
                   {
-                    glVertex3d (val, p1N, p2N);
-                    glVertex3d (val, p1N-dy, p2N-dz);
+                    m_glfcns.glVertex3d (val, p1N, p2N);
+                    m_glfcns.glVertex3d (val, p1N-dy, p2N-dz);
                   }
               }
             else if (xyz == Y_AXIS)
               {
-                glVertex3d (p1, val, p2);
-                glVertex3d (p1+dx, val, p2+dz);
+                m_glfcns.glVertex3d (p1, val, p2);
+                m_glfcns.glVertex3d (p1+dx, val, p2+dz);
                 if (mirror)
                   {
-                    glVertex3d (p1N, val, p2N);
-                    glVertex3d (p1N-dx, val, p2N-dz);
+                    m_glfcns.glVertex3d (p1N, val, p2N);
+                    m_glfcns.glVertex3d (p1N-dx, val, p2N-dz);
                   }
               }
             else if (xyz == Z_AXIS)
               {
-                glVertex3d (p1, p2, val);
-                glVertex3d (p1+dx, p2+dy, val);
+                m_glfcns.glVertex3d (p1, p2, val);
+                m_glfcns.glVertex3d (p1+dx, p2+dy, val);
                 if (mirror)
                   {
-                    glVertex3d (p1N, p2N, val);
-                    glVertex3d (p1N-dx, p2N-dy, val);
+                    m_glfcns.glVertex3d (p1N, p2N, val);
+                    m_glfcns.glVertex3d (p1N-dx, p2N-dy, val);
                   }
               }
           }
       }
 
-    glEnd ();
+    m_glfcns.glEnd ();
 
 #else
 
@@ -1058,9 +1054,9 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glPixelStorei (GL_PACK_ALIGNMENT, 1);
+    m_glfcns.glPixelStorei (GL_PACK_ALIGNMENT, 1);
     uint8NDArray pix(dim_vector (3, width, height), 0);
-    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
+    m_glfcns.glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE,
                  pix.fortran_vec ());
 
     // Permute and flip data
@@ -1094,7 +1090,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glFinish ();
+    m_glfcns.glFinish ();
 
 #else
 
@@ -1127,19 +1123,19 @@
     int vw[4];
 #endif
 
-    glGetIntegerv (GL_VIEWPORT, vw);
-
-    glMatrixMode (GL_MODELVIEW);
-    glLoadIdentity ();
-    glScaled (1, 1, -1);
-    glMultMatrixd (x_mat1.data ());
-    glMatrixMode (GL_PROJECTION);
-    glLoadIdentity ();
-    glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
-    glMultMatrixd (x_mat2.data ());
-    glMatrixMode (GL_MODELVIEW);
-
-    glClear (GL_DEPTH_BUFFER_BIT);
+    m_glfcns.glGetIntegerv (GL_VIEWPORT, vw);
+
+    m_glfcns.glMatrixMode (GL_MODELVIEW);
+    m_glfcns.glLoadIdentity ();
+    m_glfcns.glScaled (1, 1, -1);
+    m_glfcns.glMultMatrixd (x_mat1.data ());
+    m_glfcns.glMatrixMode (GL_PROJECTION);
+    m_glfcns.glLoadIdentity ();
+    m_glfcns.glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+    m_glfcns.glMultMatrixd (x_mat2.data ());
+    m_glfcns.glMatrixMode (GL_MODELVIEW);
+
+    m_glfcns.glClear (GL_DEPTH_BUFFER_BIT);
 
     // store axes transformation data
 
@@ -1178,30 +1174,30 @@
     set_color (axe_color);
     set_polygon_offset (true, 9.0);
 
-    glBegin (GL_QUADS);
+    m_glfcns.glBegin (GL_QUADS);
 
     if (! is2d)
       {
         // X plane
-        glVertex3d (xPlane, yPlaneN, zPlaneN);
-        glVertex3d (xPlane, yPlane, zPlaneN);
-        glVertex3d (xPlane, yPlane, zPlane);
-        glVertex3d (xPlane, yPlaneN, zPlane);
+        m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
+        m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
+        m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
+        m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
 
         // Y plane
-        glVertex3d (xPlaneN, yPlane, zPlaneN);
-        glVertex3d (xPlane, yPlane, zPlaneN);
-        glVertex3d (xPlane, yPlane, zPlane);
-        glVertex3d (xPlaneN, yPlane, zPlane);
+        m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
+        m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
+        m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
+        m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
       }
 
     // Z plane
-    glVertex3d (xPlaneN, yPlaneN, zPlane);
-    glVertex3d (xPlane, yPlaneN, zPlane);
-    glVertex3d (xPlane, yPlane, zPlane);
-    glVertex3d (xPlaneN, yPlane, zPlane);
-
-    glEnd ();
+    m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
+    m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
+    m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
+    m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
+
+    m_glfcns.glEnd ();
 
     set_polygon_offset (false);
 
@@ -1254,7 +1250,7 @@
     set_linecap ("square");
     set_linestyle ("-", true, linewidth);
 
-    glBegin (GL_LINES);
+    m_glfcns.glBegin (GL_LINES);
 
     if (layer2Dtop)
       std::swap (zpTick, zpTickN);
@@ -1264,22 +1260,22 @@
 
     if (! isXOrigin || props.is_box() || ! is2d)
       {
-        glVertex3d (xPlaneN, ypTick, zpTick);
-        glVertex3d (xPlane, ypTick, zpTick);
+        m_glfcns.glVertex3d (xPlaneN, ypTick, zpTick);
+        m_glfcns.glVertex3d (xPlane, ypTick, zpTick);
       }
 
     if (props.is_box ())
       {
-        glVertex3d (xPlaneN, ypTickN, zpTick);
-        glVertex3d (xPlane, ypTickN, zpTick);
+        m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTick);
+        m_glfcns.glVertex3d (xPlane, ypTickN, zpTick);
         if (! is2d)
           {
-            glVertex3d (xPlaneN, ypTickN, zpTickN);
-            glVertex3d (xPlane, ypTickN, zpTickN);
+            m_glfcns.glVertex3d (xPlaneN, ypTickN, zpTickN);
+            m_glfcns.glVertex3d (xPlane, ypTickN, zpTickN);
             if (boxFull)
               {
-                glVertex3d (xPlaneN, ypTick, zpTickN);
-                glVertex3d (xPlane, ypTick, zpTickN);
+                m_glfcns.glVertex3d (xPlaneN, ypTick, zpTickN);
+                m_glfcns.glVertex3d (xPlane, ypTick, zpTickN);
               }
           }
       }
@@ -1288,23 +1284,23 @@
     set_color (props.get_ycolor_rgb ());
     if (! isYOrigin || props.is_box() || ! is2d)
       {
-        glVertex3d (xpTick, yPlaneN, zpTick);
-        glVertex3d (xpTick, yPlane, zpTick);
+        m_glfcns.glVertex3d (xpTick, yPlaneN, zpTick);
+        m_glfcns.glVertex3d (xpTick, yPlane, zpTick);
       }
 
     if (props.is_box () && ! plotyy)
       {
-        glVertex3d (xpTickN, yPlaneN, zpTick);
-        glVertex3d (xpTickN, yPlane, zpTick);
+        m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTick);
+        m_glfcns.glVertex3d (xpTickN, yPlane, zpTick);
 
         if (! is2d)
           {
-            glVertex3d (xpTickN, yPlaneN, zpTickN);
-            glVertex3d (xpTickN, yPlane, zpTickN);
+            m_glfcns.glVertex3d (xpTickN, yPlaneN, zpTickN);
+            m_glfcns.glVertex3d (xpTickN, yPlane, zpTickN);
             if (boxFull)
               {
-                glVertex3d (xpTick, yPlaneN, zpTickN);
-                glVertex3d (xpTick, yPlane, zpTickN);
+                m_glfcns.glVertex3d (xpTick, yPlaneN, zpTickN);
+                m_glfcns.glVertex3d (xpTick, yPlane, zpTickN);
               }
           }
       }
@@ -1316,40 +1312,40 @@
 
         if (xySym)
           {
-            glVertex3d (xPlaneN, yPlane, zPlaneN);
-            glVertex3d (xPlaneN, yPlane, zPlane);
+            m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
+            m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
           }
         else
           {
-            glVertex3d (xPlane, yPlaneN, zPlaneN);
-            glVertex3d (xPlane, yPlaneN, zPlane);
+            m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
+            m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
           }
 
         if (props.is_box ())
           {
-            glVertex3d (xPlane, yPlane, zPlaneN);
-            glVertex3d (xPlane, yPlane, zPlane);
+            m_glfcns.glVertex3d (xPlane, yPlane, zPlaneN);
+            m_glfcns.glVertex3d (xPlane, yPlane, zPlane);
 
             if (xySym)
               {
-                glVertex3d (xPlane, yPlaneN, zPlaneN);
-                glVertex3d (xPlane, yPlaneN, zPlane);
+                m_glfcns.glVertex3d (xPlane, yPlaneN, zPlaneN);
+                m_glfcns.glVertex3d (xPlane, yPlaneN, zPlane);
               }
             else
               {
-                glVertex3d (xPlaneN, yPlane, zPlaneN);
-                glVertex3d (xPlaneN, yPlane, zPlane);
+                m_glfcns.glVertex3d (xPlaneN, yPlane, zPlaneN);
+                m_glfcns.glVertex3d (xPlaneN, yPlane, zPlane);
               }
 
             if (boxFull)
               {
-                glVertex3d (xPlaneN, yPlaneN, zPlaneN);
-                glVertex3d (xPlaneN, yPlaneN, zPlane);
+                m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlaneN);
+                m_glfcns.glVertex3d (xPlaneN, yPlaneN, zPlane);
               }
           }
       }
 
-    glEnd ();
+    m_glfcns.glEnd ();
 
     set_linestyle ("-");  // Disable LineStipple
 
@@ -1463,11 +1459,11 @@
         if (is_origin)
           {
             y_axis_pos = math::max (math::min (0., y_max), y_min);
-            glBegin (GL_LINES);
+            m_glfcns.glBegin (GL_LINES);
             set_color (props.get_ycolor_rgb ());
-            glVertex3d (x_min, y_axis_pos, zpTick);
-            glVertex3d (x_max, y_axis_pos, zpTick);
-            glEnd ();
+            m_glfcns.glVertex3d (x_min, y_axis_pos, zpTick);
+            m_glfcns.glVertex3d (x_max, y_axis_pos, zpTick);
+            m_glfcns.glEnd ();
           }
 
         // minor tick marks
@@ -1646,11 +1642,11 @@
         if (is_origin)
           {
             x_axis_pos = math::max (math::min (0., x_max), x_min);
-            glBegin (GL_LINES);
+            m_glfcns.glBegin (GL_LINES);
             set_color (props.get_ycolor_rgb ());
-            glVertex3d (x_axis_pos, y_min, zpTick);
-            glVertex3d (x_axis_pos, y_max, zpTick);
-            glEnd ();
+            m_glfcns.glVertex3d (x_axis_pos, y_min, zpTick);
+            m_glfcns.glVertex3d (x_axis_pos, y_max, zpTick);
+            m_glfcns.glEnd ();
           }
 
         // minor tick marks
@@ -1908,10 +1904,10 @@
     // Disable line smoothing for axes
     GLboolean antialias;
 
-    glGetBooleanv (GL_LINE_SMOOTH, &antialias);
+    m_glfcns.glGetBooleanv (GL_LINE_SMOOTH, &antialias);
 
     if (antialias == GL_TRUE)
-      glDisable (GL_LINE_SMOOTH);
+      m_glfcns.glDisable (GL_LINE_SMOOTH);
 
     set_linecap ("butt");
     set_linewidth (props.get_linewidth ());
@@ -1923,7 +1919,7 @@
     draw_axes_z_grid (props);
 
     if (antialias == GL_TRUE)
-      glEnable (GL_LINE_SMOOTH);
+      m_glfcns.glEnable (GL_LINE_SMOOTH);
 #else
 
     octave_unused_parameter (props);
@@ -1954,11 +1950,11 @@
           {
             if (go.isa ("light") && ! selecting)
               {
-                if (current_light-GL_LIGHT0 < max_lights)
+                if (m_current_light-GL_LIGHT0 < m_max_lights)
                   {
                     set_clipping (p.is_clipping ());
                     draw (go);
-                    current_light++;
+                    m_current_light++;
                   }
               }
             else if (go.isa ("hggroup")
@@ -1981,29 +1977,6 @@
 #endif
   }
 
-#if defined (HAVE_OPENGL)
-
-  static int
-  get_maxlights (void)
-  {
-    static int max_lights = 0;
-
-    // Check actual maximum number of lights possible
-    if (max_lights == 0)
-      {
-        for (max_lights = 0; max_lights < GL_MAX_LIGHTS; max_lights++)
-          {
-            glDisable (GL_LIGHT0 + max_lights);
-            if (glGetError ())
-              break;
-          }
-      }
-
-    return max_lights;
-  }
-
-#endif
-
   void
   opengl_renderer::draw_axes_children (const axes::properties& props)
   {
@@ -2019,22 +1992,22 @@
     // but this seems to lead to calls of OpenGL functions before the context
     // is actually initialized.  See bug #48669.
     // Check actual maximum number of lights possible
-    max_lights = get_maxlights ();
+    init_maxlights ();
 
     // Start with the last element of the array of child objects to
     // display them in the order they were added to the array.
 
-    if (props.get_num_lights () > max_lights)
+    if (props.get_num_lights () > m_max_lights)
       warning_with_id ("Octave:max-lights-exceeded",
                        "light: Maximum number of lights (%d) in these axes is "
-                       "exceeded.", max_lights);
-
-    current_light = GL_LIGHT0;
+                       "exceeded.", m_max_lights);
+
+    m_current_light = GL_LIGHT0;
     draw_all_lights (props, obj_list);
 
     // disable other OpenGL lights
-    for (unsigned int i = props.get_num_lights (); i < max_lights; i++)
-      glDisable (GL_LIGHT0 + i);
+    for (unsigned int i = props.get_num_lights (); i < m_max_lights; i++)
+      m_glfcns.glDisable (GL_LIGHT0 + i);
 
     // save camera position and set ambient light color before drawing
     // other objects
@@ -2044,7 +2017,7 @@
     ColumnVector ambient_color = props.get_ambientlightcolor_rgb ();
     for (int i = 0; i < 3; i++)
       cb[i] = ambient_color(i);
-    glLightfv (GL_LIGHT0, GL_AMBIENT, cb);
+    m_glfcns.glLightfv (GL_LIGHT0, GL_AMBIENT, cb);
 
     // 2nd pass: draw other objects (with units set to "data")
 
@@ -2068,7 +2041,7 @@
 
     // 3rd pass: draw remaining objects
 
-    glDisable (GL_DEPTH_TEST);
+    m_glfcns.glDisable (GL_DEPTH_TEST);
 
     for (it = obj_list.begin (); it != obj_list.end (); it++)
       {
@@ -2131,9 +2104,9 @@
     // depth sorting
     bool is2D = props.get_is2D (true);
     if (is2D)
-      glDisable (GL_DEPTH_TEST);
+      m_glfcns.glDisable (GL_DEPTH_TEST);
     else
-      glEnable (GL_DEPTH_TEST);
+      m_glfcns.glEnable (GL_DEPTH_TEST);
 
     draw_axes_planes (props);
 
@@ -2216,20 +2189,20 @@
                     if (! flag)
                       {
                         flag = true;
-                        glBegin (GL_LINE_STRIP);
-                        glVertex3d (x(i-1), y(i-1), z(i-1));
+                        m_glfcns.glBegin (GL_LINE_STRIP);
+                        m_glfcns.glVertex3d (x(i-1), y(i-1), z(i-1));
                       }
-                    glVertex3d (x(i), y(i), z(i));
+                    m_glfcns.glVertex3d (x(i), y(i), z(i));
                   }
                 else if (flag)
                   {
                     flag = false;
-                    glEnd ();
+                    m_glfcns.glEnd ();
                   }
               }
 
             if (flag)
-              glEnd ();
+              m_glfcns.glEnd ();
           }
         else
           {
@@ -2242,20 +2215,20 @@
                     if (! flag)
                       {
                         flag = true;
-                        glBegin (GL_LINE_STRIP);
-                        glVertex2d (x(i-1), y(i-1));
+                        m_glfcns.glBegin (GL_LINE_STRIP);
+                        m_glfcns.glVertex2d (x(i-1), y(i-1));
                       }
-                    glVertex2d (x(i), y(i));
+                    m_glfcns.glVertex2d (x(i), y(i));
                   }
                 else if (flag)
                   {
                     flag = false;
-                    glEnd ();
+                    m_glfcns.glEnd ();
                   }
               }
 
             if (flag)
-              glEnd ();
+              m_glfcns.glEnd ();
           }
 
         set_linewidth (0.5f);
@@ -2363,7 +2336,7 @@
     float scr = props.get_specularcolorreflectance ();
     float cb[4] = { 0.0, 0.0, 0.0, 1.0 };
 
-    opengl_texture tex;
+    opengl_texture tex (m_glfcns);
 
     int i1, i2, j1, j2;
     bool x_mat = (x.rows () == z.rows ());
@@ -2397,12 +2370,12 @@
       }
 
     if (fl_mode > 0 || el_mode > 0)
-      glMaterialf (LIGHT_MODE, GL_SHININESS, se);
+      m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
 
     // FIXME: good candidate for caching,
     //        transferring pixel data to OpenGL is time consuming.
     if (fc_mode == TEXTURE)
-      tex = opengl_texture::create (props.get_color_data ());
+      tex = opengl_texture::create (m_glfcns, props.get_color_data ());
 
     if (draw_all || ! props.facecolor_is ("none"))
       {
@@ -2411,30 +2384,30 @@
             fa = props.get_facealpha_double ();
             if (fc_mode == UNIFORM || fc_mode == TEXTURE)
               {
-                glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
+                m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
                 if (fl_mode > 0)
                   {
                     for (int i = 0; i < 3; i++)
                       cb[i] = as * fcolor(i);
-                    glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ds * fcolor(i);
-                    glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ss * (scr + (1-scr) * fcolor(i));
-                    glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                   }
               }
 
             if ((fl_mode > 0) && do_lighting)
-              glEnable (GL_LIGHTING);
-            glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD)
+              m_glfcns.glEnable (GL_LIGHTING);
+            m_glfcns.glShadeModel ((fc_mode == INTERP || fl_mode == GOURAUD)
                           ? GL_SMOOTH : GL_FLAT);
             set_polygon_offset (true, 1.0);
             if (fc_mode == TEXTURE)
-              glEnable (GL_TEXTURE_2D);
+              m_glfcns.glEnable (GL_TEXTURE_2D);
 
             for (int i = 1; i < zc; i++)
               {
@@ -2471,7 +2444,7 @@
                         j2 = j;
                       }
 
-                    glBegin (GL_QUADS);
+                    m_glfcns.glBegin (GL_QUADS);
 
                     // Vertex 1
                     if (fc_mode == TEXTURE)
@@ -2483,27 +2456,27 @@
                         for (int k = 0; k < 3; k++)
                           cb[k] = c(j-1, i-1, k);
                         cb[3] = fa;
-                        glColor4fv (cb);
+                        m_glfcns.glColor4fv (cb);
 
                         if (fl_mode > 0)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] *= as;
-                            glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ds * c(j-1, i-1, k);
-                            glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ss * (scr + (1-scr) * c(j-1, i-1, k));
-                            glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                           }
                       }
                     if (fl_mode > 0)
                       set_normal (bfl_mode, n, j-1, i-1);
 
-                    glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
+                    m_glfcns.glVertex3d (x(j1,i-1), y(j-1,i1), z(j-1,i-1));
 
                     // Vertex 2
                     if (fc_mode == TEXTURE)
@@ -2513,28 +2486,28 @@
                         for (int k = 0; k < 3; k++)
                           cb[k] = c(j-1, i, k);
                         cb[3] = fa;
-                        glColor4fv (cb);
+                        m_glfcns.glColor4fv (cb);
 
                         if (fl_mode > 0)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] *= as;
-                            glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ds * c(j-1, i, k);
-                            glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
-                            glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                           }
                       }
 
                     if (fl_mode == GOURAUD)
                       set_normal (bfl_mode, n, j-1, i);
 
-                    glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
+                    m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
 
                     // Vertex 3
                     if (fc_mode == TEXTURE)
@@ -2544,27 +2517,27 @@
                         for (int k = 0; k < 3; k++)
                           cb[k] = c(j, i, k);
                         cb[3] = fa;
-                        glColor4fv (cb);
+                        m_glfcns.glColor4fv (cb);
 
                         if (fl_mode > 0)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] *= as;
-                            glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ds * c(j, i, k);
-                            glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ss * (scr + (1-scr) * c(j, i, k));
-                            glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                           }
                       }
                     if (fl_mode == GOURAUD)
                       set_normal (bfl_mode, n, j, i);
 
-                    glVertex3d (x(j2,i), y(j,i2), z(j,i));
+                    m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
 
                     // Vertex 4
                     if (fc_mode == TEXTURE)
@@ -2574,38 +2547,38 @@
                         for (int k = 0; k < 3; k++)
                           cb[k] = c(j, i-1, k);
                         cb[3] = fa;
-                        glColor4fv (cb);
+                        m_glfcns.glColor4fv (cb);
 
                         if (fl_mode > 0)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] *= as;
-                            glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ds * c(j, i-1, k);
-                            glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                             for (int k = 0; k < 3; k++)
                               cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
-                            glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                            m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                           }
                       }
                     if (fl_mode == GOURAUD)
                       set_normal (bfl_mode, n, j, i-1);
 
-                    glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
-
-                    glEnd ();
+                    m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
+
+                    m_glfcns.glEnd ();
                   }
               }
 
             set_polygon_offset (false);
             if (fc_mode == TEXTURE)
-              glDisable (GL_TEXTURE_2D);
+              m_glfcns.glDisable (GL_TEXTURE_2D);
 
             if ((fl_mode > 0) && do_lighting)
-              glDisable (GL_LIGHTING);
+              m_glfcns.glDisable (GL_LIGHTING);
           }
         else
           {
@@ -2619,26 +2592,26 @@
           {
             if (ec_mode == UNIFORM)
               {
-                glColor3dv (ecolor.data ());
+                m_glfcns.glColor3dv (ecolor.data ());
                 if (fl_mode > 0)
                   {
                     for (int i = 0; i < 3; i++)
                       cb[i] = as * ecolor(i);
-                    glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ds * ecolor(i);
-                    glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ss * (scr + (1-scr) * ecolor(i));
-                    glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                   }
               }
 
             if ((el_mode > 0) && do_lighting)
-              glEnable (GL_LIGHTING);
-            glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
+              m_glfcns.glEnable (GL_LIGHTING);
+            m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
                           ? GL_SMOOTH : GL_FLAT);
 
             set_linestyle (props.get_linestyle (), false,
@@ -2683,63 +2656,63 @@
                             j2 = j;
                           }
 
-                        glBegin (GL_LINES);
+                        m_glfcns.glBegin (GL_LINES);
 
                         // Vertex 1
                         if (ec_mode > 0)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] = c(j-1, i, k);
-                            glColor3fv (cb);
+                            m_glfcns.glColor3fv (cb);
 
                             if (el_mode > 0)
                               {
                                 for (int k = 0; k < 3; k++)
                                   cb[k] *= as;
-                                glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ds * c(j-1, i, k);
-                                glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ss * (scr + (1-scr) * c(j-1, i, k));
-                                glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                               }
                           }
                         if (el_mode > 0)
                           set_normal (bfl_mode, n, j-1, i);
 
-                        glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
+                        m_glfcns.glVertex3d (x(j1,i), y(j-1,i2), z(j-1,i));
 
                         // Vertex 2
                         if (ec_mode == INTERP)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] = c(j, i, k);
-                            glColor3fv (cb);
+                            m_glfcns.glColor3fv (cb);
 
                             if (el_mode > 0)
                               {
                                 for (int k = 0; k < 3; k++)
                                   cb[k] *= as;
-                                glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ds * c(j, i, k);
-                                glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ss * (scr + (1-scr) * c(j, i, k));
-                                glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                               }
                           }
                         if (el_mode == GOURAUD)
                           set_normal (bfl_mode, n, j, i);
 
-                        glVertex3d (x(j2,i), y(j,i2), z(j,i));
-
-                        glEnd ();
+                        m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
+
+                        m_glfcns.glEnd ();
                       }
                   }
               }
@@ -2780,63 +2753,63 @@
                             i2 = i;
                           }
 
-                        glBegin (GL_LINES);
+                        m_glfcns.glBegin (GL_LINES);
 
                         // Vertex 1
                         if (ec_mode > 0)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] = c(j, i-1, k);
-                            glColor3fv (cb);
+                            m_glfcns.glColor3fv (cb);
 
                             if (el_mode > 0)
                               {
                                 for (int k = 0; k < 3; k++)
                                   cb[k] *= as;
-                                glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ds * c(j, i-1, k);
-                                glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ss * (scr + (1-scr) * c(j, i-1, k));
-                                glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                               }
                           }
                         if (el_mode > 0)
                           set_normal (bfl_mode, n, j, i-1);
 
-                        glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
+                        m_glfcns.glVertex3d (x(j2,i-1), y(j,i1), z(j,i-1));
 
                         // Vertex 2
                         if (ec_mode == INTERP)
                           {
                             for (int k = 0; k < 3; k++)
                               cb[k] = c(j, i, k);
-                            glColor3fv (cb);
+                            m_glfcns.glColor3fv (cb);
 
                             if (el_mode > 0)
                               {
                                 for (int k = 0; k < 3; k++)
                                   cb[k] *= as;
-                                glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ds * c(j, i, k);
-                                glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                                 for (int k = 0; k < 3; k++)
                                   cb[k] = ss * (scr + (1-scr) * c(j, i, k));
-                                glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                                m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                               }
                           }
                         if (el_mode == GOURAUD)
                           set_normal (bfl_mode, n, j, i);
 
-                        glVertex3d (x(j2,i), y(j,i2), z(j,i));
-
-                        glEnd ();
+                        m_glfcns.glVertex3d (x(j2,i), y(j,i2), z(j,i));
+
+                        m_glfcns.glEnd ();
                       }
                   }
               }
@@ -2845,7 +2818,7 @@
             set_linewidth (0.5f);
 
             if ((el_mode > 0) && do_lighting)
-              glDisable (GL_LIGHTING);
+              m_glfcns.glDisable (GL_LIGHTING);
           }
         else
           {
@@ -3124,7 +3097,7 @@
         }
 
     if (fl_mode > 0 || el_mode > 0)
-      glMaterialf (LIGHT_MODE, GL_SHININESS, se);
+      m_glfcns.glMaterialf (LIGHT_MODE, GL_SHININESS, se);
 
     std::list<std::list<octave_idx_type>>::const_iterator it1;
 
@@ -3135,27 +3108,27 @@
           {
             if (fc_mode == UNIFORM)
               {
-                glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
+                m_glfcns.glColor4d (fcolor(0), fcolor(1), fcolor(2), fa);
                 if (fl_mode > 0)
                   {
                     float cb[4] = { 0, 0, 0, 1 };
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = as * fcolor(i);
-                    glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ds * fcolor(i);
-                    glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ss * (scr + (1-scr) * fcolor(i));
-                    glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                   }
               }
 
             if ((fl_mode > 0) && do_lighting)
-              glEnable (GL_LIGHTING);
+              m_glfcns.glEnable (GL_LIGHTING);
 
             // NOTE: Push filled part of patch backwards to avoid Z-fighting
             // with tesselator outline.  A value of 1.0 seems to work fine.
@@ -3223,25 +3196,25 @@
 
                             if (col.numel () == 3)
                               {
-                                glColor4d (col(0), col(1), col(2), fa);
+                                m_glfcns.glColor4d (col(0), col(1), col(2), fa);
                                 if (fl_mode > 0)
                                   {
                                     float cb[4] = { 0, 0, 0, 1 };
 
                                     for (int k = 0; k < 3; k++)
                                       cb[k] = (vv->ambient * col(k));
-                                    glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                                     for (int k = 0; k < 3; k++)
                                       cb[k] = (vv->diffuse * col(k));
-                                    glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                                     for (int k = 0; k < 3; k++)
                                       cb[k] = vv->specular *
                                               (vv->specular_color_refl
                                                + (1-vv->specular_color_refl) *
                                               col(k));
-                                    glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                                   }
                               }
                           }
@@ -3258,7 +3231,7 @@
               }
 
             if ((fl_mode > 0) && do_lighting)
-              glDisable (GL_LIGHTING);
+              m_glfcns.glDisable (GL_LIGHTING);
           }
         else
           {
@@ -3274,27 +3247,27 @@
           {
             if (ec_mode == UNIFORM)
               {
-                glColor3dv (ecolor.data ());
+                m_glfcns.glColor3dv (ecolor.data ());
                 if (el_mode > 0)
                   {
                     float cb[4] = { 0, 0, 0, 1 };
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = (as * ecolor(i));
-                    glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_AMBIENT, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ds * ecolor(i);
-                    glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_DIFFUSE, cb);
 
                     for (int i = 0; i < 3; i++)
                       cb[i] = ss * (scr + (1-scr) * ecolor(i));
-                    glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
+                    m_glfcns.glMaterialfv (LIGHT_MODE, GL_SPECULAR, cb);
                   }
               }
 
             if ((el_mode > 0) && do_lighting)
-              glEnable (GL_LIGHTING);
+              m_glfcns.glEnable (GL_LIGHTING);
 
             double linewidth = props.get_linewidth ();
             set_linestyle (props.get_linestyle (), false, linewidth);
@@ -3322,7 +3295,7 @@
                     // Draw it as a line.
                     bool flag = false;
 
-                    glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
+                    m_glfcns.glShadeModel ((ec_mode == INTERP || el_mode == GOURAUD)
                                   ? GL_SMOOTH : GL_FLAT);
 
                     // Add vertices in reverse order for Matlab compatibility
@@ -3336,21 +3309,21 @@
                             if (! flag)
                               {
                                 flag = true;
-                                glBegin (GL_LINE_STRIP);
+                                m_glfcns.glBegin (GL_LINE_STRIP);
                               }
                             if (ec_mode != UNIFORM)
                               {
                                 Matrix col = vv->color;
 
                                 if (col.numel () == 3)
-                                  glColor3dv (col.data ());
+                                  m_glfcns.glColor3dv (col.data ());
                               }
-                            glVertex3d (m(0), m(1), m(2));
+                            m_glfcns.glVertex3d (m(0), m(1), m(2));
                           }
                         else if (flag)
                           {
                             flag = false;
-                            glEnd ();
+                            m_glfcns.glEnd ();
                           }
                       }
                     // Do loop body with vertex N to "close" GL_LINE_STRIP
@@ -3366,13 +3339,13 @@
                             Matrix col = vv->color;
 
                             if (col.numel () == 3)
-                              glColor3dv (col.data ());
+                              m_glfcns.glColor3dv (col.data ());
                           }
-                        glVertex3d (m(0), m(1), m(2));
+                        m_glfcns.glVertex3d (m(0), m(1), m(2));
                       }
 
                     if (flag)
-                      glEnd ();
+                      m_glfcns.glEnd ();
                   }
                 else  // Normal edge contour drawn with tesselator
                   {
@@ -3397,7 +3370,7 @@
             set_linewidth (0.5f);
 
             if ((el_mode > 0) && do_lighting)
-              glDisable (GL_LIGHTING);
+              m_glfcns.glDisable (GL_LIGHTING);
           }
         else
           {
@@ -3492,7 +3465,7 @@
 #if defined (HAVE_OPENGL)
 
     // enable light source
-    glEnable (current_light);
+    m_glfcns.glEnable (m_current_light);
 
     // light position
     float pos[4] = { 0, 0, 0, 0 }; // X,Y,Z,infinite/local
@@ -3501,15 +3474,15 @@
       pos[i] = lpos(i);
     if (props.style_is ("local"))
       pos[3] = 1;
-    glLightfv (current_light, GL_POSITION, pos);
+    m_glfcns.glLightfv (m_current_light, GL_POSITION, pos);
 
     // light color
     float col[4] = { 1, 1, 1, 1 }; // R,G,B,ALPHA (the latter has no meaning)
     Matrix lcolor = props.get_color ().matrix_value ();
     for (int i = 0; i < 3; i++)
       col[i] = lcolor(i);
-    glLightfv (current_light, GL_DIFFUSE,  col);
-    glLightfv (current_light, GL_SPECULAR, col);
+    m_glfcns.glLightfv (m_current_light, GL_DIFFUSE,  col);
+    m_glfcns.glLightfv (m_current_light, GL_SPECULAR, col);
 
 #else
 
@@ -3553,17 +3526,17 @@
 
     const Matrix bbox = props.get_extent_matrix ();
 
-    bool blend = glIsEnabled (GL_BLEND);
-
-    glEnable (GL_BLEND);
-    glEnable (GL_ALPHA_TEST);
-    glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0);
-    glBitmap (0, 0, 0, 0, bbox(0), bbox(1), nullptr);
-    glDrawPixels (bbox(2), bbox(3),
-                  GL_RGBA, GL_UNSIGNED_BYTE, props.get_pixels ().data ());
-    glDisable (GL_ALPHA_TEST);
+    bool blend = m_glfcns.glIsEnabled (GL_BLEND);
+
+    m_glfcns.glEnable (GL_BLEND);
+    m_glfcns.glEnable (GL_ALPHA_TEST);
+    m_glfcns.glRasterPos3d (pos(0), pos(1), pos.numel () > 2 ? pos(2) : 0.0);
+    m_glfcns.glBitmap (0, 0, 0, 0, bbox(0), bbox(1), nullptr);
+    m_glfcns.glDrawPixels (bbox(2), bbox(3), GL_RGBA, GL_UNSIGNED_BYTE,
+                           props.get_pixels ().data ());
+    m_glfcns.glDisable (GL_ALPHA_TEST);
     if (! blend)
-      glDisable (GL_BLEND);
+      m_glfcns.glDisable (GL_BLEND);
 
 #else
 
@@ -3600,19 +3573,19 @@
     int vp[4];
 #  endif
 
-    glGetIntegerv (GL_VIEWPORT, vp);
+    m_glfcns.glGetIntegerv (GL_VIEWPORT, vp);
 
     // Save current transform matrices and set orthogonal window coordinates
-    glMatrixMode (GL_PROJECTION);
-    glPushMatrix ();
-    glLoadIdentity ();
-    glOrtho (0, vp[2], vp[3], 0, xZ1, xZ2);
-    glMatrixMode (GL_MODELVIEW);
-    glPushMatrix ();
-    glLoadIdentity ();
+    m_glfcns.glMatrixMode (GL_PROJECTION);
+    m_glfcns.glPushMatrix ();
+    m_glfcns.glLoadIdentity ();
+    m_glfcns.glOrtho (0, vp[2], vp[3], 0, xZ1, xZ2);
+    m_glfcns.glMatrixMode (GL_MODELVIEW);
+    m_glfcns.glPushMatrix ();
+    m_glfcns.glLoadIdentity ();
 
     // Translate coordinates so that the text anchor is (0,0)
-    glTranslated (pixpos(0), pixpos(1), -pixpos(2));
+    m_glfcns.glTranslated (pixpos(0), pixpos(1), -pixpos(2));
 
     // FIXME: Only multiples of 90° are handled by the text renderer.
     //        Handle others here.
@@ -3620,7 +3593,7 @@
 
     if (do_rotate && rotation != 0.0 && rotation != 90.0
         && rotation != 180.0 && rotation != 270.0)
-      glRotated (-rotation, 0.0, 0.0, 1.0);
+      m_glfcns.glRotated (-rotation, 0.0, 0.0, 1.0);
 
     double m = props.get_margin ();
     double x0 = bbox (0) - m;
@@ -3630,18 +3603,18 @@
 
     if (! bgcol.isempty ())
       {
-        glColor3f (bgcol(0), bgcol(1), bgcol(2));
-
-        bool depth_test = glIsEnabled (GL_DEPTH_TEST);
+        m_glfcns.glColor3f (bgcol(0), bgcol(1), bgcol(2));
+
+        bool depth_test = m_glfcns.glIsEnabled (GL_DEPTH_TEST);
         if (depth_test)
           set_polygon_offset (true, 4.0);
 
-        glBegin (GL_QUADS);
-        glVertex2d (x0, y0);
-        glVertex2d (x1, y0);
-        glVertex2d (x1, y1);
-        glVertex2d (x0, y1);
-        glEnd ();
+        m_glfcns.glBegin (GL_QUADS);
+        m_glfcns.glVertex2d (x0, y0);
+        m_glfcns.glVertex2d (x1, y0);
+        m_glfcns.glVertex2d (x1, y1);
+        m_glfcns.glVertex2d (x0, y1);
+        m_glfcns.glEnd ();
 
         if (depth_test)
           set_polygon_offset (false);
@@ -3649,26 +3622,26 @@
 
     if (! ecol.isempty ())
       {
-        glColor3f (ecol(0), ecol(1), ecol(2));
+        m_glfcns.glColor3f (ecol(0), ecol(1), ecol(2));
 
         set_linestyle (props.get_linestyle (), false, props.get_linewidth ());
         set_linewidth (props.get_linewidth ());
 
-        glBegin (GL_LINE_STRIP);
-        glVertex2d (x0, y0);
-        glVertex2d (x1, y0);
-        glVertex2d (x1, y1);
-        glVertex2d (x0, y1);
-        glVertex2d (x0, y0);
-        glEnd ();
+        m_glfcns.glBegin (GL_LINE_STRIP);
+        m_glfcns.glVertex2d (x0, y0);
+        m_glfcns.glVertex2d (x1, y0);
+        m_glfcns.glVertex2d (x1, y1);
+        m_glfcns.glVertex2d (x0, y1);
+        m_glfcns.glVertex2d (x0, y0);
+        m_glfcns.glEnd ();
 
         set_linestyle ("-");
       }
 
     // Restore previous coordinate system
-    glPopMatrix();
-    glMatrixMode (GL_PROJECTION);
-    glPopMatrix();
+    m_glfcns.glPopMatrix();
+    m_glfcns.glMatrixMode (GL_PROJECTION);
+    m_glfcns.glPopMatrix();
 
 #else
 
@@ -3771,18 +3744,18 @@
     else // clip to viewport
       {
         GLfloat vp[4];
-        glGetFloatv (GL_VIEWPORT, vp);
+        m_glfcns.glGetFloatv (GL_VIEWPORT, vp);
         // FIXME: actually add the code to do it!
       }
 
     if (i0 >= i1 || j0 >= j1)
       return;
 
-    glPixelZoom (pix_dx, -pix_dy);
-    glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0);
+    m_glfcns.glPixelZoom (pix_dx, -pix_dy);
+    m_glfcns.glRasterPos3d (im_xmin + nor_dx*j0, im_ymin + nor_dy*i0, 0);
 
     // by default this is 4
-    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
+    m_glfcns.glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
 
     // Expect RGB data
     if (dv.ndims () == 3 && dv(2) == 3)
@@ -3869,7 +3842,7 @@
     else
       warning ("opengl_renderer: invalid image size (expected MxNx3 or MxN)");
 
-    glPixelZoom (1, 1);
+    m_glfcns.glPixelZoom (1, 1);
 
 #else
 
@@ -3888,7 +3861,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glViewport (0, 0, w, h);
+    m_glfcns.glViewport (0, 0, w, h);
 
 #else
 
@@ -3908,7 +3881,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glDrawPixels (width, height, GL_RGB, GL_FLOAT, data);
+    m_glfcns.glDrawPixels (width, height, GL_RGB, GL_FLOAT, data);
 
 #else
 
@@ -3929,7 +3902,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
+    m_glfcns.glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_BYTE, data);
 
 #else
 
@@ -3950,7 +3923,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_SHORT, data);
+    m_glfcns.glDrawPixels (width, height, GL_RGB, GL_UNSIGNED_SHORT, data);
 
 #else
 
@@ -3971,7 +3944,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glColor3dv (c.data ());
+    m_glfcns.glColor3dv (c.data ());
 
     txt_renderer.set_color (c);
 
@@ -4003,14 +3976,14 @@
 
     if (on)
       {
-        glEnable (GL_POLYGON_OFFSET_FILL);
-        glEnable (GL_POLYGON_OFFSET_LINE);
-        glPolygonOffset (offset, offset);
+        m_glfcns.glEnable (GL_POLYGON_OFFSET_FILL);
+        m_glfcns.glEnable (GL_POLYGON_OFFSET_LINE);
+        m_glfcns.glPolygonOffset (offset, offset);
       }
     else
       {
-        glDisable (GL_POLYGON_OFFSET_FILL);
-        glDisable (GL_POLYGON_OFFSET_LINE);
+        m_glfcns.glDisable (GL_POLYGON_OFFSET_FILL);
+        m_glfcns.glDisable (GL_POLYGON_OFFSET_LINE);
       }
 
 #else
@@ -4031,7 +4004,7 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glLineWidth (w);
+    m_glfcns.glLineWidth (w);
 
 #else
 
@@ -4055,22 +4028,22 @@
 
     if (s == "-")
       {
-        glLineStipple (1, static_cast<unsigned short> (0xFFFF));
+        m_glfcns.glLineStipple (1, static_cast<unsigned short> (0xFFFF));
         solid = true;
       }
     else if (s == ":")
-      glLineStipple (linewidth, static_cast<unsigned short> (0x5555));
+      m_glfcns.glLineStipple (linewidth, static_cast<unsigned short> (0x5555));
     else if (s == "--")
-      glLineStipple (linewidth, static_cast<unsigned short> (0x0F0F));
+      m_glfcns.glLineStipple (linewidth, static_cast<unsigned short> (0x0F0F));
     else if (s == "-.")
-      glLineStipple (linewidth, static_cast<unsigned short> (0x6F6F));
+      m_glfcns.glLineStipple (linewidth, static_cast<unsigned short> (0x6F6F));
     else
-      glLineStipple (1, static_cast<unsigned short> (0x0000));
+      m_glfcns.glLineStipple (1, static_cast<unsigned short> (0x0000));
 
     if (solid && ! use_stipple)
-      glDisable (GL_LINE_STIPPLE);
+      m_glfcns.glDisable (GL_LINE_STIPPLE);
     else
-      glEnable (GL_LINE_STIPPLE);
+      m_glfcns.glEnable (GL_LINE_STIPPLE);
 
 #else
 
@@ -4103,17 +4076,17 @@
     ColumnVector p (4, 0.0);
 
     p(0) = -1; p(3) = x2;
-    glClipPlane (GL_CLIP_PLANE0, p.data ());
+    m_glfcns.glClipPlane (GL_CLIP_PLANE0, p.data ());
     p(0) = 1; p(3) = -x1;
-    glClipPlane (GL_CLIP_PLANE1, p.data ());
+    m_glfcns.glClipPlane (GL_CLIP_PLANE1, p.data ());
     p(0) = 0; p(1) = -1; p(3) = y2;
-    glClipPlane (GL_CLIP_PLANE2, p.data ());
+    m_glfcns.glClipPlane (GL_CLIP_PLANE2, p.data ());
     p(1) = 1; p(3) = -y1;
-    glClipPlane (GL_CLIP_PLANE3, p.data ());
+    m_glfcns.glClipPlane (GL_CLIP_PLANE3, p.data ());
     p(1) = 0; p(2) = -1; p(3) = z2;
-    glClipPlane (GL_CLIP_PLANE4, p.data ());
+    m_glfcns.glClipPlane (GL_CLIP_PLANE4, p.data ());
     p(2) = 1; p(3) = -z1;
-    glClipPlane (GL_CLIP_PLANE5, p.data ());
+    m_glfcns.glClipPlane (GL_CLIP_PLANE5, p.data ());
 
     xmin = x1; xmax = x2;
     ymin = y1; ymax = y2;
@@ -4141,16 +4114,16 @@
   {
 #if defined (HAVE_OPENGL)
 
-    bool has_clipping = (glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
+    bool has_clipping = (m_glfcns.glIsEnabled (GL_CLIP_PLANE0) == GL_TRUE);
 
     if (enable != has_clipping)
       {
         if (enable)
           for (int i = 0; i < 6; i++)
-            glEnable (GL_CLIP_PLANE0+i);
+            m_glfcns.glEnable (GL_CLIP_PLANE0+i);
         else
           for (int i = 0; i < 6; i++)
-            glDisable (GL_CLIP_PLANE0+i);
+            m_glfcns.glDisable (GL_CLIP_PLANE0+i);
       }
 
 #else
@@ -4176,14 +4149,14 @@
     int vw[4];
 #  endif
 
-    glGetIntegerv (GL_VIEWPORT, vw);
-
-    glMatrixMode (GL_PROJECTION);
-    glPushMatrix ();
-    glLoadIdentity ();
-    glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
-    glMatrixMode (GL_MODELVIEW);
-    glPushMatrix ();
+    m_glfcns.glGetIntegerv (GL_VIEWPORT, vw);
+
+    m_glfcns.glMatrixMode (GL_PROJECTION);
+    m_glfcns.glPushMatrix ();
+    m_glfcns.glLoadIdentity ();
+    m_glfcns.glOrtho (0, vw[2], vw[3], 0, xZ1, xZ2);
+    m_glfcns.glMatrixMode (GL_MODELVIEW);
+    m_glfcns.glPushMatrix ();
 
     set_clipping (false);
     set_linewidth (width);
@@ -4210,13 +4183,13 @@
   {
 #if defined (HAVE_OPENGL)
 
-    glDeleteLists (marker_id, 1);
-    glDeleteLists (filled_marker_id, 1);
-
-    glMatrixMode (GL_MODELVIEW);
-    glPopMatrix ();
-    glMatrixMode (GL_PROJECTION);
-    glPopMatrix ();
+    m_glfcns.glDeleteLists (marker_id, 1);
+    m_glfcns.glDeleteLists (filled_marker_id, 1);
+
+    m_glfcns.glMatrixMode (GL_MODELVIEW);
+    m_glfcns.glPopMatrix ();
+    m_glfcns.glMatrixMode (GL_PROJECTION);
+    m_glfcns.glPopMatrix ();
     set_linewidth (0.5f);
 
 #else
@@ -4237,29 +4210,29 @@
 
     ColumnVector tmp = xform.transform (x, y, z, false);
 
-    glLoadIdentity ();
-    glTranslated (tmp(0), tmp(1), -tmp(2));
+    m_glfcns.glLoadIdentity ();
+    m_glfcns.glTranslated (tmp(0), tmp(1), -tmp(2));
 
     if (filled_marker_id > 0 && fc.numel () > 0)
       {
-        glColor3dv (fc.data ());
+        m_glfcns.glColor3dv (fc.data ());
         set_polygon_offset (true, -1.0);
-        glCallList (filled_marker_id);
+        m_glfcns.glCallList (filled_marker_id);
         if (lc.numel () > 0)
           {
-            glColor3dv (lc.data ());
-            glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
-            glEdgeFlag (GL_TRUE);
+            m_glfcns.glColor3dv (lc.data ());
+            m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
+            m_glfcns.glEdgeFlag (GL_TRUE);
             set_polygon_offset (true, -2.0);
-            glCallList (filled_marker_id);
-            glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
+            m_glfcns.glCallList (filled_marker_id);
+            m_glfcns.glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
           }
         set_polygon_offset (false);
       }
     else if (marker_id > 0 && lc.numel () > 0)
       {
-        glColor3dv (lc.data ());
-        glCallList (marker_id);
+        m_glfcns.glColor3dv (lc.data ());
+        m_glfcns.glCallList (marker_id);
       }
 
 #else
@@ -4279,6 +4252,57 @@
   }
 
   void
+  opengl_renderer::init_maxlights (void)
+  {
+#if defined (HAVE_OPENGL)
+
+    // Check actual maximum number of lights possible
+    if (m_max_lights == 0)
+      {
+        for (m_max_lights = 0; m_max_lights < GL_MAX_LIGHTS; m_max_lights++)
+          {
+            m_glfcns.glDisable (GL_LIGHT0 + m_max_lights);
+            if (m_glfcns.glGetError ())
+              break;
+          }
+      }
+
+#else
+
+    // This shouldn't happen because construction of opengl_renderer
+    // objects is supposed to be impossible if OpenGL is not available.
+
+    panic_impossible ();
+
+#endif
+  }
+
+  std::string
+  opengl_renderer::get_string (GLenum id) const
+  {
+#if defined (HAVE_OPENGL)
+
+    // This is kind of ugly, but glGetString returns a pointer to GLubyte
+    // and there is no std::string constructor that matches.  Is there a
+    // better way?
+
+    std::ostringstream buf;
+    buf << m_glfcns.glGetString (id);
+    return std::string (buf.str ());
+
+#else
+
+    octave_unused_parameter (id);
+
+    // This shouldn't happen because construction of opengl_renderer
+    // objects is supposed to be impossible if OpenGL is not available.
+
+    panic_impossible ();
+
+#endif
+  }
+
+  void
   opengl_renderer::set_normal (int bfl_mode, const NDArray& n, int j, int i)
   {
 #if defined (HAVE_OPENGL)
@@ -4295,7 +4319,7 @@
       dir = ((x * view_vector(0) + y * view_vector(1) + z * view_vector(2) < 0)
              ? ((bfl_mode > 1) ? 0.0 : -1.0) : 1.0);
 
-    glNormal3d (dir*x/d, dir*y/d, dir*z/d);
+    m_glfcns.glNormal3d (dir*x/d, dir*y/d, dir*z/d);
 
 #else
 
@@ -4323,44 +4347,44 @@
     if (filled && (c == '+' || c == 'x' || c == '*' || c == '.'))
       return 0;
 
-    unsigned int ID = glGenLists (1);
+    unsigned int ID = m_glfcns.glGenLists (1);
     double sz = size * toolkit.get_screen_resolution () / 72.0;
 
     // constants for the * marker
     const double sqrt2d4 = 0.35355339059327;
     double tt = sz*sqrt2d4;
 
-    glNewList (ID, GL_COMPILE);
+    m_glfcns.glNewList (ID, GL_COMPILE);
 
     switch (marker[0])
       {
       case '+':
-        glBegin (GL_LINES);
-        glVertex2d (-sz/2, 0);
-        glVertex2d (sz/2, 0);
-        glVertex2d (0, -sz/2);
-        glVertex2d (0, sz/2);
-        glEnd ();
+        m_glfcns.glBegin (GL_LINES);
+        m_glfcns.glVertex2d (-sz/2, 0);
+        m_glfcns.glVertex2d (sz/2, 0);
+        m_glfcns.glVertex2d (0, -sz/2);
+        m_glfcns.glVertex2d (0, sz/2);
+        m_glfcns.glEnd ();
         break;
       case 'x':
-        glBegin (GL_LINES);
-        glVertex2d (-sz/2, -sz/2);
-        glVertex2d (sz/2, sz/2);
-        glVertex2d (-sz/2, sz/2);
-        glVertex2d (sz/2, -sz/2);
-        glEnd ();
+        m_glfcns.glBegin (GL_LINES);
+        m_glfcns.glVertex2d (-sz/2, -sz/2);
+        m_glfcns.glVertex2d (sz/2, sz/2);
+        m_glfcns.glVertex2d (-sz/2, sz/2);
+        m_glfcns.glVertex2d (sz/2, -sz/2);
+        m_glfcns.glEnd ();
         break;
       case '*':
-        glBegin (GL_LINES);
-        glVertex2d (-sz/2, 0);
-        glVertex2d (sz/2, 0);
-        glVertex2d (0, -sz/2);
-        glVertex2d (0, sz/2);
-        glVertex2d (-tt, -tt);
-        glVertex2d (+tt, +tt);
-        glVertex2d (-tt, +tt);
-        glVertex2d (+tt, -tt);
-        glEnd ();
+        m_glfcns.glBegin (GL_LINES);
+        m_glfcns.glVertex2d (-sz/2, 0);
+        m_glfcns.glVertex2d (sz/2, 0);
+        m_glfcns.glVertex2d (0, -sz/2);
+        m_glfcns.glVertex2d (0, sz/2);
+        m_glfcns.glVertex2d (-tt, -tt);
+        m_glfcns.glVertex2d (+tt, +tt);
+        m_glfcns.glVertex2d (-tt, +tt);
+        m_glfcns.glVertex2d (+tt, -tt);
+        m_glfcns.glEnd ();
         break;
       case '.':
         {
@@ -4376,19 +4400,19 @@
           div = std::max (div, 3);  // ensure at least a few vertices are drawn
           double ang_step = M_PI / div;
 
-          glBegin (GL_POLYGON);
+          m_glfcns.glBegin (GL_POLYGON);
           for (double ang = 0; ang < 2*M_PI; ang += ang_step)
-            glVertex2d (sz/6*cos (ang), sz/6*sin (ang));
-          glEnd ();
+            m_glfcns.glVertex2d (sz/6*cos (ang), sz/6*sin (ang));
+          m_glfcns.glEnd ();
         }
         break;
       case 's':
-        glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        glVertex2d (-sz/2, -sz/2);
-        glVertex2d (-sz/2, sz/2);
-        glVertex2d (sz/2, sz/2);
-        glVertex2d (sz/2, -sz/2);
-        glEnd ();
+        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+        m_glfcns.glVertex2d (-sz/2, -sz/2);
+        m_glfcns.glVertex2d (-sz/2, sz/2);
+        m_glfcns.glVertex2d (sz/2, sz/2);
+        m_glfcns.glVertex2d (sz/2, -sz/2);
+        m_glfcns.glEnd ();
         break;
       case 'o':
         {
@@ -4398,61 +4422,61 @@
           div = std::max (div, 5);  // ensure at least a few vertices are drawn
           double ang_step = M_PI / div;
 
-          glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+          m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
           for (double ang = 0; ang < 2*M_PI; ang += ang_step)
-            glVertex2d (sz/2*cos (ang), sz/2*sin (ang));
-          glEnd ();
+            m_glfcns.glVertex2d (sz/2*cos (ang), sz/2*sin (ang));
+          m_glfcns.glEnd ();
         }
         break;
       case 'd':
-        glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        glVertex2d (0, -sz/2);
-        glVertex2d (sz/2, 0);
-        glVertex2d (0, sz/2);
-        glVertex2d (-sz/2, 0);
-        glEnd ();
+        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+        m_glfcns.glVertex2d (0, -sz/2);
+        m_glfcns.glVertex2d (sz/2, 0);
+        m_glfcns.glVertex2d (0, sz/2);
+        m_glfcns.glVertex2d (-sz/2, 0);
+        m_glfcns.glEnd ();
         break;
       case 'v':
-        glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        glVertex2d (0, sz/2);
-        glVertex2d (sz/2, -sz/2);
-        glVertex2d (-sz/2, -sz/2);
-        glEnd ();
+        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+        m_glfcns.glVertex2d (0, sz/2);
+        m_glfcns.glVertex2d (sz/2, -sz/2);
+        m_glfcns.glVertex2d (-sz/2, -sz/2);
+        m_glfcns.glEnd ();
         break;
       case '^':
-        glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        glVertex2d (0, -sz/2);
-        glVertex2d (-sz/2, sz/2);
-        glVertex2d (sz/2, sz/2);
-        glEnd ();
+        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+        m_glfcns.glVertex2d (0, -sz/2);
+        m_glfcns.glVertex2d (-sz/2, sz/2);
+        m_glfcns.glVertex2d (sz/2, sz/2);
+        m_glfcns.glEnd ();
         break;
       case '>':
-        glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        glVertex2d (sz/2, 0);
-        glVertex2d (-sz/2, sz/2);
-        glVertex2d (-sz/2, -sz/2);
-        glEnd ();
+        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+        m_glfcns.glVertex2d (sz/2, 0);
+        m_glfcns.glVertex2d (-sz/2, sz/2);
+        m_glfcns.glVertex2d (-sz/2, -sz/2);
+        m_glfcns.glEnd ();
         break;
       case '<':
-        glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
-        glVertex2d (-sz/2, 0);
-        glVertex2d (sz/2, -sz/2);
-        glVertex2d (sz/2, sz/2);
-        glEnd ();
+        m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+        m_glfcns.glVertex2d (-sz/2, 0);
+        m_glfcns.glVertex2d (sz/2, -sz/2);
+        m_glfcns.glVertex2d (sz/2, sz/2);
+        m_glfcns.glEnd ();
         break;
       case 'p':
         {
           double ang, r, dr;
           dr = 1.0 - sin (M_PI/10)/sin (3*M_PI/10)*1.02;
 
-          glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+          m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
           for (int i = 0; i < 2*5; i++)
             {
               ang = (-0.5 + double (i+1) / 5) * M_PI;
               r = 1.0 - (dr * fmod (double (i+1), 2.0));
-              glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
+              m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
             }
-          glEnd ();
+          m_glfcns.glEnd ();
         }
         break;
       case 'h':
@@ -4460,14 +4484,14 @@
           double ang, r, dr;
           dr = 1.0 - 0.5/sin (M_PI/3)*1.02;
 
-          glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
+          m_glfcns.glBegin (filled ? GL_POLYGON : GL_LINE_LOOP);
           for (int i = 0; i < 2*6; i++)
             {
               ang = (0.5 + double (i+1) / 6.0) * M_PI;
               r = 1.0 - (dr * fmod (double (i+1), 2.0));
-              glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
+              m_glfcns.glVertex2d (sz/2*r*cos (ang), sz/2*r*sin (ang));
             }
-          glEnd ();
+          m_glfcns.glEnd ();
         }
         break;
       default:
@@ -4475,7 +4499,7 @@
         break;
       }
 
-    glEndList ();
+    m_glfcns.glEndList ();
 
     return ID;
 
@@ -4530,18 +4554,18 @@
         uint8NDArray pixels;
         text_to_pixels (txt, pixels, bbox, halign, valign, rotation);
 
-        bool blend = glIsEnabled (GL_BLEND);
-
-        glEnable (GL_BLEND);
-        glEnable (GL_ALPHA_TEST);
-        glRasterPos3d (x, y, z);
-        glBitmap(0, 0, 0, 0, bbox(0), bbox(1), nullptr);
-        glDrawPixels (bbox(2), bbox(3),
+        bool blend = m_glfcns.glIsEnabled (GL_BLEND);
+
+        m_glfcns.glEnable (GL_BLEND);
+        m_glfcns.glEnable (GL_ALPHA_TEST);
+        m_glfcns.glRasterPos3d (x, y, z);
+        m_glfcns.glBitmap(0, 0, 0, 0, bbox(0), bbox(1), nullptr);
+        m_glfcns.glDrawPixels (bbox(2), bbox(3),
                       GL_RGBA, GL_UNSIGNED_BYTE, pixels.data ());
-        glDisable (GL_ALPHA_TEST);
+        m_glfcns.glDisable (GL_ALPHA_TEST);
 
         if (! blend)
-          glDisable (GL_BLEND);
+          m_glfcns.glDisable (GL_BLEND);
       }
 
     return bbox;