Difference between revisions of "Using CEGUI with GLUT"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
(The New Loop)
m (Robot: Cosmetic changes)
 
(5 intermediate revisions by 3 users not shown)
Line 1: Line 1:
GLUT is the OpenGL Utility Toolkit. It was designed to make it easy to create
+
{{VersionBadge|0.6}}
 +
GLUT is the OpenGL Utility Toolkit. It was designed to make it easy to create
 
simple OpenGL application across different platforms. One reason to use GLUT is
 
simple OpenGL application across different platforms. One reason to use GLUT is
 
you may want a basic life support system while you design your CEGUI code.
 
you may want a basic life support system while you design your CEGUI code.
Line 8: Line 9:
  
 
The information presented here is the result of a couple days of tinkering with
 
The information presented here is the result of a couple days of tinkering with
CEGUI and FreeGLUT on Linux. Let me clearly state that I am an not an expert
+
CEGUI and FreeGLUT on Linux. Let me clearly state that I am an not an expert
 
on CEGUI, GLUT, C++, or Linux and the things I say may not be the optimum
 
on CEGUI, GLUT, C++, or Linux and the things I say may not be the optimum
approaches. This information is presented with the simple hope that some may
+
approaches. This information is presented with the simple hope that some may
 
find it useful.
 
find it useful.
  
  
==The Loop==
+
== The Loop ==
  
 
We're going to specify FreeGLUT instead of GLUT because we need one of the new
 
We're going to specify FreeGLUT instead of GLUT because we need one of the new
functions added. In GLUT you call glutMainLoop() and it never comes back.
+
functions added. In GLUT you call glutMainLoop() and it never comes back.
 
FreeGLUT gives us glutMainLoopEvent which invokes one loop and then returns
 
FreeGLUT gives us glutMainLoopEvent which invokes one loop and then returns
control. We need this arrangement so we can call CEGUI's renderer later.
+
control. We need this arrangement so we can call CEGUI's renderer later.
  
 
This is a simple teapot drawing app which should compile and run provided you
 
