Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / OpenGL

GLUT Subwindow Template

4.29/5 (11 votes)
13 Oct 2015CPOL10 min read 1   2.2K  
This article describes in detail how to set up your first OpenGL FreeGLUT window with multiple subwindows, and provides you with a template for your OpenGL applications.

Visual C++ 2015 Files

Visual C++ 6 Files (Old)

Screenshot - GLUT_Subwindow_Template.jpg

Contents

Introduction

This article describes in detail how to set up your first OpenGL GLUT window with multiple sub-windows, and provides you with a template for your OpenGL applications. It can be used in the following ways:

  • Understand GLUT sub-windows
  • Learn new GLUT event handlers
  • Use the code as a template if you ever needed a Graphics program with multiple sub-windows

For information about what is OpenGL, what is GLUT, and how to set up a GLUT Window, check the GLUT Window Template article.

Why Have a GLUT Subwindow Template?

When writing graphical applications, programmers sometimes need multiple subwindows as a way of organizing their graphical contents on the window. Subwindows allow us to divide the main window into many regions, with each region having its own OpenGL context and callback functions. This means that we can have different drawings and event handling at every region.

To avoid having to write the same code every time you want to create an OpenGL graphical application that requires multiple subwindows, this program code can be used as a template to get you directly started.

The OpenGL GLUT subwindow template has the following properties:

  • Window Title: "GLUT Subwindow Template"
  • Window Background Color: Black (R = 0, G = 0, B = 0)
  • Subwindow 1 Background Color: Blue
  • Subwindow 2 Background Color: Yellow
  • Subwindow 1 Foreground Color: Yellow
  • Subwindow 2 Foreground Color: Blue
  • Window Dimensions: width = 256 + GAP * 2, height = 256 + 64 + GAP * 3 where GAP is the gap between the two subwindows in the main window. 256 is the width of both subwindows. 256 is the height of the first subwindow and 64 is the height of the second subwindow. The subwindows are placed vertically on the main window.
  • Subwindow 1 Dimensions: width = 256, height = 256
  • Subwindow 2 Dimensions: width = 256, height = 64
  • Window Position: x = (Screen Width - Window Width) / 2, y = (Screen Height - Window Height) / 2. This would mean that the window is centered on the screen
  • Subwindow 1 Position (relative to main window): x = GAP, y = GAP
  • Subwindow 2 Position (relative to main window): x = GAP, y = GAP + Subwindow 1 Height + GAP
  • Handling of keyboard, mouse, and display events for window, subwindow1 and subwindow2.
  • Showing when and where events occur and their meaning through the command prompt.

The image below gives an idea about how the subwindows would be laid out on the main window:

Screenshot - GLUT_Subwindow_Template_1.jpg

Usage

Compiling & Running the Program

For information on how to compile and run the program, please check the Usage section in the GLUT Window Template article.

Using the Code

The source code is intended to be used as a template for your OpenGL applications. To use it in your new application, you can simply rename the C file and add it to your Visual Studio project.

Explaining the Code

Creating Window

The following are the variables relating to the window id, position, dimensions and title. The GAP constant represents the GAP between the subwindows placed on this window.

C++
// gap between subwindows
#define GAP  25
    
//  define the window position on screen
float main_window_x;
float main_window_y;
    
//  variables representing the window size
float main_window_w = 256 + GAP * 2;
float main_window_h = 256 + 64 + GAP * 3;
    
//  variable representing the window title
char *window_title = "SubWindow Template";
    
//  Represents the window id
int main_window;

After setting the variables, the window is created as shown below. Notice how the window id is returned from the glutCreateWindow function. This window id main_window will be used to reference the window and place multiple sub windows inside it.

C++
//  Set the main window x and y coordinates such that the 
//  window becomes centered
centerOnScreen ();

//  Set Window size 
glutInitWindowSize (main_window_w, main_window_h);  

//  Set window position
glutInitWindowPosition (main_window_x, main_window_y);  

