Introduction
Most of the basic OpenGL setup code that I have come across is demonstrated either using a windowing-system-neutral approach using glut or specific to some GUI toolkit like MFC, GTK+ or Qt. For instance, the glut examples show how to create a ready-to-use window with glutCreateWindow
or MFC examples show how to setup an OpenGL
context in a CView
derived class. GTK+ uses some extensions like gtkglarea
or gtkglext
and Qt has its own QGLWidget
which hides all the OpenGL setup implementations from the user. Good enough if you want to stick to one toolkit or operating system with your code. But imagine a situation where you want to change the toolkit or the OS, say from MFC to Motif! The OpenGL
view class that you have written has to go through a lot of change! So why don't we have a platform and toolkit neutral class which can simply render into any window created on any platform?
Background
In this article, I would like to demonstrate how to create a platform and toolkit independent OpenGL
view class. For that, we need to first understand the primary requirement for setting up an OpenGL
rendering on any window.
OpenGL
rendering on any toolkit's window boils down to the underlying windowing system supported on a particular OS. That is, the Device Context on Microsoft Windows and the Display of the X Window System on UNIX/Linux. Although I haven't tested the code on MacOSX, since MacOSX also uses the X Window System as its windowing system, this article is also applicable for MacOSX.
The reader must understand that this article is not about teaching OpenGL or any specific toolkit. This article is intended for those who already have a good understanding of OpenGL and at least one GUI toolkit. Details of creating DLLs or UNIX/Linux shared objects are also out of scope of this article. I am trying to show how to write OpenGL code that is portable to not only different OSs but also to other GUI toolkits. Though language portability can be achieved by writing suitable wrappers on the code shown here, we are focusing on writing the code in C/C++.
Using the Code
First, we need to prepare the window so that it should accept its pixel rendering instructions from the OpenGL API. Then, we must handle the OpenGL drawing commands. Next, we must also handle the effects of resizing the window on OpenGL rendering. So essentially, the three prime methods of our class will be for setting up the window, rendering the OpenGL stuff and resizing the window.
For getting hold of the windowing system, the key things are HWND
(Handle to the Window) and HDC
(Handle to the Device Context) on Microsoft Windows and Display
(X Display is somewhat equivalent to MSWin Device Context though the theory of X Windows is a bit different from MSWin and not in the scope of this article) and Window
(equivalent to HWND
) on X Windows. If we can access these things from any toolkit, we can pretty much enable our OpenGL
view class to render onto any toolkit's window. And trust me, any good toolkit should have functions or methods to provide you the window handle and the Display. The window handle is nothing but HWND
or Window
.
So let us look at the code now. We would be writing conditional compilation preprocessor statements to make our class portable on both Windows and UNIX/Linux.
Since our class would be in a shared library, we would be creating a DLL (GLView.dll) on Windows and a shared object file (libGLView.so) on Linux. We need to export the class symbols on Windows and hence would be using the __declspec
extensions. Linux does not need that and hence we mask it using preprocessor directives.
#ifdef GLVIEW_EXPORTS
#ifdef WIN32
#define GLVIEW_API __declspec(dllexport)
#else
#define GLVIEW_API
#endif
#else
#ifdef WIN32
#define GLVIEW_API __declspec(dllimport)
#else
#define GLVIEW_API
#endif
#endif
Then we include the necessary headers, again using preprocessor directives to make platform specific includes.
#ifdef WIN32
#include <windows.h> // required by OpenGL and must be included before gl.h
#include <TCHAR.H> // for unicode support
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#ifndef WIN32 // X Windows only
#include <GL/glx.h>
#endif
And now our class declaration...
Note, the methods and data members are also guarded by preprocessor directives.
class GLVIEW_API CGLView
{
public:
CGLView(void);
#ifdef WIN32
void SetWindow(HWND ihWnd);
#else
void SetWindow(Display* pDisp, const Window& wnd);
#endif
bool SetupGLContext(bool iSetPixelFormat);
void Resize(unsigned short iWidth, unsigned short iHeight);
void RenderScene(void);
void Refresh( void);
private:
#ifdef WIN32
int SetPixelFormat(HDC hdc);
#endif
void Setup3DEnvironment();
void DrawGradientBackground();
void InitDisplayLists();
void DoAntialiasing();
private:
#ifdef WIN32
HWND m_hWnd;
HDC m_hDC;
HGLRC m_hGLRC;
#else
Display* m_pXDisplay;
int m_iXScreen;
Window m_iXWindow;
GLXContext m_hGLContext;
XVisualInfo *m_hVisual;
Colormap m_ColMap;
#endif
};
Let us have a look at the private
data members. The WIN32 members are of type HWND
, HDC
and HGLRC
. HGLRC
is the handle to the GL Rendering Context provided by Windows to make the window OpenGL-aware. HDC
can be obtained using Windows API function GetDC()
and HGLRC
can created by Windows specific GL API function wglCreateContext()
once HWND
is obtained.
The X Windows members are of type Display
, Window
and GLXContext
. GLXContext
is similar to HGLRC
which again can be created using X Window specific GL API function glXCreateContext()
once Display
and Window
is obtained.
Remember, creation of the OpenGL Rendering Context is the key entry point to begin rendering to any window.
Now, let us look at the important methods of the class. The constructor essentially does only the initialization of the private
variables.
This first important method of the class is SetWindow()
. This method has a different signature on Linux than on Windows. The difference is that on Windows, it takes the HWND
as the argument whereas Display
and Window
as arguments on Linux. Note, though I am referring as Linux, it's applicable to any OS that has X Windows as its windowing system. Let us look at the implementation of the SetWindow()
method for both systems.
#ifdef WIN32
void CGLView::SetWindow(HWND ihWnd)
{
m_hWnd = ihWnd;
}
#else
void CGLView::SetWindow(Display* pDisp, const Window& wnd)
{
m_pXDisplay = pDisp;
m_iXWindow = wnd;
}
#endif
The method simply sets the arguments to the respective private
data members.
The next method is SetupGLContext
. This method is actually responsible to setup the OpenGL context by calling the appropriate platform based API functions discussed earlier. This method has code discretized by preprocessor directives.
bool CGLView::SetupGLContext(bool iSetPixelFormat)
{
#ifdef WIN32
if(m_hWnd == NULL)
{
return false;
}
m_hDC = ::GetDC(m_hWnd);
if(iSetPixelFormat)
{
SetPixelFormat(m_hDC);
}
if (m_hGLRC = ::wglCreateContext(m_hDC))
{
if(false == wglMakeCurrent(m_hDC, m_hGLRC))
{
MessageBox(m_hWnd, _T("Failed wglMakeCurrent"),
_T("Error!"), MB_ICONERROR);
return false;
}
else
{
glClearColor(0.0, 0.0, 0.0, 1.0);
InitDisplayLists();
}
}
return true;
#else
int attrListSgl[] =
{ GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16, None };
int attrListDbl[] =
{ GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4, GLX_DEPTH_SIZE, 16, None };
m_iXScreen = DefaultScreen(m_pXDisplay);
m_hVisual = glXChooseVisual(m_pXDisplay, m_iXScreen, attrListDbl);
if (NULL == m_hVisual)
{
m_hVisual = glXChooseVisual(m_pXDisplay, m_iXScreen, attrListSgl);
cout << "Singlebuffered : true" << endl;
if(NULL == m_hVisual)
{
cerr << "Could not get suitable XVisualInfo\n" << endl;
return false;
}
}
else
{
cout << "Doublebuffered : true\n" << endl;
}
m_hGLContext = glXCreateContext(m_pXDisplay, m_hVisual, 0, GL_TRUE);
m_ColMap = XCreateColormap
(m_pXDisplay, RootWindow(m_pXDisplay, m_hVisual->screen),
m_hVisual->visual, AllocNone);
if(False == glXMakeCurrent(m_pXDisplay, m_iXWindow, m_hGLContext))
{
return false;
}
else
{
glClearColor(0.0, 0.0, 0.0, 1.0);
InitDisplayLists();
}
glXMakeCurrent(m_pXDisplay, None, NULL);
return true;
#endif
}
Note the additional method call (SetPixelFormat()
), within SetupGLContext()
, for Windows platform. This method is purposefully based on the boolean value coming in as the argument to SetupGLContext()
. This is done so that GL-ready widgets from some toolkits can be accomodated, that is, resetting of PixelFormat
is not required for already set widgets. The developer using the GLView
class should discretely pass the boolean value to SetupGLContext
based on what kind of widget he is using to fit the GLView
class onto.
Once the GLContext
is created, it is made current and the window ready for receiving OpenGL commands by calling wglMakeCurrent()
and glXMakeCurrent()
on Windows and X respectively.
The Resize()
method is responsible to remap the view-port and the viewing volume to the window after it is resized. It does the common OpenGL view-port and matrix transformations stuff on both platforms except for discretely calling the xxxMakeCurrent()
methods.
void CGLView::Resize(unsigned short iWidth, unsigned short iHeight)
{
GLdouble modelMatrix[16];
GLdouble projMatrix[16];
GLint viewport[4];
winH = (GLdouble)iHeight;
winW = (GLdouble)iWidth;
if(iHeight == 0)
iHeight = 1;
#ifdef WIN32
wglMakeCurrent(m_hDC, m_hGLRC);
#else
glXMakeCurrent(m_pXDisplay, m_iXWindow, m_hGLContext);
#endif
glViewport (0, 0, iWidth, iHeight);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
if (iWidth <= iHeight)
glOrtho (-nRange, nRange, -nRange*iHeight/iWidth, nRange*iHeight/iWidth,
-nRange*10000, nRange*10000);
else
glOrtho (-nRange*iWidth/iHeight, nRange*iWidth/iHeight, -nRange, nRange,
-nRange*10000, nRange*10000);
glMatrixMode (GL_MODELVIEW);
glLoadIdentity ();
...
#ifdef WIN32
wglMakeCurrent(NULL,NULL);
#else
glXMakeCurrent(m_pXDisplay, None, NULL);
#endif
}
The RenderScene()
method is responsible to show the OpenGL primitives drawn by the programmer onto the OpenGL-ready window. This method too does mostly the common OpenGL stuff except for the xxxMakeCurrent()
methods and swapping the display buffers.
void CGLView::RenderScene(void)
{
#ifdef WIN32
wglMakeCurrent(m_hDC, m_hGLRC);
#else
glXMakeCurrent(m_pXDisplay, m_iXWindow, m_hGLContext);
#endif
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
DrawGradientBackground();
DoAntialiasing();
Setup3DEnvironment();
glPushMatrix();
{
glRotatef(-45.0f, 1.0f, 0.0f, 0.0f);
glRotatef(-45.0f, 0.0f, 0.0f, 1.0f);
glColor4ub(125, 255, 255, 255);
drawTorus(30, 20, 50, 25, false);
}
glPopMatrix();
#ifdef WIN32
SwapBuffers(m_hDC);
wglMakeCurrent(NULL, NULL);
#else
glXSwapBuffers(m_pXDisplay, m_iXWindow);
glXMakeCurrent(m_pXDisplay, None, NULL);
#endif
}
The Refresh()
method is responsible for repainting the window and thus contains platform specific code.
void CGLView::Refresh( void)
{
#ifdef WIN32
::InvalidateRect(m_hWnd, NULL, FALSE);
#else
XWindowAttributes winattr;
Status st = XGetWindowAttributes(m_pXDisplay, m_iXWindow, &winattr);
XExposeEvent ev =
{ Expose, 0, 1, m_pXDisplay, m_iXWindow, 0, 0,
winattr.width, winattr.height, 0 };
XSendEvent(m_pXDisplay, m_iXWindow, False, ExposureMask, (XEvent *) &ev);
XFlush(m_pXDisplay);
#endif
}
The other methods are up to the developer to enhance the GLView
class and add functionality like view zooming, panning, etc. To keep the class brief for this tutorial, I have not added many such functionalities of a typical OpenGL
View class.
Build a dynamic or a static library and use it as common API for any toolkit or any platform.
Now let us take a look at using our base code library in different toolkits across Windows and Linux.
WINDOWS
To begin with, let's see how to use it in the most commonly used toolkit on Windows -- its own MFC.
In an application with the Document/View architecture, the CView
derived class is the one that hosts the visualization. We use that class to embed our GLView
object. Then we obtain the HWND
from the CView
derived class and setup our GLView
class.
Note the call to the GetSafeWnd()
method to get the HWND
in the OnInitialUpdate()
method.
The client area rectangle is obtained using the GetClientRect()
method and then its dimensions are used to call the Resize()
method to initially resize the GLView
to fit to the client area of the window.
The OnDraw()
method is tapped to call the RenderScene()
method of our GLView
and OnSize()
method to handle the resizing of the GLView
.
CGLViewMFCMDIAppView::CGLViewMFCMDIAppView()
{
m_pGLView = new CGLView();
}
void CGLViewMFCMDIAppView::OnInitialUpdate()
{
CView::OnInitialUpdate();
if(m_pGLView)
{
m_pGLView->SetWindow(GetSafeHwnd());
m_pGLView->SetupGLContext(true);
CRect rect;
GetClientRect(&rect);
m_pGLView->Resize(rect.Width(), rect.Height());
}
}
void CGLViewMFCMDIAppView::OnDraw(CDC* )
{
CGLViewMFCMDIAppDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
if(m_pGLView)
{
m_pGLView->RenderScene();
}
}
void CGLViewMFCMDIAppView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
if(m_pGLView)
{
m_pGLView->Resize(cx, cy);
}
}
Now let us have a look at how we can use the GLView
in an MFC dialog based application. Since we just need the HWND
, we can use our class to render into almost any window or control. But mind you, you can't just render by getting the HWND
alone if the paint event of the control is already handled by the control class. In that case, you just need to subclass the control and handle its paint event. The most suitable control in an MFC dialog is the picture control. Just place a Picture Control on the MFC dialog that you desire to render the OpenGL view. Create a variable for the control and you are all ready to use it in the dialog code to get the HWND
and render the OpenGL view onto it. The suitable methods in the dialog class are OnInitDialog
and OnPaint
for creating the view, setting the window and rendering.
BOOL CGLViewMFCDlgAppDlg::OnInitDialog()
{
CDialog::OnInitDialog();
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE); SetIcon(m_hIcon, FALSE); if(m_pGLView)
{
m_pGLView->SetWindow(m_cGLFrame.GetSafeHwnd());
m_pGLView->SetupGLContext(true);
CRect rect;
m_cGLFrame.GetClientRect(&rect);
m_pGLView->Resize(rect.Width(), rect.Height());
}
return TRUE; }
void CGLViewMFCDlgAppDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); SendMessage(WM_ICONERASEBKGND,
reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
if(m_pGLView)
{
m_pGLView->RenderScene();
}
}
}
Resizing of most dialogs does not happen from the application logic point of view unless your application has specific needs. In that case, for MFC dialogs you need to write additional code so that the control resizes with the dialog and then call the GLView::Resize()
method. Subclassing of controls may be required in this case.
UNIX/Linux
Now let us have a look at using our GLView
class on UNIX/Linux. The most commonly used GUI toolkit on UNIX is Motif though Linux applications are mostly written in GTK and Qt which are cross platform. However, when different flavours of UNIX comes to one's mind and writing a native application on X, Motif is still the commercially preferred toolkit. It is also one of the oldest and most mature ones. So let us have a look at the code in Motif.
The most suitable widget on Motif is the DrawingArea
widget of the widget class xmDrawingAreaWidgetClass
. Since most Motif applications are written in plain C style, we have a static
global variable of our GLView
class. In the main function, we create a DrawingArea
widget and then add the callbacks for the Expose
event (equivalent to paint event on Windows) and the Resize
event.
drawing_area=XtCreateManagedWidget("drawing_area", xmDrawingAreaWidgetClass, form,al,ac);
XtAddCallback (drawing_area, XmNexposeCallback,
(XtCallbackProc)exposeCB, NULL);
XtAddCallback (drawing_area, XmNresizeCallback,
(XtCallbackProc)resizeCB, NULL);
Then we use our GLView
's methods to set the window after retrieving the Display
and Window
from the DrawingArea
widget.
pGLView = new CGLView();
if(pGLView == NULL)
{
cout << "Failed to create CGLView!" << endl;
exit(0);
}
else
{
Arg args[5];
int n = 0;
Dimension width, height;
XtSetArg (args[n], XmNwidth, &width); n++;
XtSetArg (args[n], XmNheight, &height); n++;
XtGetValues (drawing_area, args, n);
pGLView->SetWindow(XtDisplay(drawing_area), XtWindow(drawing_area));
pGLView->SetupGLContext(true);
pGLView->Resize(width, height);
}
Then we just implement the callback methods for Expose
and Resize
events.
void exposeCB(Widget w,
int client_data,
XmDrawingAreaCallbackStruct *cd)
{
if(pGLView != NULL)
{
pGLView->RenderScene();
}
}
void resizeCB(Widget w,
int client_data,
XmDrawingAreaCallbackStruct *cd)
{
Arg args[5];
int n = 0;
Dimension width, height;
XtSetArg (args[n], XmNwidth, &width); n++;
XtSetArg (args[n], XmNheight, &height); n++;
XtGetValues (w, args, n);
if(pGLView != NULL)
{
pGLView->Resize(width, height);
}
}
That's it! Our GLView
is rendering on the native Motif window!
Cross Platform
GTK+ and Qt are some of the leading GUI toolkits available for writing cross platform GUI applications with the "code once build anywhere" way. One important thing that we must remember while using such toolkits is the fact that we are using them in the first place to have the code built on other platforms without having the need to modify the code. We must also understand that these toolkits have some platform specific functions and classes for us to handle platform specific code just like in our case where we need the HWND
on Windows and Window
on X. So let us look into how to use these toolkits along with their platform specific functions so that we can write the application on any one platform and make it suitable to be built directly on multiple platforms.
GTK+
We will be using the GtkDrawingArea
widget to render our GLView
and we wiil be handling the realize, configure and expose events to create our GLView
, setup the window and resize it respectively. We will also be using the platform specific header inclusions. Note the inclusion of gdkwin32.h
on Windows and gdkx.h
on X. The code is pretty much self explanatory and you can easily see how the HWND
is obtained for Windows and the Display
and Window
on X. One notable thing is the macro GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED)
that we use to disable the built-in double buffering supported on the GtkDrawingArea
widget that causes flickering of our GLView
.
#ifdef WIN32
#include <gdk/gdkwin32.h>
#else
#include <gdk/gdkx.h>
#endif
void
UpdateDrawingArea (GtkWidget * widget)
{
GtkWidget *drw = lookup_widget (GTK_WIDGET (widget), "drawingarea1");
gdk_window_invalidate_rect (GTK_WIDGET (drw)->window,
>K_WIDGET (drw)->allocation, FALSE);
}
void
on_drawingarea1_realize (GtkWidget * widget, gpointer user_data)
{
GTK_WIDGET_UNSET_FLAGS (widget, GTK_DOUBLE_BUFFERED);
pGLView = new CGLView ();
if (pGLView)
{
GdkWindow *gdkwin = GTK_WIDGET (widget)->window;
#ifdef WIN32
glong hWnd;
hWnd = (glong) gdk_win32_drawable_get_handle (gdkwin); pGLView->SetWindow ((HWND) hWnd);
#else
Display* pDisplay = gdk_x11_drawable_get_xdisplay(gdkwin);
Window window = (Window) gdk_x11_drawable_get_xid(gdkwin);
pGLView->SetWindow (pDisplay, window);
#endif
pGLView->SetupGLContext (TRUE);
gint w, h;
gtk_widget_get_size_request (widget, &w, &h);
pGLView->Resize (w, h);
}
}
gboolean
on_drawingarea1_configure_event (GtkWidget * widget,
GdkEventConfigure * event,
gpointer user_data)
{
if (pGLView)
{
pGLView->Resize (event->width, event->height);
}
return FALSE;
}
gboolean
on_drawingarea1_expose_event (GtkWidget * widget,
GdkEventExpose * event, gpointer user_data)
{
if (pGLView)
{
pGLView->RenderScene ();
UpdateDrawingArea (GTK_WIDGET (widget));
}
return FALSE;
}
Qt
Qt is an object oriented C++ cross platform GUI toolkit that is becoming increasingly popular equally for desktop and mobile applications. Qt provides a rich set of widget classes and also a fully loaded OpenGL widget. However, if we have a powerful GLView
class fully loaded with all functionality, we can easily port our code from other toolkits to Qt without having to rewrite our OpenGL code using the QGLWidget
. To use our GLView
in a Qt widget, we write a class derived from QWidget
and override its paintEvent()
and resizeEvent()
methods. Since Qt provides a powerful Paint Engine that obscures our GLView
, we need to disable it and also ask the QWidget
to directly render to the screen. We disable the Paint Engine by overriding the paintEngine()
method to return 0 from it and rendering to the screen directly is done by setting the widget attribute Qt::WA_PaintOnScreen
. The constructor is the place where we create our GLView
instance and set the window. the method winId()
gets the HWND
and Window
on Windows and X respectively. On X, to get the Display
, the QWidget
class provides a method x11Info()
that gets the QX11Info
class object which in turn provides a static method display()
.
#ifndef WIN32
#include <QX11Info>
#endif
GLFrame::GLFrame(QWidget *parent)
: QWidget(parent)
{
m_pGLView = new CGLView();
if(m_pGLView)
{
setAttribute(Qt::WA_PaintOnScreen);
#ifdef WIN32
m_pGLView->SetWindow((HWND)winId());
#else
Display* pDisp = x11Info().display();
Window wnd = winId();
m_pGLView->SetWindow(pDisp, wnd);
#endif
m_pGLView->SetupGLContext(true);
m_pGLView->Resize(width(), height());
}
}
QPaintEngine * GLFrame::paintEngine () const
{
return 0;
}
void GLFrame::paintEvent(QPaintEvent* )
{
if(m_pGLView)
{
m_pGLView->RenderScene();
}
}
void GLFrame::resizeEvent(QResizeEvent* event)
{
if(m_pGLView)
{
QSize size = event->size();
m_pGLView->Resize(size.width(), size.height());
}
}
We can very well use our GLView
to render onto the QGLwidget
too! Just override the initializeGL()
, paintGL()
and resizeGL()
methods. No need to disable the double buffering or ask the widget to directly render to the screen. But then, the boolean argument to SetupGLContext()
should be set to false
so that it does not reset the pixel format already setup by the QGLWidget
.
We have seen how to create an OpenGL
view class that can be seamlessly used across different toolkits and platforms.
I hope you enjoyed this article and it makes a lot of sense to make the OpenGL component a flexible one in the software development world where OpenGL is becoming increasingly popular and strong.
The source code for the base code and projects on Windows and Linux has been provided.
Windows project is a VS2008 solution that encompasses the GLView DLL project, MFC MDI and Dialog examples, GTK and Qt examples. You need to have the windows version of GTK+ (I used version 2.12.9) and Qt (I used 4.6.3)
The Linux projects require Lesstif (Open Source Motif clone). I believe OpenMotif should also work. And of course, GTK+ and Qt if you want to build those examples too. The build is done using Autotools and is required to be installed.
The project folder contains a readme.txt file which explains how to setup, build and run the examples. It also has two convenience scripts to build and run the library and samples.
Points of Interest
I have successully used this paradigm also to create a COM component for the GLView
class which not only works for different toolkits but also with different scripting languages like Tcl/Tk (using Tcom), Python (using comtypes) and VBScript. It also works for Windows Forms in C# and VB.NET using .NET/COM interoperability. But since it is a COM component, its use is restricted to the Windows platform only.
There are better ways to make a fully blown object oriented GLView
class using the right Design Patterns, etc. But here, I am focusing on how to write the class in such a way that once you write the code, you need not make any changes to the files and can just compile it on any platform and make it available to any toolkit.
The beauty of this paradigm is that, you have already seen, setting up and rendering a basic OpenGL view requires only 4-5 lines of extra code on any toolkit.
History