This is a simple teapot drawing app which should compile and run provided you
Line 39: Line 40:
 
  //
 
  //
 
  int main()
 
  int main()
    {
+
    {
    // Create our OpenGL Window
+
    // Create our OpenGL Window
    int fake_argc = 1;
+
    int fake_argc = 1;
    char* fake_argv;
+
    char* fake_argv;
    glutInit(&fake_argc, &fake_argv);
+
    glutInit(&fake_argc, &fake_argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
+
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(640, 480);
+
    glutInitWindowSize(640, 480);
    window_id = glutCreateWindow("GLUT Loop");
+
    window_id = glutCreateWindow("GLUT Loop");
    // Set the function to handle normal key presses
+
    // Set the function to handle normal key presses
    glutKeyboardFunc(keyFunc);
+
    glutKeyboardFunc(keyFunc);
    // Begin the loop
+
    // Begin the loop
    while(keep_running)
+
    while(keep_running)
        {
+
        {
        glutMainLoopEvent();
+
        glutMainLoopEvent();
        render();
+
        render();
        glutSwapBuffers();
+
        glutSwapBuffers();
        }
+
        }
    // Exit gracefully
+
    // Exit gracefully
    glutDestroyWindow(window_id);
+
    glutDestroyWindow(window_id);
    return 0;
+
    return 0;
    }
+
    }
 
  //
 
  //
 
  // This is where you'd draw a frame of your 3D application
 
  // This is where you'd draw a frame of your 3D application
 
  //
 
  //
 
  void render()
 
  void render()
        {
+
    {
        glLoadIdentity();
+
    glLoadIdentity();
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // Look on my works, ye Mighty, and despair!
+
    // Look on my works, ye Mighty, and despair!
        glutWireTeapot(0.5);
+
    glutWireTeapot( 0.5 );
        }
+
    }
 
  //
 
  //
 
  //  Handler for normal keypressed
 
  //  Handler for normal keypressed
 
  //
 
  //
 
  void keyFunc(unsigned char key, int x, int y)
 
  void keyFunc(unsigned char key, int x, int y)
    {
+
    {
    switch (key)
+
    switch (key)
        {
+
        {
        case 113:  // 'q',
+
        case 113:  // 'q',
        case 81:    // 'Q',
+
        case 81:    // 'Q',
        case 27:    // 'ESC'
+
        case 27:    // 'ESC'
 
             keep_running = false;
 
             keep_running = false;
 
             break;
 
             break;
        }
+
        }
    }
+
    }
  
==Makefile for GLUT Only==
+
== Makefile for GLUT Only ==
  
 
Remember, 'make' expects tabs not spaces when indenting compiler commands.
 
Remember, 'make' expects tabs not spaces when indenting compiler commands.
Line 100: Line 101:
 
     rm -rf *.o glut_loop
 
     rm -rf *.o glut_loop
  
==GLUT's Keyboard Callbacks==
+
== GLUT's Keyboard Callbacks ==
  
 
A note on GLUT's keyboard functions:
 
A note on GLUT's keyboard functions:
  
 
Keys that don't generate an ASCII value or aren't in the short list for
 
Keys that don't generate an ASCII value or aren't in the short list for
glutSpecialFunc generate ... nothing. There are no events triggered when
+
glutSpecialFunc generate ... nothing. There are no events triggered when
pressing the CTRL, ALT, or Shift keys. There is no way to tell if '5' was
+
pressing the CTRL, ALT, or Shift keys. There is no way to tell if '5' was
pressed on the keypad instead of the number keys. Later, I will show you a way
+
pressed on the keypad instead of the number keys. Later, I will show you a way
 
to fake CTRL, ALT, and Shift for CEGUI but it wont be in real time.
 
to fake CTRL, ALT, and Shift for CEGUI but it wont be in real time.
  
Line 121: Line 122:
  
  
Set the handler for a normal keypress. These would be keys that generate
+
Set the handler for a normal keypress. These would be keys that generate
normal ASCII characters. 'Delete' (ASCII 127) is a normal key. 'Insert' is
+
normal ASCII characters. 'Delete' (ASCII 127) is a normal key. 'Insert' is
 
not. The special keys are handled by glutSpecialFunc (see below).
 
not. The special keys are handled by glutSpecialFunc (see below).
  
Line 143: Line 144:
  
  
Set the handler for a special keypress.
+
Set the handler for a special keypress.  
  
 
Example Handler:
 
Example Handler:
Line 163: Line 164:
 
You get an int with the special key value and the current mouse x and y.
 
You get an int with the special key value and the current mouse x and y.
  
==Connecting GLUT's Keyboard Callbacks to CEGUI==
+
== Connecting GLUT's Keyboard Callbacks to CEGUI ==
  
As you probably know, CEGUI does not capture any input. You need to feed it in
+
As you probably know, CEGUI does not capture any input. You need to feed it in
 
it with the various .inject_Something_() functions.
 
it with the various .inject_Something_() functions.
  
Line 173: Line 174:
  
 
As mentioned, GLUT does not generate an event when the user presses or releases
 
As mentioned, GLUT does not generate an event when the user presses or releases
these keys. If does have a function called glutGetModifiers() that lets you
+
these keys. If does have a function called glutGetModifiers() that lets you
poll their state. It returns an INT with certain bits set.
+
poll their state. It returns an INT with certain bits set.
  
 
First we need to add some global flags to our program:
 
First we need to add some global flags to our program:
Line 240: Line 241:
 
     }
 
     }
  
We'll call this in every keyboard handler. It's not pretty but it should keep
+
We'll call this in every keyboard handler. It's not pretty but it should keep
 
CEGUI informed.
 
CEGUI informed.
  
Line 589: Line 590:
  
  
==GLUT's Mouse Callbacks==
+
== GLUT's Mouse Callbacks ==
  
  
Line 595: Line 596:
  
  
Sets the handler for any mouse button function. This includes presses and
+
Sets the handler for any mouse button function. This includes presses and
releases. Scrolling a mouse wheels generates a press and a release for each
+
releases. Scrolling a mouse wheels generates a press and a release for each
 
tick.
 
tick.
  
Line 623: Line 624:
  
 
* void myPassiveMotionFunc(int x, int y)
 
