Initial commit
authorRalf Jung <post@ralfj.de>
Sun, 31 Mar 2013 12:40:31 +0000 (14:40 +0200)
committerRalf Jung <post@ralfj.de>
Sun, 31 Mar 2013 12:40:31 +0000 (14:40 +0200)
Makefile [new file with mode: 0644]
eglbackend.cpp [new file with mode: 0644]
eglbackend.h [new file with mode: 0644]
gltest.cpp [new file with mode: 0644]
glwindow.cpp [new file with mode: 0644]
glwindow.h [new file with mode: 0644]
glxbackend.cpp [new file with mode: 0644]
glxbackend.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3fc8800
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+FLAGS := -Wall
+
+COMMON_SRC = gltest.cpp glwindow.cpp
+COMMON_HDR = glwindow.h
+
+all: glxtest egltest
+
+glxtest: $(COMMON_SRC) $(COMMON_HDR) glxbackend.cpp glxbackend.h
+       g++ $(FLAGS) -DUSE_GLX $(COMMON_SRC) glxbackend.cpp -lGL -lX11 -o glxtest
+
+egltest: $(COMMON_SRC) $(COMMON_HDR) eglbackend.cpp eglbackend.h
+       g++ $(FLAGS) -DUSE_EGL $(COMMON_SRC) eglbackend.cpp -lEGL -lGL -lX11 -o egltest
+
+clean:
+       rm -f glxtest egltest
diff --git a/eglbackend.cpp b/eglbackend.cpp
new file mode 100644 (file)
index 0000000..359d54a
--- /dev/null
@@ -0,0 +1,104 @@
+#include "eglbackend.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include <EGL/eglext.h>
+
+static const char *eglErrorToString(EGLint e)
+{
+#define CASE(name) case name: return #name
+       switch (e) {
+               CASE(EGL_SUCCESS);
+               CASE(EGL_NOT_INITIALIZED);
+               CASE(EGL_BAD_ACCESS);
+               CASE(EGL_BAD_ALLOC);
+               CASE(EGL_BAD_ATTRIBUTE);
+               CASE(EGL_BAD_CONTEXT);
+               CASE(EGL_BAD_CONFIG);
+               CASE(EGL_BAD_CURRENT_SURFACE);
+               CASE(EGL_BAD_DISPLAY);
+               default: return "<unknown>";
+       }
+#undef CASE
+}
+
+static void exitEglError(const char *what)
+{
+       EGLint e = eglGetError();
+       fprintf(stderr, "EGL error %d (%s): %s\n", e, eglErrorToString(e), what);
+       exit(1);
+}
+
+/* attributes for a double buffered visual in RGBA format with at least
+* 4 bits per color */
+static const EGLint config_attribs[] = {
+       EGL_RED_SIZE,             4,
+       EGL_GREEN_SIZE,           4,
+       EGL_BLUE_SIZE,            4,
+       EGL_RENDERABLE_TYPE,      EGL_OPENGL_BIT,
+       EGL_NONE,
+};
+
+VisualID EGLBackend::initialize(Display *xDisplay)
+{
+       if (display == EGL_NO_DISPLAY) { // this implies that the context is also unitialized
+               // get connection and bind API
+               EGLint eglMajor, eglMinor;
+               display = eglGetDisplay(xDisplay);
+               if (display == EGL_NO_DISPLAY)                         
+                       exitEglError("Failed to get EGL display");
+               if (eglInitialize(display, &eglMajor, &eglMinor) == EGL_FALSE)                   
+                       exitEglError("Failed to initialize EGL");
+               printf("Using EGL version %d.%d\n", eglMajor, eglMinor);
+               eglBindAPI(EGL_OPENGL_API);
+               // get an appropriate config
+               EGLConfig configs[1];
+               EGLint count;
+               if (eglChooseConfig(display, config_attribs, configs, 1, &count) == EGL_FALSE || count == 0)                            
+                       exitEglError("Failed to choose config");
+               config = configs[0];
+       }
+       // return visual ID
+       EGLint val;
+       eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &val);
+       return (VisualID)val;
+}
+
+void EGLBackend::createContext(Window window)
+{
+       assert(display != EGL_NO_DISPLAY && context == EGL_NO_CONTEXT);
+       surface = eglCreateWindowSurface(display, config, window, NULL);
+       /* create an EGL context and use it with the surface */                                    
+       context = eglCreateContext(display, config, EGL_NO_CONTEXT, NULL);
+       if (context == EGL_NO_CONTEXT)
+               exitEglError("Failed to create context");
+       if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
+               exitEglError("Failed to make context current");
+}
+
+EGLBackend::~EGLBackend()
+{
+       if (display != EGL_NO_DISPLAY) {
+               if (context != EGL_NO_CONTEXT) {
+                       eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+                       eglDestroyContext(display, context);
+                       eglDestroySurface(display, surface);
+               }
+               eglTerminate(display);
+               eglReleaseThread();
+       }
+}
+
+void EGLBackend::swapBuffers() const
+{
+       assert(context != EGL_NO_CONTEXT); // this implies the display is also initialized
+       eglSwapBuffers(display, surface);
+}
+
+void EGLBackend::setSwapInterval(int i) const
+{
+       assert(context != EGL_NO_CONTEXT);
+       eglSwapInterval(display, i);
+}
diff --git a/eglbackend.h b/eglbackend.h
new file mode 100644 (file)
index 0000000..17c3bcf
--- /dev/null
@@ -0,0 +1,30 @@
+#include "glwindow.h"
+
+#include <EGL/egl.h>
+
+
+/** Make an X window fit for GL. You have to manage the X window yourself! */
+class EGLBackend : public GLBackend {
+public:
+       /** Create a GL window class and find an appropriate visual */
+       EGLBackend() : display(EGL_NO_DISPLAY), context(EGL_NO_CONTEXT) {}
+       /** Fre all resources */
+       virtual ~EGLBackend();
+       
+       /** Initialize GL backend, choose visual configuration and return the ID */
+       virtual VisualID initialize(Display *display);
+       
+       /** create a GL context for the given window */
+       virtual void createContext(Window window);
+       
+       /** Swap back and front buffers */
+       virtual void swapBuffers() const;
+       
+       /** Set the swap interval */
+       virtual void setSwapInterval(int i) const;
+private:
+       EGLDisplay display;
+       EGLConfig  config;
+       EGLSurface surface;
+       EGLContext context;
+};
diff --git a/gltest.cpp b/gltest.cpp
new file mode 100644 (file)
index 0000000..2b6105c
--- /dev/null
@@ -0,0 +1,185 @@
+// stdlib includes
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <assert.h>
+#include <GL/gl.h>
+
+// include proper GL connector
+#include "glwindow.h"
+#if defined(USE_GLX)
+#include "glxbackend.h"
+GLBackend *createGLBackend()
+{
+       return new GLXBackend();
+}
+#elif defined(USE_EGL)
+#include "eglbackend.h"
+GLBackend *createGLBackend()
+{
+       return new EGLBackend();
+}
+#else
+#error "No GL window type selected"
+#endif
+
+// configuration
+const GLfloat boxWidth = 0.045f;
+const GLfloat boxSpeed = 1.25f; // per second
+
+// profiler
+const int numProfilerStates = 5;
+const char *profilerStateNames[numProfilerStates] = { "Pre-Render", "Drawing", "Swapping", "Post-Render", "Outside renderer"};
+
+// utility functions
+static double getTime()
+{
+       struct timespec tp;
+       clock_gettime(CLOCK_MONOTONIC, &tp);
+       return tp.tv_sec + 1e-9 * tp.tv_nsec;
+}
+
+static void drawRect(GLfloat x1, GLfloat y1, GLfloat x2, GLfloat y2)
+{
+       glVertex2f(x1, y1); glVertex2f(x2, y1); glVertex2f(x2, y2); glVertex2f(x1, y2);
+}
+
+// the window
+class TearTestWindow : public GLWindow {
+public:
+       TearTestWindow() : GLWindow(XOpenDisplay(0), createGLBackend()), boxPos(0), boxDirection(1)
+       {}
+
+protected:
+       virtual void initGL()
+       {
+               getBackend()->setSwapInterval(1);
+               // initialize GL proper
+               glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+               glDisable(GL_DEPTH_TEST);
+               // initialize clocks
+               lastFrame = getTime();
+               // initailize profiler
+               framect = 0;
+               memset(stateTime, 0, sizeof(stateTime));
+               curState = -1;
+               lastDisplay = lastProfile = getTime();
+       }
+       
+       virtual void resizeGL(unsigned int width, unsigned int height)
+       {
+               /* prevent divide-by-zero */                      
+               if (height == 0)                                  
+                       height = 1;                                   
+               glViewport(0, 0, width, height);
+               glMatrixMode(GL_PROJECTION);                      
+               glLoadIdentity();                                 
+               glOrtho (0, 1, 1, 0, 0, 1);
+               glMatrixMode(GL_MODELVIEW);      
+               glClear(GL_COLOR_BUFFER_BIT);                                                 
+               glFlush();
+       }
+       
+       void profilerTick(int nextState)
+       {
+               assert (nextState >= 0 && nextState < numProfilerStates);
+               double time = getTime();
+               if (curState >= 0)
+                       stateTime[curState] += time-lastProfile;
+               curState = nextState;
+               lastProfile = time;
+               // display?
+               const double elapsed = time-lastDisplay;
+               if (elapsed >= 3) {
+                       printf("%.1f fps, time spent: ", framect/elapsed);
+                       for (int i = 0; i < numProfilerStates; ++i) {
+                               if (i != 0) printf(", ");
+                               printf("%s %.1f%%", profilerStateNames[i], stateTime[i]/elapsed*100);
+                       }
+                       printf("\n");
+                       lastDisplay = time;
+                       framect = 0;
+                       memset(stateTime, 0, sizeof(stateTime));
+               }
+       }
+       
+       void renderGL()
+       {                            
+               profilerTick(0);
+               double time = getTime();
+               // anim
+               double passedTime = time-lastFrame;
+               boxPos += boxSpeed*passedTime*boxDirection;
+               while (boxPos < 0 || boxPos+boxWidth > 1) { // wrapover
+                       if (boxPos < 0) {
+                               boxPos = -boxPos;
+                               boxDirection = -boxDirection;
+                       }
+                       else {
+                               boxPos = 1.0-boxWidth-(boxPos+boxWidth-1.0);
+                               boxDirection = -boxDirection;
+                       }
+               }
+               lastFrame = time;
+               // draw
+               //glFlush();
+               profilerTick(1);
+               //glClear(GL_COLOR_BUFFER_BIT);
+               glBegin(GL_QUADS);
+               // clear manually
+               glColor3f(0.0f, 0.0f, 0.0f);
+               drawRect(0, 0, 1, 1);
+               glColor3f(0.8f, 1.0f, 0.75f);
+               drawRect(boxPos, 0, boxPos+boxWidth, 1);
+               glEnd();
+               usleep(20*1000);
+               profilerTick(2);
+               getBackend()->swapBuffers();
+//             glDrawBuffer(GL_FRONT);
+//             int xpos = 0;
+//             int ypos = 0;
+               //foreach (const QRect &r, region.rects()) {
+                       // convert to OpenGL coordinates
+                       //int y = displayHeight() - 0 - r.height();
+//                     glBitmap(0, 0, 0, 0, 0 - xpos, 0 - ypos, NULL); // not glRasterPos2f, see glxbackend.cpp
+//                     xpos = 0;
+//                     ypos = 0;
+//                     glCopyPixels(0, 0, getWidth(), getHeight(), GL_COLOR);
+               //}
+//             glBitmap(0, 0, 0, 0, -xpos, -ypos, NULL); // move position back to 0,0
+//             glDrawBuffer(GL_BACK);
+               profilerTick(3);
+               glFlush();
+               ++framect;
+               profilerTick(4);
+       }
+       
+       virtual void handleKeyPress(KeySym key)
+       {
+               switch (key) {
+                       case XK_Escape: close(); break;
+                       case XK_F1: setFullscreen(!getFullscreen()); break;
+                       default: break;
+               }
+       }
+
+private:
+       double lastFrame;
+       GLfloat boxPos, boxDirection;
+       // FPS
+       double lastDisplay, lastProfile;                 
+       int framect, curState;
+       double stateTime[numProfilerStates];
+};
+
+
+
+int main(int argc, char ** argv)
+{
+       TearTestWindow w;
+       w.open(800, 600);
+       w.exec();
+}
diff --git a/glwindow.cpp b/glwindow.cpp
new file mode 100644 (file)
index 0000000..67e9b9a
--- /dev/null
@@ -0,0 +1,136 @@
+#include "glwindow.h"
+
+#include <assert.h>
+#include <X11/Xutil.h>
+#include <stdio.h>
+#include <string.h>
+
+GLWindow::~GLWindow()
+{
+       delete backend;
+       if (window)
+               XDestroyWindow(display, window);
+       XCloseDisplay(display);
+}
+
+void GLWindow::create(unsigned int width, unsigned int height)
+{
+       assert(!window);
+       // get visual from backend
+       XVisualInfo vinfo_template;
+       vinfo_template.visualid = backend->initialize(display);
+       int num_vinfo;
+       XVisualInfo *vi = XGetVisualInfo(display, VisualIDMask, &vinfo_template, &num_vinfo);
+       assert(num_vinfo == 1 && vi != NULL);
+       // set winattrs
+       XSetWindowAttributes winAttr;
+       winAttr.colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone);
+       winAttr.event_mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;                                                 
+       winAttr.border_pixel = 0;
+       // create window
+       /*if (fullscreen) {
+               // get root window size
+               width = DisplayWidth(display, vi->screen);
+               height = DisplayHeight(display, vi->screen);
+               printf("Display size: %dx%d\n", width, height);
+               // set window attributes
+               winAttr.override_redirect = True;
+               window = XCreateWindow(display, RootWindow(display, vi->screen),
+                       0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
+                       CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect, &winAttr);
+               XWarpPointer(display, None, window, 0, 0, 0, 0, 0, 0);
+               XMapRaised(display, window);
+               XGrabKeyboard(display, window, True, GrabModeAsync, GrabModeAsync, CurrentTime);
+               XGrabPointer(display, window, True, ButtonPressMask, GrabModeAsync, GrabModeAsync, window, None, CurrentTime);
+       }
+       else {*/
+       printf("Initial window size: %dx%d\n", width, height);
+       // create a window in window mode
+       window = XCreateWindow(display, RootWindow(display, vi->screen),
+               0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
+               CWBorderPixel | CWColormap | CWEventMask, &winAttr);
+       /* handle wm_delete_events */
+       Atom wmDelete = XInternAtom(display, "WM_DELETE_WINDOW", True);
+       XMapRaised(display, window);
+       XSetWMProtocols(display, window, &wmDelete, 1);
+       // done
+       this->fullscreen = false;
+       this->width = width;
+       this->height = height;
+       backend->createContext(window);
+       initGL();
+       resizeGL(width, height);
+}
+
+void GLWindow::close()
+{
+       if (!window) return;
+       XEvent ev;
+       memset(&ev, 0, sizeof (ev));
+       ev.xclient.type = ClientMessage;
+       ev.xclient.window = window;
+       ev.xclient.message_type = XInternAtom(display, "WM_PROTOCOLS", true);
+       ev.xclient.format = 32;
+       ev.xclient.data.l[0] = XInternAtom(display, "WM_DELETE_WINDOW", false);
+       ev.xclient.data.l[1] = CurrentTime;
+       XSendEvent(display, window, False, NoEventMask, &ev);
+}
+
+void GLWindow::setFullscreen(bool fullscreen)
+{
+       assert(window);
+       // set fullscreen property
+       XEvent e;
+       e.xclient.type         = ClientMessage;
+       e.xclient.window       = window;
+       e.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", true);
+       e.xclient.format = 32;
+       e.xclient.data.l[0] = 2;    // _NET_WM_STATE_TOGGLE
+       e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", fullscreen);
+       e.xclient.data.l[2] = 0;    // no second property to toggle
+       e.xclient.data.l[3] = 1;
+       e.xclient.data.l[4] = 0;
+       XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &e);
+}
+
+void GLWindow::exec()
+{
+       while (true)                            
+       {                                        
+               /* handle the events in the queue */ 
+               while (XPending(display) > 0)        
+               {
+                       XEvent event;
+                       XNextEvent(display, &event);     
+                       switch (event.type)              
+                       {
+                               case Expose:
+                                       renderGL();
+                                       break;
+                               case ConfigureNotify:
+                                       if (event.xconfigure.width != this->width || event.xconfigure.height != this->height) {
+                                               printf("Window resized to: %dx%d\n", event.xconfigure.width, event.xconfigure.height);
+                                               this->width = event.xconfigure.width;
+                                               this->height = event.xconfigure.height;
+                                               resizeGL(this->width, this->height);
+                                       }
+                                       break;
+                               case KeyPress:
+                                       handleKeyPress(XLookupKeysym(&event.xkey, 0));
+                                       break;
+                               case ClientMessage:
+                                       if (strcmp(XGetAtomName(display, event.xclient.message_type), "WM_PROTOCOLS") == 0 &&
+                                               strcmp(XGetAtomName(display, event.xclient.data.l[0]), "WM_DELETE_WINDOW") == 0)
+                                       {
+                                               XDestroyWindow(display, window);
+                                               window = 0;
+                                               return;
+                                       }
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               renderGL();
+       }
+}
diff --git a/glwindow.h b/glwindow.h
new file mode 100644 (file)
index 0000000..edd7b1f
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef GL_WINDOW_H
+#define GL_WINDOW_H
+
+#include <X11/Xlib.h>
+
+/** Abstracts the GL binding API away */
+class GLBackend {
+public:
+       /** Create a GL window class, but does not do anything */
+       GLBackend() {}
+       /** Fre all resources */
+       virtual ~GLBackend() {}
+       
+       /** Initialize GL backend, choose visual configuration and return the ID */
+       virtual VisualID initialize(Display *display) = 0;
+       
+       /** create a GL context for the given window */
+       virtual void createContext(Window window) = 0;
+       
+       /** Swap back and front buffers */
+       virtual void swapBuffers() const = 0;
+       
+       /** Set the swap interval */
+       virtual void setSwapInterval(int i) const = 0;
+};
+
+/** A window to render GL stuff in */
+class GLWindow {
+public:
+       GLWindow(Display *display, GLBackend *backend) //!< Create the window class, but do not open it. Taks ownership of the backend and the X connection.
+        : display(display), backend(backend), window(0) {}
+       virtual ~GLWindow();
+
+       void open(unsigned int width, unsigned int height) { if (!window) create(width, height); }
+       void close();
+       void setFullscreen(bool fullscreen);
+       bool getFullscreen(void) { return fullscreen; }
+       
+       int getWidth() { return width; }
+       int getHeight() { return height; }
+
+       void exec();
+
+protected:
+       const GLBackend * getBackend() { return backend; }
+
+       virtual void initGL() = 0;
+       virtual void resizeGL(unsigned int width, unsigned int height) = 0;
+       virtual void renderGL() = 0;
+       virtual void handleKeyPress(KeySym key) = 0;
+        
+private:
+       void create(unsigned int width, unsigned int height);
+
+       Display *display;
+       GLBackend *backend;
+       Window window;
+       int width, height;
+       bool fullscreen;
+};
+
+#endif
diff --git a/glxbackend.cpp b/glxbackend.cpp
new file mode 100644 (file)
index 0000000..ff126eb
--- /dev/null
@@ -0,0 +1,85 @@
+#include "glxbackend.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <GL/glxext.h>
+#include <string>
+
+/* attributes for a double buffered visual in RGBA format with at least
+* 4 bits per color */                       
+static int attrList[] =                                             
+{                                                                      
+       GLX_RGBA, GLX_DOUBLEBUFFER,
+       GLX_RED_SIZE, 4,                                                   
+       GLX_GREEN_SIZE, 4,                                                 
+       GLX_BLUE_SIZE, 4,
+       None                                                               
+};
+
+VisualID GLXBackend::initialize(Display *display)
+{
+       if (this->display == NULL) { // this implies that the context is also unitialized
+               this->display = display;
+               // debugging: show version information
+               int glxMajor, glxMinor;
+               glXQueryVersion(display, &glxMajor, &glxMinor);
+               printf("Using GLX version %d.%d\n", glxMajor, glxMinor);
+               // check for swap control functions
+               const char *extensions = glXQueryExtensionsString(display, DefaultScreen(display));
+               if (std::string(extensions).find("GLX_MESA_swap_control") != std::string::npos) {
+                       funSwapIntervalMesa = (PFNGLXSWAPINTERVALMESAPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalMESA");
+                       if (funSwapIntervalMesa) printf("glXSwapIntervalMESA is present\n");
+               }
+               else {
+                       funSwapIntervalMesa = 0;
+               }
+               if (std::string(extensions).find("GLX_EXT_swap_control") != std::string::npos) {
+                       funSwapIntervalExt = (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress((const GLubyte*)"glXSwapIntervalEXT");
+                       if (funSwapIntervalExt) printf("glXSwapIntervalEXT is present\n");
+               }
+               else {
+                       funSwapIntervalExt = 0;
+               }
+               // get the visual
+               vi = glXChooseVisual(display, DefaultScreen(display), attrList);
+       }
+       return vi->visualid;
+}
+
+void GLXBackend::createContext(Window window)
+{
+       assert(display != NULL && context == None);
+       this->window = window;
+       // create context with that window
+       context = glXCreateContext(display, vi, 0, GL_TRUE);                             
+       glXMakeCurrent(display, window, context);                                      
+       assert(glXIsDirect(display, context));
+}
+
+GLXBackend::~GLXBackend()
+{
+       if (display != NULL) {
+               if (context != None) {
+                       glXMakeCurrent(display, None, NULL);
+                       glXDestroyContext(display, context);
+               }
+               XFree(vi);
+       }
+}
+
+void GLXBackend::swapBuffers() const
+{
+       assert(context != None); // this implies the display is also initialized
+       glXSwapBuffers(display, window);
+}
+
+void GLXBackend::setSwapInterval(int i) const
+{
+       assert(context != None);
+       if (funSwapIntervalExt)
+               funSwapIntervalExt(display, window, i);
+       else if (funSwapIntervalMesa)
+               funSwapIntervalMesa(i);
+       else
+               assert(false && "At least one of glXSwapIntervalMESA, glXSwapIntervalEXT must be provided by the system");
+}
diff --git a/glxbackend.h b/glxbackend.h
new file mode 100644 (file)
index 0000000..3f24d48
--- /dev/null
@@ -0,0 +1,33 @@
+#include "glwindow.h"
+
+#include <GL/glx.h>
+
+
+/** Make an X window fit for GL. You have to manage the X window yourself! */
+class GLXBackend : public GLBackend {
+public:
+       /** Create a GL window class and find an appropriate visual */
+       GLXBackend() : display(NULL), context(None) {}
+       /** Fre all resources */
+       virtual ~GLXBackend();
+       
+       /** Initialize GL backend, choose visual configuration and return the ID */
+       virtual VisualID initialize(Display *display);
+       
+       /** create a GL context for the given window */
+       virtual void createContext(Window window);
+       
+       /** Swap back and front buffers */
+       virtual void swapBuffers() const;
+       
+       /** Set the swap interval */
+       virtual void setSwapInterval(int i) const;
+private:
+       Display *display;
+       XVisualInfo *vi;
+       GLXContext context;
+       Window window;
+       
+       PFNGLXSWAPINTERVALMESAPROC funSwapIntervalMesa;
+       PFNGLXSWAPINTERVALEXTPROC funSwapIntervalExt;
+};