//  Set window display mode
glutInitDisplayMode (GLUT_RGB | GLUT_DOUBLE);  

//  Create window
main_window = glutCreateWindow (window_title);

Creating Subwindows

After initializing and creating the window, we need to initialize the subwindows and place them on the window. We can place as many subwindows as we want. The window becomes the parent of its subwindows. A subwindow can also be the parent of other subwindow(s), and thus subwindows could be nested arbitrarily deep.

Below are the variables relating to subwindow 1 position relative to the main window, subwindow 1 dimensions and its id.

C++
//  define the window position on screen
float subwindow1_x = GAP;
float subwindow1_y = GAP;

//  variables representing the window size
float subwindow1_w = 256;
float subwindow1_h = 256;

//  Represents the subwindow id
int subwindow_1;

In order to create a subwindow, the glutCreateSubwindow function is used, and it has the following signature:

C++
int glutCreateSubWindow(int win, int x, int y, int width, int height);

 

Parameter Description
win Identifier of the subwindow's parent window.
x Window X location in pixels relative to parent window's origin.
y Window Y location in pixels relative to parent window's origin.
width Width in pixels.
height Height in pixels.

The first subwindow is created with the specified position and dimensions as follows. Notice that the main_window id must be passed as a parameter so that the subwindow can know its parent. After the glutCreateSubwindow function is called, the subwindow 1 will become the current window.

C++
//  Create subwindow 1
subwindow_1 = glutCreateSubWindow (main_window, GAP, GAP, subwindow1_w, 
    subwindow1_h);

Destroying Subwindows

When a subwindow is no longer needed, it can be destroyed as follows:

C++
void glutDestroyWindow(int win);

where win is the identifier of GLUT window to destroy. If win was the current window, the current window becomes invalid.

Positioning & Resizing Subwindows

After specifying how to create and destroy a subwindow, we shall see next how to position and resize subwindows dynamically. However, before we can position or resize a subwindow, we need to set it as the current window. In order to do so, we must have its id and we must use the glutSetWindow function to set it as the current window.

Here is how we can position and resize subwindow 1:

C++
glutSetWindow (subwindow_1);
glutPositionWindow (subwindow1_x, subwindow1_y);
glutReshapeWindow (subwindow1_w, subwindow1_h);

Below is the description of each of these functions.

glutSetWindow

glutSetWindow sets the current window.

C++
void glutSetWindow(int win);

where win is the identifier of the GLUT window (or subwindow) that is to be set as the current window.

glutGetWindow

If we want to know which is the current window, we can use this function.

C++
int glutGetWindow(void);

If no windows exist or the previously current window was destroyed, glutGetWindow returns zero.

glutPositionWindow

glutPositionWindow requests a change to the position of the current window. For top-level windows, the x and y parameters are pixel offsets from the screen origin. For subwindows, the x and y parameters are pixel offsets from the window's parent window origin.

C++
void glutPositionWindow(int x, int y);

where x is the new X location of the window (or subwindow) in pixels, and y is the new Y location of the window in pixels.

glutPositionWindow disables the full screen status of a window if previously enabled.

glutReshapeWindow

glutReshapeWindow requests a change to the size of the current window.

C++
void glutReshapeWindow(int width, int height);

where width is the new width of window in pixels and height is the new height of window in pixels.

When a window is reshaped, the reshaped dimensions are reported to the program by a reshape callback. glutReshapeWindow disables the full screen status of a window if previously enabled.

Callback Functions

The main window and each of the two subwindows will be assigned to the following callback functions.

  • Display Function: Called whenever the OpenGL context needs to be redisplayed.
  • Reshape Function: Called whenever the sub\window is resized.
  • Mouse Function: Called whenever the mouse is clicked, pressed, or released.
  • Motion Function: Called whenever the mouse is dragged.
  • Passive Motion Function: Called whenever the mouse is moved.
  • Keyboard Function: Called whenever an ASCII-Code key is pressed.
  • Special Function: Called whenever a non-ASCII-Code key is pressed.