* void myPassiveMotionFunc(int x, int y)
Pretty simple, x == x, y == y. You will not receive callbacks when the mouse
+
Pretty simple, x == x, y == y. You will not receive callbacks when the mouse
 
pointer leaves the GLUT window.
 
pointer leaves the GLUT window.
  
Line 635: Line 636:
  
 
* void myMotionFunc(int x, int y)
 
* void myMotionFunc(int x, int y)
Again x==x, y==y. The behavoir is a little different in that you will get
+
Again x==x, y==y. The behavoir is a little different in that you will get
 
callbacks with a button held and the mouse point dragged out of the GLUT window.
 
callbacks with a button held and the mouse point dragged out of the GLUT window.
  
Line 642: Line 643:
  
  
==Connecting GLUT's Mouse Callbacks to CEGUI==
+
== Connecting GLUT's Mouse Callbacks to CEGUI ==
  
  
Line 649: Line 650:
  
 
  void myMouseFunc(int button, int state, int x, int y)
 
  void myMouseFunc(int button, int state, int x, int y)
    {
+
    {
    if (state == 0)    // State 0 = Button Pressed
+
    if (state == 0)    // State 0 = Button Pressed
        {
+
        {
        switch (button)
+
        switch (button)
            {
+
            {
            case 0:    // glut's left mouse button
+
            case 0:    // glut's left mouse button
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton);
+
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton);
                break;
+
                break;
            case 1:    // glut's middle mouse button
+
            case 1:    // glut's middle mouse button
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::MiddleButton);
+
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::MiddleButton);
                break;
+
                break;
            case 2:    // glut's right mouse button
+
            case 2:    // glut's right mouse button
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton);
+
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton);
                break;
+
                break;
            case 3:    // glut's mouse wheel up
+
            case 3:    // glut's mouse wheel up
                CEGUI::System::getSingleton().injectMouseWheelChange(2.0);
+
                CEGUI::System::getSingleton().injectMouseWheelChange(2.0);
                break;
+
                break;
            case 4:    // glut's mouse wheen down
+
            case 4:    // glut's mouse wheen down
                CEGUI::System::getSingleton().injectMouseWheelChange(-2.0);
+
                CEGUI::System::getSingleton().injectMouseWheelChange(-2.0);
                break;
+
                break;
            }
+
            }
        }
+
        }
    else if (state == 1)    // State 1 = Button Released
+
    else if (state == 1)    // State 1 = Button Released
        {
+
        {
        switch (button)
+
        switch (button)
            {
+
            {
            case 0:    // glut's left mouse button
+
            case 0:    // glut's left mouse button
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton);
+
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton);
                break;
+
                break;
            case 1:    // glut's middle mouse button
+
            case 1:    // glut's middle mouse button
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::MiddleButton);
+
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::MiddleButton);
                break;
+
                break;
            case 2:    // glut's right mouse button
+
            case 2:    // glut's right mouse button
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton);
+
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton);
                break;
+
                break;
            }
+
            }
        }
+
        }
 
     }
 
     }
 
  
  
Line 697: Line 697:
  
 
  void myPassiveMotionFunc(int x, int y)
 
  void myPassiveMotionFunc(int x, int y)
    {
+
    {
    CEGUI::System::getSingleton().injectMousePosition(x,y);
+
    CEGUI::System::getSingleton().injectMousePosition(x,y);
    }
+
    }
  
 
  void myMotionFunc(int x, int y)
 
  void myMotionFunc(int x, int y)
    {
+
    {
    CEGUI::System::getSingleton().injectMousePosition(x,y);
+
    CEGUI::System::getSingleton().injectMousePosition(x,y);
    }
+
    }
  
==Registering the Callbacks==
+
== Registering the Callbacks ==
  
 
In the initialization code of your GLUT program you'll want to add:
 
In the initialization code of your GLUT program you'll want to add:
Line 720: Line 720:
  
  
==Creating a Renderer==
+
== Creating a Renderer ==
  
Naturally, GLUT will use the OpenGLRenderer. So in your CEGUI initialization
+
Naturally, GLUT will use the OpenGLRenderer. So in your CEGUI initialization
 
