Making a Subclass of Fl_Gl_Window

To make a subclass of Fl_Gl_Window, you must provide:

If your subclass provides static controls in the window, they must be redrawn whenever the FL_DAMAGE_ALL bit is set in the value returned by damage(). For double-buffered windows you will need to surround the drawing code with the following code to make sure that both buffers are redrawn:

#ifndef MESA
glDrawBuffer(GL_FRONT_AND_BACK);
#endif // !MESA
... draw stuff here ...
#ifndef MESA
glDrawBuffer(GL_BACK);
#endif // !MESA

Note: If you are using the Mesa graphics library, the call to glDrawBuffer() is not required and will slow down drawing considerably. The preprocessor instructions shown above will optimize your code based upon the graphics library used.

Defining the Subclass

To define the subclass you just subclass the Fl_Gl_Window class:

class MyWindow : public Fl_Gl_Window {
  void draw();
  int handle(int);

public:
  MyWindow(int X, int Y, int W, int H, const char *L)
    : Fl_Gl_Window(X, Y, W, H, L) {}
};

The draw() and handle() methods are described below. Like any widget, you can include additional private and public data in your class (such as scene graph information, etc.)

The draw() Method

The draw() method is where you actually do your OpenGL drawing:

void MyWindow::draw() {
  if (!valid()) {
    ... set up projection, viewport, etc ...
    ... window size is in w() and h().
    ... valid() is turned on by FLTK after draw() returns
  }
  ... draw ...
}

The handle() Method

The handle() method handles mouse and keyboard events for the window:

int MyWindow::handle(int event) {
  switch(event) {
  case FL_PUSH:
    ... mouse down event ...
    ... position in Fl::event_x() and Fl::event_y()
    return 1;
  case FL_DRAG:
    ... mouse moved while down event ...
    return 1;
  case FL_RELEASE:    
    ... mouse up event ...
    return 1;
  case FL_FOCUS :
  case FL_UNFOCUS :
    ... Return 1 if you want keyboard events, 0 otherwise
    return 1;
  case FL_KEYBOARD:
    ... keypress, key is in Fl::event_key(), ascii in Fl::event_text()
    ... Return 1 if you understand/use the keyboard event, 0 otherwise...
    return 1;
  case FL_SHORTCUT:
    ... shortcut, key is in Fl::event_key(), ascii in Fl::event_text()
    ... Return 1 if you understand/use the shortcut event, 0 otherwise...
    return 1;
  default:
    // tell FLTK that I don't understand other events
    return 0;
  }
}

When handle() is called, the OpenGL context is not set up! If your display changes, you should call redraw() and let draw() do the work. Don't call any OpenGL drawing functions from inside handle()!

You can call some OpenGL stuff like hit detection and texture loading functions by doing:

  case FL_PUSH:
    make_current(); // make OpenGL context current
    if (!valid()) {
      ... set up projection exactly the same as draw ...
      valid(1); // stop it from doing this next time
    }
    ... ok to call NON-DRAWING OpenGL code here, such as hit
    detection, loading textures, etc...

Your main program can now create one of your windows by doing new MyWindow(...). You can also use FLUID by:

  1. Putting your class definition in a MyWindow.H file.

  2. Creating a Fl_Box widget in FLUID.

  3. In the widget panel fill in the "class" field with MyWindow. This will make FLUID produce constructors for your new class.

  4. In the "Extra Code" field put #include "MyWindow.H", so that the FLUID output file will compile.

You must put glwindow->show() in your main code after calling show() on the window containing the OpenGL window.