For example, subwindow 2 callback functions will be assigned as follows:

C++
glutDisplayFunc (subwindow2_display);
glutReshapeFunc  (subwindow2_reshape);
glutMouseFunc (subwindow2_mouse);
glutMotionFunc (subwindow2_motion);
glutPassiveMotionFunc (subwindow2_pmotion);
glutKeyboardFunc (subwindow2_keyboard);
glutSpecialFunc (subwindow2_special);

In addition to the above callback functions, the main window will be assigned to the entry function, which is called whenever the mouse enters or leaves the window.

For a complete specification of the callback functions and how they are used in my OpenGL program, please check the Callback Functions section of the GLUT Window Template article. The reshape and entry functions will be described here.

glutReshapeFunc

The below function is called when the main window is reshaped.

C++
//-------------------------------------------------------------------------
//  Main Window Reshape Function.
//
//  Reset the position and dimensions of subwindows when main window size
//  changes.
//-------------------------------------------------------------------------

void main_reshape (int width, int height) 
{
    //  Notify that we are reshaping the main window
    printf ("Main Window: ");
    
    //  Just take the case when the user tries
    //  to make the size of the window very small...
    if (width < GAP * 4 || height < GAP * 6)
    {
        glutSetWindow (main_window);
        glutReshapeWindow (main_window_w, main_window_h);
        return;
    }
    //  Change the subwindow 1 dimensions as window dimensions change
    //  main_window_w          ---> subwindow1_w
    //  main_window_w' (width) ---> ??
    //  ==> 
    subwindow1_w = (subwindow1_w * (width-GAP*2.0))/(main_window_w-GAP*2.0);
    subwindow1_h = (subwindow1_h * (height-GAP*3.0))/(main_window_h-GAP*3.0);

    //  Set subwindow 1 as current window and then reposition and resize it
    glutSetWindow (subwindow_1);
    glutPositionWindow (GAP, GAP);
    glutReshapeWindow (subwindow1_w, subwindow1_h);
    
    //  Change the subwindow 2 dimensions as window dimensions change
    subwindow2_w = (subwindow2_w * (width-GAP*2.0))/(main_window_w-GAP*2.0);
    subwindow2_h = (subwindow2_h * (height-GAP*3.0))/(main_window_h-GAP*3.0);

    //  Set subwindow 2 as current window and then reposition and resize it
    glutSetWindow (subwindow_2);
    glutPositionWindow (GAP, GAP+subwindow1_h+GAP);
    glutReshapeWindow (subwindow2_w, subwindow2_h);

    //  Stay updated with the window width and height
    main_window_w = width;
    main_window_h = height;

    //  Print current width and height on the screen
    printf ("Width: %d, Height: %d.\n", width, height);
}

When the size of the main window is changed, the sizes and relative positions of the subwindows to the main window will be changed accordingly.

Knowing that the GAP is constant, the subwindows will resized in proportion to the main window size, but without taking the GAP into consideration. The calculation of the new subwindow dimensions will be done as follows:

  1. width is the new width of the main window and main_window_w is the old width of the main window.
  2. Discard horizontal gaps. Knowing that there are two gaps horizontally, we need to deduct GAP*2 from the old and new main window widths.
  3. New Subwindow Width / Old Subwindow Width = (New Window Width - Total Horizontal Gap) / (Old Window Width - Total Horizontal Gap).

    Knowing that Old Subwindow Width = subwindow_w and Old Window Width = main_window_w and New window Width = width, the New Subwindow Width will be equal to:

    (width - GAP*2) / (main_window_w - GAP*2) * subwindow_w.

The same calculation would be done for the height.

Since we are deducting the horizontal and vertical gaps in our calculation, we need to make sure that the size of the main window doesn't get too small in order to avoid changing our subwindow size to zero.

C++
if (width < GAP * 4 || height < GAP * 6)
{
    glutSetWindow (main_window);
    glutReshapeWindow (main_window_w, main_window_h);
    return;
}