you'll want:
 
you'll want:
  
 
  CEGUI::OpenGLRenderer* myRenderer = new CEGUI::OpenGLRenderer(0);
 
  CEGUI::OpenGLRenderer* myRenderer = new CEGUI::OpenGLRenderer(0);
 +
new CEGUI::System(myRenderer);
  
 
+
== The New Loop ==
==The New Loop==
+
  
 
  while( keep_running )
 
  while( keep_running )
Line 738: Line 738:
 
     }
 
     }
  
==Makefile for GLUT + CEGUI==
+
== Makefile for GLUT + CEGUI ==
  
 
  # Makefile
 
  # Makefile
Line 754: Line 754:
  
  
==License for the Code Samples==
+
== License for the Code Samples ==
  
 
Copyright (C) 2006 Jim Storch
 
Copyright (C) 2006 Jim Storch
  
 
This software is provided 'as-is', without any express or implied
 
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
+
warranty. In no event will the authors be held liable for any damages
 
arising from the use of this software.
 
arising from the use of this software.
  
 
Permission is granted to anyone to use this software for any purpose,
 
Permission is granted to anyone to use this software for any purpose,
 
including commercial applications, and to alter it and redistribute it
 
including commercial applications, and to alter it and redistribute it
freely. Attribution is not required.
+
freely. Attribution is not required.
 +
 
 +
[[Category:Tutorials]]

Latest revision as of 16:20, 26 February 2011

Written for CEGUI 0.6


Works with versions 0.6.x (obsolete)

GLUT is the OpenGL Utility Toolkit. It was designed to make it easy to create simple OpenGL application across different platforms. One reason to use GLUT is you may want a basic life support system while you design your CEGUI code. GLUT lets you get up and running with minimal code and dependencies.

On the other hand, GLUT has a wonky input system that you may find inadequate, especially if your GUI needs exotic keypress combinations or unicode support.

The information presented here is the result of a couple days of tinkering with CEGUI and FreeGLUT on Linux. Let me clearly state that I am an not an expert on CEGUI, GLUT, C++, or Linux and the things I say may not be the optimum approaches. This information is presented with the simple hope that some may find it useful.


The Loop

We're going to specify FreeGLUT instead of GLUT because we need one of the new functions added. In GLUT you call glutMainLoop() and it never comes back. FreeGLUT gives us glutMainLoopEvent which invokes one loop and then returns control. We need this arrangement so we can call CEGUI's renderer later.

This is a simple teapot drawing app which should compile and run provided you have an OpenGL capable display and freeglut + freeglut-devel packages installed.

Note the while(keep_running){} loop.

