gtkglextmm: logo.cc

Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Examples

# logo.cc

GtkGLExt logo demo.

    // -*- C++ -*-
    /*
     * logo.cc:
     * GtkGLExt logo demo.
     *
     * written by Naofumi Yasufuku  <naofumi@users.sourceforge.net>
     */

    #include <iostream>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>

    #include <GL/gl.h>
    #include <GL/glu.h>

    #ifdef G_OS_WIN32
    #define WIN32_LEAN_AND_MEAN 1
    #include <windows.h>
    #endif

    #include "logo.h"

    //
    // logo_draw_*() functions.
    //
    namespace LogoModel {
      #include "logo-model.h"
    }

    //
    // Trackball utilities.
    //
    namespace Trackball {
      extern "C" {
        #include "trackball.h"
      }
    }

    #define DIG_2_RAD (G_PI / 180.0)
    #define RAD_2_DIG (180.0 / G_PI)

    //
    // OpenGL frame buffer configuration utilities.
    //

    struct GLConfigUtil
    {
      static void print_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig,
                                  const char* attrib_str,
                                  int attrib,
                                  bool is_boolean);

      static void examine_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig);
    };

    //
    // Print a configuration attribute.
    //
    void GLConfigUtil::print_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig,
                                       const char* attrib_str,
                                       int attrib,
                                       bool is_boolean)
    {
      int value;

      if (glconfig->get_attrib(attrib, value))
        {
          std::cout << attrib_str << " = ";
          if (is_boolean)
            std::cout << (value == true ? "true" : "false") << std::endl;
          else
            std::cout << value << std::endl;
        }
      else
        {
          std::cout << "*** Cannot get "
                    << attrib_str
                    << " attribute value\n";
        }
    }

    //
    // Print configuration attributes.
    //
    void GLConfigUtil::examine_gl_attrib(const Glib::RefPtr<const Gdk::GL::Config>& glconfig)
    {
      std::cout << "\nOpenGL visual configurations :\n\n";

      std::cout << "glconfig->is_rgba() = "
                << (glconfig->is_rgba() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->is_double_buffered() = "
                << (glconfig->is_double_buffered() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->is_stereo() = "
                << (glconfig->is_stereo() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_alpha() = "
                << (glconfig->has_alpha() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_depth_buffer() = "
                << (glconfig->has_depth_buffer() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_stencil_buffer() = "
                << (glconfig->has_stencil_buffer() ? "true" : "false")
                << std::endl;
      std::cout << "glconfig->has_accum_buffer() = "
                << (glconfig->has_accum_buffer() ? "true" : "false")
                << std::endl;

      std::cout << std::endl;

      print_gl_attrib(glconfig, "Gdk::GL::USE_GL",           Gdk::GL::USE_GL,           true);
      print_gl_attrib(glconfig, "Gdk::GL::BUFFER_SIZE",      Gdk::GL::BUFFER_SIZE,      false);
      print_gl_attrib(glconfig, "Gdk::GL::LEVEL",            Gdk::GL::LEVEL,            false);
      print_gl_attrib(glconfig, "Gdk::GL::RGBA",             Gdk::GL::RGBA,             true);
      print_gl_attrib(glconfig, "Gdk::GL::DOUBLEBUFFER",     Gdk::GL::DOUBLEBUFFER,     true);
      print_gl_attrib(glconfig, "Gdk::GL::STEREO",           Gdk::GL::STEREO,           true);
      print_gl_attrib(glconfig, "Gdk::GL::AUX_BUFFERS",      Gdk::GL::AUX_BUFFERS,      false);
      print_gl_attrib(glconfig, "Gdk::GL::RED_SIZE",         Gdk::GL::RED_SIZE,         false);
      print_gl_attrib(glconfig, "Gdk::GL::GREEN_SIZE",       Gdk::GL::GREEN_SIZE,       false);
      print_gl_attrib(glconfig, "Gdk::GL::BLUE_SIZE",        Gdk::GL::BLUE_SIZE,        false);
      print_gl_attrib(glconfig, "Gdk::GL::ALPHA_SIZE",       Gdk::GL::ALPHA_SIZE,       false);
      print_gl_attrib(glconfig, "Gdk::GL::DEPTH_SIZE",       Gdk::GL::DEPTH_SIZE,       false);
      print_gl_attrib(glconfig, "Gdk::GL::STENCIL_SIZE",     Gdk::GL::STENCIL_SIZE,     false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_RED_SIZE",   Gdk::GL::ACCUM_RED_SIZE,   false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_GREEN_SIZE", Gdk::GL::ACCUM_GREEN_SIZE, false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_BLUE_SIZE",  Gdk::GL::ACCUM_BLUE_SIZE,  false);
      print_gl_attrib(glconfig, "Gdk::GL::ACCUM_ALPHA_SIZE", Gdk::GL::ACCUM_ALPHA_SIZE, false);

      std::cout << std::endl;
    }

    //
    // Logo classes.
    //

    namespace Logo
    {

      //
      // View class implementation.
      //

      const float View::NEAR_CLIP   = 2.0;
      const float View::FAR_CLIP    = 60.0;

      const float View::INIT_POS_X  = 0.0;
      const float View::INIT_POS_Y  = 0.0;
      const float View::INIT_POS_Z  = -30.0;

      const float View::INIT_AXIS_X = 1.0;
      const float View::INIT_AXIS_Y = 0.0;
      const float View::INIT_AXIS_Z = 0.0;
      const float View::INIT_ANGLE  = 20.0;

      const float View::INIT_SCALE  = 1.0;

      const float View::SCALE_MAX   = 2.0;
      const float View::SCALE_MIN   = 0.5;

      View::View()
        : m_Scale(INIT_SCALE), m_BeginX(0.0), m_BeginY(0.0)
      {
        reset();
      }

      View::~View()
      {
      }

      void View::frustum(int w, int h)
      {
        glViewport(0, 0, w, h);

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();

        if (w > h) {
          float aspect = static_cast<float>(w) / static_cast<float>(h);
          glFrustum(-aspect, aspect, -1.0, 1.0, NEAR_CLIP, FAR_CLIP);
        } else {
          float aspect = static_cast<float>(h) / static_cast<float>(w);
          glFrustum(-1.0, 1.0, -aspect, aspect, NEAR_CLIP, FAR_CLIP);
        }

        glMatrixMode(GL_MODELVIEW);
      }

      void View::xform()
      {
        glTranslatef(m_Pos[0], m_Pos[1], m_Pos[2]);

        glScalef(m_Scale, m_Scale, m_Scale);

        float m[4][4];
        Trackball::build_rotmatrix(m, m_Quat);
        glMultMatrixf(&m[0][0]);
      }

      void View::reset()
      {
        m_Pos[0] = INIT_POS_X;
        m_Pos[1] = INIT_POS_Y;
        m_Pos[2] = INIT_POS_Z;

        float sine = sin(0.5 * INIT_ANGLE * DIG_2_RAD);
        m_Quat[0] = INIT_AXIS_X * sine;
        m_Quat[1] = INIT_AXIS_Y * sine;
        m_Quat[2] = INIT_AXIS_Z * sine;
        m_Quat[3] = cos(0.5 * INIT_ANGLE * DIG_2_RAD);

        m_Scale = INIT_SCALE;
      }

      bool View::on_button_press_event(GdkEventButton* event,
                                       Scene* scene)
      {
        m_BeginX = event->x;
        m_BeginY = event->y;

        // don't block
        return false;
      }

      bool View::on_motion_notify_event(GdkEventMotion* event,
                                        Scene* scene)
      {
        if (scene == 0)
          return false;

        float w = scene->get_width();
        float h = scene->get_height();
        float x = event->x;
        float y = event->y;
        float d_quat[4];
        bool redraw = false;

        // Rotation.
        if (event->state & GDK_BUTTON1_MASK) {
          Trackball::trackball(d_quat,
                               (2.0 * m_BeginX - w) / w,
                               (h - 2.0 * m_BeginY) / h,
                               (2.0 * x - w) / w,
                               (h - 2.0 * y) / h);
          Trackball::add_quats(d_quat, m_Quat, m_Quat);
          redraw = true;
        }

        // Scaling.
        if (event->state & GDK_BUTTON2_MASK) {
          m_Scale = m_Scale * (1.0 + (y - m_BeginY) / h);
          if (m_Scale > SCALE_MAX)
            m_Scale = SCALE_MAX;
          else if (m_Scale < SCALE_MIN)
            m_Scale = SCALE_MIN;
          redraw = true;
        }

        m_BeginX = x;
        m_BeginY = y;

        if (redraw && !scene->anim_is_enabled())
          scene->invalidate();

        // don't block
        return false;
      }

      //
      // Model class implementation.
      //

      const float Model::MAT_SPECULAR[4]  = { 0.5, 0.5, 0.5, 1.0 };
      const float Model::MAT_SHININESS[1] = { 10.0 };
      const float Model::MAT_BLACK[4]     = { 0.0, 0.0, 0.0, 1.0 };
      const float Model::MAT_RED[4]       = { 1.0, 0.0, 0.0, 1.0 };
      const float Model::MAT_GREEN[4]     = { 0.0, 1.0, 0.0, 1.0 };
      const float Model::MAT_BLUE[4]      = { 0.0, 0.0, 1.0, 1.0 };

      const unsigned int Model::DEFAULT_ROT_COUNT = 100;

      static float AXIS_X[3] = { 1.0, 0.0, 0.0 };
      static float AXIS_Y[3] = { 0.0, 1.0, 0.0 };
      static float AXIS_Z[3] = { 0.0, 0.0, 1.0 };

      const Model::RotMode Model::ROT_MODE[] = {
        { AXIS_X,  1.0 },
        { AXIS_Y,  1.0 },
        { AXIS_X,  1.0 },
        { AXIS_Z,  1.0 },
        { AXIS_X,  1.0 },
        { AXIS_Y, -1.0 },
        { AXIS_X,  1.0 },
        { AXIS_Z, -1.0 },
        { 0,       0.0 }  // terminator
      };

      Model::Model(unsigned int rot_count,
                   bool enable_anim)
        : m_RotCount(rot_count),
          m_EnableAnim(enable_anim), m_Mode(0), m_Counter(0)
      {
        reset_anim();
      }

      Model::~Model()
      {
      }

      void Model::init_gl()
      {
        glEnable(GL_CULL_FACE);

        glPushMatrix();

        glMaterialfv(GL_FRONT, GL_SPECULAR, MAT_SPECULAR);
        glMaterialfv(GL_FRONT, GL_SHININESS, MAT_SHININESS);

        /* Center black cube. */
        glNewList(CUBE, GL_COMPILE);
          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK);
          LogoModel::logo_draw_cube();
        glEndList();

        /* Forward "G". */
        glNewList(G_FORWARD, GL_COMPILE);
          glDisable(GL_CULL_FACE);
          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLUE);
          LogoModel::logo_draw_g_plane();
        glEnable(GL_CULL_FACE);

        glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK);
          LogoModel::logo_draw_g();
        glEndList();

        /* Backward "G". */
        glNewList(G_BACKWARD, GL_COMPILE);
          glPushMatrix();
            glRotatef(180.0, 1.0, 0.0, 0.0);

            glDisable(GL_CULL_FACE);
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLUE);
            LogoModel::logo_draw_g_plane();
            glEnable(GL_CULL_FACE);

            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK);
            LogoModel::logo_draw_g();

          glPopMatrix();
        glEndList();

        /* Forward "T". */
        glNewList(T_FORWARD, GL_COMPILE);
          glDisable(GL_CULL_FACE);
          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_RED);
          LogoModel::logo_draw_t_plane();
          glEnable(GL_CULL_FACE);

          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK);
          LogoModel::logo_draw_t();
        glEndList();

        /* Backward "T". */
        glNewList(T_BACKWARD, GL_COMPILE);
          glPushMatrix();
            glRotatef(180.0, 1.0, 0.0, 0.0);

            glDisable(GL_CULL_FACE);
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_RED);
            LogoModel::logo_draw_t_plane();
            glEnable(GL_CULL_FACE);

            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK);
            LogoModel::logo_draw_t();

          glPopMatrix();
        glEndList();

        /* Forward "K". */
        glNewList(K_FORWARD, GL_COMPILE);
          glDisable(GL_CULL_FACE);
          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_GREEN);
          LogoModel::logo_draw_k_plane();
          glEnable(GL_CULL_FACE);

          glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK);
          LogoModel::logo_draw_k();
        glEndList();

        /* Backward "K". */
        glNewList(K_BACKWARD, GL_COMPILE);
          glPushMatrix();
            glRotatef(180.0, 0.0, 0.0, 1.0);

            glDisable(GL_CULL_FACE);
            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_GREEN);
            LogoModel::logo_draw_k_plane();
            glEnable(GL_CULL_FACE);

            glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, MAT_BLACK);
            LogoModel::logo_draw_k();
          glPopMatrix();
        glEndList();

        glPopMatrix();

        glEnable(GL_NORMALIZE);
      }

      void Model::draw()
      {
        // Init GL context.
        static bool initialized = false;
        if (!initialized) {
          init_gl();
          initialized = true;
        }

        // Animation.
        if (m_EnableAnim) {
          if (m_Counter == m_RotCount) {
            if (ROT_MODE[++m_Mode].axis == 0)
              m_Mode = 0;
            m_Counter = 0;
          }

          float d_quat[4];
          Trackball::axis_to_quat(ROT_MODE[m_Mode].axis,
                                  ROT_MODE[m_Mode].sign * G_PI_2 / m_RotCount,
                                  d_quat);
          Trackball::add_quats(d_quat, m_Quat, m_Quat);

          ++m_Counter;
        }

        // Draw logo model.
        glPushMatrix();
          glTranslatef(m_Pos[0], m_Pos[1], m_Pos[2]);

          float m[4][4];
          Trackball::build_rotmatrix(m, m_Quat);
          glMultMatrixf(&m[0][0]);

          glRotatef(90.0, 1.0, 0.0, 0.0);
          glCallList(CUBE);
          glCallList(G_FORWARD);
          glCallList(G_BACKWARD);
          glCallList(T_FORWARD);
          glCallList(T_BACKWARD);
          glCallList(K_FORWARD);
          glCallList(K_BACKWARD);
        glPopMatrix();
      }

      void Model::reset_anim()
      {
        m_Pos[0] = 0.0;
        m_Pos[1] = 0.0;
        m_Pos[2] = 0.0;

        m_Quat[0] = 0.0;
        m_Quat[1] = 0.0;
        m_Quat[2] = 0.0;
        m_Quat[3] = 1.0;

        m_Mode = 0;
        m_Counter = 0;
      }

      //
      // Scene class implementation.
      //

      const unsigned int Scene::TIMEOUT_INTERVAL = 10;

      const float Scene::CLEAR_COLOR[4] = { 0.5, 0.5, 0.8, 1.0 };
      const float Scene::CLEAR_DEPTH    = 1.0;

      const float Scene::LIGHT0_POSITION[4] = { 0.0, 0.0, 30.0, 0.0 };
      const float Scene::LIGHT0_DIFFUSE[4]  = { 1.0, 1.0, 1.0, 1.0 };
      const float Scene::LIGHT0_SPECULAR[4] = { 1.0, 1.0, 1.0, 1.0 };

      Scene::Scene(unsigned int rot_count,
                   bool enable_anim)
        : m_Menu(0), m_Model(rot_count, enable_anim)
      {
        //
        // Configure OpenGL-capable visual.
        //

        Glib::RefPtr<Gdk::GL::Config> glconfig;

        // Try double-buffered visual
        glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB    |
                                           Gdk::GL::MODE_DEPTH  |
                                           Gdk::GL::MODE_DOUBLE);
        if (!glconfig) {
          std::cerr << "*** Cannot find the double-buffered visual.\n"
                    << "*** Trying single-buffered visual.\n";

          // Try single-buffered visual
          glconfig = Gdk::GL::Config::create(Gdk::GL::MODE_RGB   |
                                             Gdk::GL::MODE_DEPTH);
          if (!glconfig) {
            std::cerr << "*** Cannot find any OpenGL-capable visual.\n";
            std::exit(1);
          }
        }

        // print frame buffer attributes.
        GLConfigUtil::examine_gl_attrib(glconfig);

        //
        // Set OpenGL-capability to the widget.
        //

        set_gl_capability(glconfig);

        //
        // Add events.
        //
        add_events(Gdk::BUTTON1_MOTION_MASK    |
                   Gdk::BUTTON2_MOTION_MASK    |
                   Gdk::BUTTON_PRESS_MASK      |
                   Gdk::VISIBILITY_NOTIFY_MASK);

        // View transformation signals.
        signal_button_press_event().connect(
          sigc::bind(sigc::mem_fun(m_View, &View::on_button_press_event), this));
        signal_motion_notify_event().connect(
          sigc::bind(sigc::mem_fun(m_View, &View::on_motion_notify_event), this));

        //
        // Popup menu.
        //

        m_Menu = create_popup_menu();
      }

      Scene::~Scene()
      {
      }

      void Scene::on_realize()
      {
        // We need to call the base on_realize()
        Gtk::DrawingArea::on_realize();

        //
        // Get GL::Drawable.
        //

        Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

        //
        // GL calls.
        //

        // *** OpenGL BEGIN ***
        if (!gldrawable->gl_begin(get_gl_context()))
          return;

        glClearColor(CLEAR_COLOR[0], CLEAR_COLOR[1], CLEAR_COLOR[2], CLEAR_COLOR[3]);
        glClearDepth(CLEAR_DEPTH);

        glLightfv(GL_LIGHT0, GL_POSITION, LIGHT0_POSITION);
        glLightfv(GL_LIGHT0, GL_DIFFUSE,  LIGHT0_DIFFUSE);
        glLightfv(GL_LIGHT0, GL_SPECULAR, LIGHT0_SPECULAR);

        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);

        glEnable(GL_DEPTH_TEST);

        glShadeModel(GL_SMOOTH);

        gldrawable->gl_end();
        // *** OpenGL END ***
      }

      bool Scene::on_configure_event(GdkEventConfigure* event)
      {
        //
        // Get GL::Drawable.
        //

        Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

        //
        // GL calls.
        //

        // *** OpenGL BEGIN ***
        if (!gldrawable->gl_begin(get_gl_context()))
          return false;

        m_View.frustum(get_width(), get_height());

        gldrawable->gl_end();
        // *** OpenGL END ***

        return true;
      }

      bool Scene::on_expose_event(GdkEventExpose* event)
      {
        //
        // Get GL::Drawable.
        //

        Glib::RefPtr<Gdk::GL::Drawable> gldrawable = get_gl_drawable();

        //
        // GL calls.
        //

        // *** OpenGL BEGIN ***
        if (!gldrawable->gl_begin(get_gl_context()))
          return false;

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glLoadIdentity();

        // View transformation.
        m_View.xform();

        // Logo model.
        m_Model.draw();

        // Swap buffers.
        if (gldrawable->is_double_buffered())
          gldrawable->swap_buffers();
        else
          glFlush();

        gldrawable->gl_end();
        // *** OpenGL END ***

        return true;
      }

      bool Scene::on_button_press_event(GdkEventButton* event)
      {
        if (event->button == 3) {
          m_Menu->popup(event->button, event->time);
          return true;
        }

        // don't block
        return false;
      }

      bool Scene::on_map_event(GdkEventAny* event)
      {
        if (m_Model.anim_is_enabled())
          timeout_add();

        return true;
      }

      bool Scene::on_unmap_event(GdkEventAny* event)
      {
        timeout_remove();

        return true;
      }

      bool Scene::on_visibility_notify_event(GdkEventVisibility* event)
      {
        if (m_Model.anim_is_enabled()) {
          if (event->state == GDK_VISIBILITY_FULLY_OBSCURED)
            timeout_remove();
          else
            timeout_add();
        }

        return true;
      }

      bool Scene::on_timeout()
      {
        // Invalidate whole window.
        invalidate();
        // Update window synchronously (fast).
        update();

        return true;
      }

      void Scene::timeout_add()
      {
        if (!m_ConnectionTimeout.connected())
          m_ConnectionTimeout = Glib::signal_timeout().connect(
            sigc::mem_fun(*this, &Scene::on_timeout), TIMEOUT_INTERVAL);
      }

      void Scene::timeout_remove()
      {
        if (m_ConnectionTimeout.connected())
          m_ConnectionTimeout.disconnect();
      }

      void Scene::toggle_anim()
      {
        if (m_Model.anim_is_enabled()) {
          m_Model.disable_anim();
          timeout_remove();
        } else {
          m_Model.enable_anim();
          timeout_add();
        }
      }

      void Scene::init_anim()
      {
        m_View.reset();
        m_Model.reset_anim();

        invalidate();
      }

      Gtk::Menu* Scene::create_popup_menu()
      {
        Gtk::Menu* menu = Gtk::manage(new Gtk::Menu());

        Gtk::Menu::MenuList& menu_list = menu->items();

        // Toggle animation
        menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Toggle Animation",
          sigc::mem_fun(*this, &Scene::toggle_anim)));

        // Init orientation
        menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Initialize",
          sigc::mem_fun(*this, &Scene::init_anim)));

        // Quit
        menu_list.push_back(Gtk::Menu_Helpers::MenuElem("Quit",
          sigc::ptr_fun(&Gtk::Main::quit)));

        return menu;
      }

      //
      // Application class implementation.
      //

      const Glib::ustring Application::APP_NAME = "Logo";

      Application::Application(unsigned int rot_count,
                               bool enable_anim)
        : m_VBox(false, 0), m_Scene(rot_count, enable_anim), m_ButtonQuit("Quit")
      {
        //
        // Top-level window.
        //

        set_title(APP_NAME);

        // Get automatically redrawn if any of their children changed allocation.
        set_reallocate_redraws(true);

        add(m_VBox);

        //
        // Application scene.
        //

        m_Scene.set_size_request(300, 300);

        m_VBox.pack_start(m_Scene);

        //
        // Simple quit button.
        //

        m_ButtonQuit.signal_clicked().connect(
          sigc::mem_fun(*this, &Application::on_button_quit_clicked));

        m_VBox.pack_start(m_ButtonQuit, Gtk::PACK_SHRINK, 0);

        //
        // Show window.
        //

        show_all();
      }

      Application::~Application()
      {
      }

      void Application::on_button_quit_clicked()
      {
        Gtk::Main::quit();
      }

      bool Application::on_key_press_event(GdkEventKey* event)
      {
        switch (event->keyval) {
        case GDK_a:
          m_Scene.toggle_anim();
          break;
        case GDK_i:
          m_Scene.init_anim();
          break;
        case GDK_Escape:
          Gtk::Main::quit();
          break;
        default:
          return true;
        }

        m_Scene.invalidate();

        return true;
      }

    } // namespace Logo

    //
    // Main.
    //

    int main(int argc, char** argv)
    {
      Gtk::Main kit(argc, argv);

      //
      // Init gtkglextmm.
      //

      Gtk::GL::init(argc, argv);

      //
      // Parse arguments.
      //

      unsigned int rot_count = Logo::Model::DEFAULT_ROT_COUNT;
      bool enable_anim = true;

      bool arg_count = false;
      for (int i = 1; i < argc; ++i) {
        if (arg_count)
          rot_count = std::atoi(argv[i]);

        if (std::strcmp(argv[i], "--help") == 0 ||
            std::strcmp(argv[i], "-h") == 0) {
          std::cerr << "Usage: "
                    << argv[0]
                    << " [-count num] [-noanim] [--help]\n";
          std::exit(0);
        }

        if (std::strcmp(argv[i], "-count") == 0)
          arg_count = true;

        if (std::strcmp(argv[i], "-noanim") == 0)
          enable_anim = false;
      }

      //
      // Query OpenGL extension version.
      //

      int major, minor;
      Gdk::GL::query_version(major, minor);
      std::cout << "OpenGL extension version - "
                << major << "." << minor << std::endl;

      //
      // Instantiate and run the application.
      //

      Logo::Application application(rot_count, enable_anim);

      kit.run(application);

      return 0;
    }