After the main window gets reshaped, the subwindows laid inside it get automatically reshaped (as seen in the main_reshape function) and their reshape callback functions are called. Knowing that we are drawing a teapot inside each of the subwindows, we care to keep their width to height ratio constant (keep aspect ratio constant) so that the teapot shape doesn't get distorted. This would be done by making sure that the viewport width and height remain equal. The viewport is the rectangular region of the window (or subwindow) where the image is drawn.

Below is the code of the subwindow 1 reshape function:

C++
//-------------------------------------------------------------------------
//  SubWindow 1 Reshape Function.
//
//  Preserve aspect ratio of viewport when subwindow is resized.
//-------------------------------------------------------------------------

void subwindow1_reshape (int width, int height) 
{
    //  Represents a side of the viewport. A viewport is intended to
    //  to take a square shape so that the aspect ratio is reserved
    int viewport_side = 0;
    
    //  Viewport x and y positions 
    int viewport_x = 0, viewport_y = 0;
    
    //  Calculate viewport side
    viewport_side = (width > height) ? height : width;
    
    //  Calculate viewport position (Center viewport)
    viewport_x = (width - viewport_side) / 2;
    viewport_y = (height - viewport_side) / 2;
    
    //  Preserve aspect ratio
    glViewport (viewport_x, viewport_y, viewport_side, viewport_side);
    
    //  Set subwindow width and height
    subwindow1_w = width;
    subwindow1_h = height;
    
    //  Notify that we are reshaping subwindow 1
    printf ("Subwindow 1: ");
    
    //  Print current width and height
    printf ("Width: %d, Height: %d, Viewport Side: %d.\n", width, 
        height, viewport_side);
}

Notice that the viewport is centered in the subwindow and its width and height are set to be equal to the minimum between the width and height of the subwindow.

glViewport

 

void glViewport (int x, int y, int width, int height);

The x and y parameters specify the lower-left corner of the viewport, and width and height are the size of the viewport rectangle in pixels. By default, the initial viewport values are (0, 0, winWidth, winHeight), where winWidth and winHeight are the dimensions of the window.

In case we don't call the glViewport function in the reshape function of subwindow 2, the viewport will take the whole size of the window, and thus the teapot will be distorted as shown below:

glut_subwindow/glut_subwindow_template_2.jpg

glutEntryFunc

The entry function is called whenever the mouse enters or leaves a window. The state parameter of the entry function is either GLUT_LEFT or GLUT_ENTERED depending on whether the mouse pointer has entered or left the window. Passing NULL to glutEntryFunc disables the generation of the mouse enter/leave callback.

C++
//-------------------------------------------------------------------------
//  Main Window Entry Function.
//
//    This function is called whenever the mouse pointer enters or leaves
//  the main window.
//-------------------------------------------------------------------------
void main_entry (int state)
{
    if (state == GLUT_ENTERED)
        printf ("Mouse entered main window...\n");
    else if (state == GLUT_LEFT)
        printf ("Mouse left main window...\n");
}

Trying to register the entry function with subwindows will not cause a compilation, linkage, or runtime error. However, on Windows OS, no event is generated when the subwindow is entered or left.

Note

The mouse passive motion events will not be logged so that we can clearly see other events on the command prompt log.

Conclusion

The purpose of this article is to teach you how to set up a GLUT window with multiple subwindows, along with event handling. At the same time, the template can be used to save lots of copy and paste from old projects or the Internet.

In case you find this template useful or have any suggestions, please let me know.

References

Revision History

10/13/2015

  • Added Visual Studio 2015 Project for the Free GLUT Subwindow Template

08/20/2007

  • Deleted and reposted at author's request.

02/07/2007

  • Modified article to meet criteria specified in "A Guide To Writing Articles For Code Project" by Marc Clifton
  • Added most of callback functions to the main window and the two subwindows
  • Logged window and subwindow events to the command prompt

21/07/2005

  • Original article posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)