//glut_loop.cpp
//
#include <GL/freeglut.h>
//
void render(void);
void keyFunc(unsigned char, int, int);
int window_id;
bool keep_running = true;
//
// A barebones GLUT application
//
int main()
    {
    // Create our OpenGL Window
    int fake_argc = 1;
    char* fake_argv;
    glutInit(&fake_argc, &fake_argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize(640, 480);
    window_id = glutCreateWindow("GLUT Loop");
    // Set the function to handle normal key presses
    glutKeyboardFunc(keyFunc);
    // Begin the loop
    while(keep_running)
        {
        glutMainLoopEvent();
        render();
        glutSwapBuffers();
        }
    // Exit gracefully
    glutDestroyWindow(window_id);
    return 0;
    }
//
// This is where you'd draw a frame of your 3D application
//
void render()
    {
    glLoadIdentity();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    // Look on my works, ye Mighty, and despair!
    glutWireTeapot( 0.5 );
    }
//
//  Handler for normal keypressed
//
void keyFunc(unsigned char key, int x, int y)
    {
    switch (key)
        {
        case 113:   // 'q',
        case 81:    // 'Q',
        case 27:    // 'ESC'
           keep_running = false;
           break;
        }
    }

Makefile for GLUT Only

Remember, 'make' expects tabs not spaces when indenting compiler commands.

# Makefile to compile glut_loop
glut_loop: glut_loop.o
    g++ glut_loop.o -lglut -o glut_loop
#
glut_loop.o: glut_loop.cpp
    g++ -c glut_loop.cpp
#
.PHONY: clean
clean:
    rm -rf *.o glut_loop

GLUT's Keyboard Callbacks

A note on GLUT's keyboard functions:

Keys that don't generate an ASCII value or aren't in the short list for glutSpecialFunc generate ... nothing. There are no events triggered when pressing the CTRL, ALT, or Shift keys. There is no way to tell if '5' was pressed on the keypad instead of the number keys. Later, I will show you a way to fake CTRL, ALT, and Shift for CEGUI but it wont be in real time.

Despite this, there should be enough utility in them to support a GUI. You can enter text, tab, arrow key about, etc. But if your app needs RIGHT-CRTL for 'fire missles' and Keypad-Slash for 'shields' you'll want another input library.

These are the Set-Handler and the What-a-Handler-Should-Look-Like functions:


void glutKeyboardFunc(myKeyboardFunc)


Set the handler for a normal keypress. These would be keys that generate normal ASCII characters. 'Delete' (ASCII 127) is a normal key. 'Insert' is not. The special keys are handled by glutSpecialFunc (see below).

Example Handler:

  • void myKeyboardFunc(unsigned char key, int x, int y);

You get the ASCII value of the key pressed and the current mouse x and y.


void glutKeyboardUpFunc(myKeyboardUpFunc)


Set the handler for a normal key release.

Example Handler:

  • void myKeyboardUpFunc(unsigned char key, int x, int y)

You get the ASCII value of the key released and the current mouse x and y.


glutSpecialFunc(mySpecialFunc)


Set the handler for a special keypress.

Example Handler:

  • void mySpecialFunc(int key, int x, int y)

You get an int with the special key value and the current mouse x and y. The possible values are:

  • GLUT_KEY_F1 through GLUT_KEY_F12
  • GLUT_KEY_UP, GLUT_KEY_RIGHT, and GLUT_KEY_DOWN, and GLUT_KEY_LEFT
  • GLUT_KEY_PAGE_UP and GLUT_KEY_PAGE_DOWN,
  • GLUT_KEY_HOME, GLUT_KEY_END, and GLUT_KEY_INSERT


glutSpecialUpFunc(mySpecialupFunc)


Sets the handler for a special key release.

Example Handler:

  • void mySpecialUpFunc(int key, int x, int y)

You get an int with the special key value and the current mouse x and y.

Connecting GLUT's Keyboard Callbacks to CEGUI

As you probably know, CEGUI does not capture any input. You need to feed it in it with the various .inject_Something_() functions.


Faking CTRL, ALT, and Shift


As mentioned, GLUT does not generate an event when the user presses or releases these keys. If does have a function called glutGetModifiers() that lets you poll their state. It returns an INT with certain bits set.

First we need to add some global flags to our program:

// Track the status of modifiers
bool ctrl_held = false;
bool alt_held = false;
bool shift_held = false;

Then a function to track the state of these keys:

void fakeCtrlAltShift()
   {
   int status = glutGetModifiers();
   if (status & GLUT_ACTIVE_CTRL)
       {
       if (!ctrl_held)
           {
           ctrl_held = true;
           CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::LeftControl);
           }
       }
   else
       {
       if (ctrl_held)
           {
           ctrl_held = false;
           CEGUI::System::getSingleton().injectKeyUp(CEGUI::Key::LeftControl);
           }
       }
   //
   if (status & GLUT_ACTIVE_ALT)
       {
       if (!alt_held)
           {
           alt_held = true;
           CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::LeftAlt);
           }
       }
   else
       {
       if (alt_held)
           {
           alt_held = false;
           CEGUI::System::getSingleton().injectKeyUp(CEGUI::Key::LeftAlt);
           }
       }
   //
   if (status & GLUT_ACTIVE_SHIFT)
       {
       if (!shift_held)
           {
           shift_held = true;
           CEGUI::System::getSingleton().injectKeyDown(CEGUI::Key::LeftShift);
           }
       }
   else
       {
       if (shift_held)
           {
           shift_held = false;
           CEGUI::System::getSingleton().injectKeyUp(CEGUI::Key::LeftShift);
           }
       }
   }

We'll call this in every keyboard handler. It's not pretty but it should keep CEGUI informed.


Scan Codes


For key presses, CEGUI wants to be told the ASCII value of the key pressed and the system scan code generated. For key releases, CEGUI wants the scan code only. Our problem is GLUT has no idea what a scan code is.

Here's two functions to create some fake scan codes, one for normal input and one for the special keys:

// Map ASCII to Scan Codes
int mapNormal(unsigned char c)
   {
   int scancode;
   switch(c)
       {
       case 97:
       case 65:
           scancode = CEGUI::Key::A;
           break;
       case 98:
       case 66:
           scancode = CEGUI::Key::B;
           break;
       case 99:
       case 67:
           scancode = CEGUI::Key::C;
           break;
       case 100:
       case 68:
           scancode = CEGUI::Key::D;
           break;
       case 101:
       case 69:
           scancode = CEGUI::Key::E;
           break;
       case 102:
       case 70:
           scancode = CEGUI::Key::F;
           break;
       case 103:
       case 71:
           scancode = CEGUI::Key::G;
           break;
       case 104:
       case 72:
           scancode = CEGUI::Key::H;
           break;
       case 105:
       case 73:
           scancode = CEGUI::Key::I;
           break;
       case 106:
       case 74:
           scancode = CEGUI::Key::J;
           break;
       case 107:
       case 75:
           scancode = CEGUI::Key::K;
           break;
       case 108:
       case 76:
           scancode = CEGUI::Key::L;
           break;
       case 109:
       case 77:
           scancode = CEGUI::Key::M;
           break;
       case 110:
       case 78:
           scancode = CEGUI::Key::N;
           break;
       case 111:
       case 79:
           scancode = CEGUI::Key::O;
           break;
       case 112:
       case 80:
           scancode = CEGUI::Key::P;
           break;
       case 113:
       case 81:
           scancode = CEGUI::Key::Q;
           break;
       case 114:
       case 82:
           scancode = CEGUI::Key::R;
           break;
       case 115:
       case 83:
           scancode = CEGUI::Key::S;
           break;
       case 116:
       case 84:
           scancode = CEGUI::Key::T;
           break;
       case 117:
       case 85:
           scancode = CEGUI::Key::U;
           break;
       case 118:
       case 86:
           scancode = CEGUI::Key::V;
           break;
       case 119:
       case 87:
           scancode = CEGUI::Key::W;
           break;
       case 120:
       case 88:
           scancode = CEGUI::Key::X;
           break;
       case 121:
       case 89:
           scancode = CEGUI::Key::Y;
           break;
       case 122:
       case 90:
           scancode = CEGUI::Key::Z;
           break;
       case 27:
           scancode = CEGUI::Key::Escape;
           break;
       case 49:
           scancode = CEGUI::Key::One;
           break;
       case 50:
           scancode = CEGUI::Key::Two;
           break;
       case 51:
           scancode = CEGUI::Key::Three;
           break;
       case 52:
           scancode = CEGUI::Key::Four;
           break;
       case 53:
           scancode = CEGUI::Key::Five;
           break;
       case 54:
           scancode = CEGUI::Key::Six;
           break;
       case 55:
           scancode = CEGUI::Key::Seven;
           break;
       case 56:
           scancode = CEGUI::Key::Eight;
           break;
       case 57:
           scancode = CEGUI::Key::Nine;
           break;
       case 48:
           scancode = CEGUI::Key::Zero;
           break;
       case 45:
           scancode = CEGUI::Key::Minus;
           break;
       case 61:
           scancode = CEGUI::Key::Equals;
           break;
       case 8:
           scancode = CEGUI::Key::Backspace;
           break;
       case 9:
           scancode = CEGUI::Key::Tab;
           break;
       case 91:
           scancode = CEGUI::Key::LeftBracket;
           break;
       case 93:
           scancode = CEGUI::Key::RightBracket;
           break;
       case 13:
           scancode = CEGUI::Key::Return;
           break;
       case 59:
           scancode = CEGUI::Key::Semicolon;
           break;
       case 39:
           scancode = CEGUI::Key::Apostrophe;
           break;
       case 96:
           scancode = CEGUI::Key::Grave;
           break;
       case 92:
           scancode = CEGUI::Key::Backslash;
           break;
       case 44:
           scancode = CEGUI::Key::Comma;
           break;
       case 46:
           scancode = CEGUI::Key::Period;
           break;
       case 47:
           scancode = CEGUI::Key::Slash;
           break;
       case 42:
           scancode = CEGUI::Key::Multiply;
           break;
       case 32:
           scancode = CEGUI::Key::Space;
           break;
        case 64:
           scancode = CEGUI::Key::At;
           break;
       case 58:
           scancode = CEGUI::Key::Colon;
           break;
       case 95:
           scancode = CEGUI::Key::Underline;
           break;
       case 127:
           scancode = CEGUI::Key::Delete;
           break;
       default:
           scancode = 0;
       }
   return scancode;
   }


// Map Special GLUT Keys to Scan Codes
int mapSpecial(int c)
   {
   int scancode;
   switch(c)
       {
       case 1:
           scancode = CEGUI::Key::F1;
           break;
       case 2:
           scancode = CEGUI::Key::F2;
           break;
       case 3:
           scancode = CEGUI::Key::F3;
           break;
       case 4:
           scancode = CEGUI::Key::F4;
           break;
       case 5:
           scancode = CEGUI::Key::F5;
           break;
       case 6:
           scancode = CEGUI::Key::F6;
           break;
       case 7:
           scancode = CEGUI::Key::F7;
           break;
       case 8:
           scancode = CEGUI::Key::F8;
           break;
       case 9:
           scancode = CEGUI::Key::F9;
           break;
       case 10:
           scancode = CEGUI::Key::F10;
           break;
        case 11:
           scancode = CEGUI::Key::F11;
           break;
       case 12:
           scancode = CEGUI::Key::F12;
           break;
       case 104:
           scancode = CEGUI::Key::PageUp;
           break;
       case 105:
           scancode = CEGUI::Key::PageDown;
           break;
       case 106:
           scancode = CEGUI::Key::Home;
           break;
       case 107:
           scancode = CEGUI::Key::End;
           break;
       case 100:
           scancode = CEGUI::Key::ArrowLeft;
           break;
       case 101:
           scancode = CEGUI::Key::ArrowUp;
           break;
       case 102:
           scancode = CEGUI::Key::ArrowRight;
           break;
       case 103:
           scancode = CEGUI::Key::ArrowDown;
           break;
       case 108:
           scancode = CEGUI::Key::Insert;
           break;
       default:
           scancode = 0;
       }
   return scancode;
   }


Normal Keypresses


void myKeyboardFunc(unsigned char key, int x, int y)
   {
   fakeCtrlAltShift();
   CEGUI::System::getSingleton().injectChar(key);
   int scancode = mapNormal(key);
   if (scancode)
       {
       CEGUI::System::getSingleton().injectKeyUp(scancode);
       }
   }
void myKeyboardUpFunc(unsigned char key, int x, int y)
   {
   fakeCtrlAltShift();
   int scancode = mapNormal(key);
   if (scancode)
       {
       CEGUI::System::getSingleton().injectKeyUp(scancode);
       }
   }


Special Keypresses


void mySpecialFunc(int key, int x, int y)
   {
   fakeCtrlAltShift();
   int scancode = mapSpecial(key);
   if (scancode)
       {
       CEGUI::System::getSingleton().injectKeyDown(scancode);
       }
   }
void mySpecialUpFunc(int key, int x, int y)
   {
   fakeCtrlAltShift();
   int scancode = mapSpecial(key);
   if (scancode)
       {
       CEGUI::System::getSingleton().injectKeyUp(scancode);
       }
   }


GLUT's Mouse Callbacks

glutMouseFunc(myMouseFunc)


Sets the handler for any mouse button function. This includes presses and releases. Scrolling a mouse wheels generates a press and a release for each tick.

Example Handler:

  • void myMouseFunc(int button, int state, int x, int y)

The button numbering looks like this (depending on your mouse):

  • 0 = Left
  • 1 = Middle
  • 2 = Right
  • 3 = MouseWheel Up
  • 4 = MouseWheel Down

You may notice that the middle and right buttons are swapped from what Windows programmers would expect.

If state == 0, the event is a button press. If state == 1, the event is a button release. X and Y are the current mouse co-ordinates.


glutPassiveMotionFunc(myPassiveMotionFunc)


Set the handler for mouse movement with NO buttons held -- ie not dragging.

Example Handler:

  • void myPassiveMotionFunc(int x, int y)

Pretty simple, x == x, y == y. You will not receive callbacks when the mouse pointer leaves the GLUT window.


glutMotionFunc(myMotionFunc)


Sets the handler for mouse movent with ANY button held -- ie dragging.

Example Handler:

  • void myMotionFunc(int x, int y)

Again x==x, y==y. The behavoir is a little different in that you will get callbacks with a button held and the mouse point dragged out of the GLUT window.

For more exotic callbacks, please see: http://www.opengl.org/resources/libraries/glut/spec3/node45.html


Connecting GLUT's Mouse Callbacks to CEGUI

Handling Buttons


void myMouseFunc(int button, int state, int x, int y)
    {
    if (state == 0)     // State 0 = Button Pressed
        {
        switch (button)
            {
            case 0:     // glut's left mouse button
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton);
                break;
            case 1:     // glut's middle mouse button
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::MiddleButton);
                break;
            case 2:     // glut's right mouse button
                CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton);
                break;
            case 3:     // glut's mouse wheel up
                CEGUI::System::getSingleton().injectMouseWheelChange(2.0);
                break;
            case 4:     // glut's mouse wheen down
                CEGUI::System::getSingleton().injectMouseWheelChange(-2.0);
                break;
            }
        }
    else if (state == 1)    // State 1 = Button Released
        {
        switch (button)
            {
            case 0:     // glut's left mouse button
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton);
                break;
            case 1:     // glut's middle mouse button
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::MiddleButton);
                break;
            case 2:     // glut's right mouse button
                CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton);
                break;
            }
        }
    }


Handling Movement


You need both of these, since GLUT uses separate callbacks for normal movement and dragging (or register one function to both callbacks):

void myPassiveMotionFunc(int x, int y)
    {
    CEGUI::System::getSingleton().injectMousePosition(x,y);
    }
void myMotionFunc(int x, int y)
    {
    CEGUI::System::getSingleton().injectMousePosition(x,y);
    }

Registering the Callbacks

In the initialization code of your GLUT program you'll want to add:

// Set some input handlers
glutKeyboardFunc(myKeyboardFunc);              // key pressed
glutKeyboardUpFunc(myKeyboardUpFunc);          // key released
glutSpecialFunc(mySpecialFunc);                // special key pressed
glutSpecialUpFunc(mySpecialUpFunc);            // special key released
glutMouseFunc(myMouseFunc);                    // any mouse button press or release
glutPassiveMotionFunc(myPassiveMotionFunc);    // mouse movement with no buttons held
glutMotionFunc(myMotionFunc);                  // mouse movement with any button held


Creating a Renderer

Naturally, GLUT will use the OpenGLRenderer. So in your CEGUI initialization you'll want:

CEGUI::OpenGLRenderer* myRenderer = new CEGUI::OpenGLRenderer(0);
new CEGUI::System(myRenderer);

The New Loop

while( keep_running )
    {
    glutMainLoopEvent();
    render();
    CEGUI::System::getSingleton().renderGUI();
    glutSwapBuffers();
    }

Makefile for GLUT + CEGUI

# Makefile
# compile gui_loop
#
gui_loop: gui_loop.o
   g++ gui_loop.o -lglut -L/usr/local/lib -lCEGUIBase -lCEGUIOpenGLRenderer -o gui_loop
#
gui_loop.o: gui_loop.cpp
   g++ -c gui_loop.cpp -I/usr/local/include/CEGUI
#
.PHONY: clean
clean:
   rm -rf *.o gui_loop


License for the Code Samples

Copyright (C) 2006 Jim Storch

This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely. Attribution is not required.