-->
Lesson 4 Startup Options
Up until now we have been using the default startup options for glut. In this
lesson we will learn how to use our MFC dialog to set initial glut options.
You can continue with the lesson 1 project you've already created and modified
in the subsequent lessons. For continuity and to allow you to download the
lessons as separate projects I will be showing the new code in a new project
called Lesson4.
If you didn't do lesson 3 you can get it
here
Click on the resources tab and expand the project tree, and then expand the
Dialog folder. double click on "IDD_LESSON4_DIALOG". You should now see this:

From your toolbox select Group Box.

Place a group box control on your form and change its caption to "Resolution"

Place four radio button controls on your form. Right click on the first radio
button control and choose properties, if the properties sheet is not already
open. Set the first radio button caption to "640 x 480" and set the id to
"IDC_RADIO_640x480".
Set the second one to "800 x 600" with the id "IDC_RADIO_800x600".
Set the third to "1024 x 768" with id "IDC_RADIO_1024x768"
For the last radio button set it to "Full Screen" with id "IDC_RADIO_FULLSCREEN"

When you double click on a control in the resource editor the IDE will
automatically create a mouse click event listener and create a function for you
to put code into. Double click on each of the controls to create a function for
each. Here's what you should get:
The event message map.

The the event listener functions.

Next we need to add a function to MFCopenGL to set the screen resolution. Click
on the class tab and expand the project. Right click on MFCopenGL, choose add
and add function. Create a public member function named "setRes" with a void
return and one parameter named "resType" of type int. We will need someplace to
save the resolution settings so add two private member variables to the
MFCopenGL class of type int called "sizeX" and "sizeY". Add a private member
variable of type bool named "fullscreen".

Open the MFCopenGL default constructor and change the default values for sizeX
and sizeY to 640 and 480 respectively. This will give our glut window a default
size if we choose to not pick a radio button. The default constructor should
look like this:

We need to move our glut window initialization code into the MFCopenGL class.
Click on the class tab and expand the project, right click on MFCopenGL
and choose add add add function. Add a function named glutInit that takes no
parameters and is a void return. expand the MFCopenGL class and double
click on glutInit. Add the following code:
//set out options to RGB, double buffered and depth
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);/
glutInitWindowSize (sizeX, sizeY);// set the initial glut
window size
glutInitWindowPosition (100, 100);// Set the initial glut
window position
glutCreateWindow("MFC Glut Lesson4");//give the glut
window a title
if(fullscreen)//if
true place glut in full screen mode
{
glutFullScreen();
}
Your glutInit function should look like this:

Remove the old glut window initialization code from CLesson4Dlg::OnBnClickedOk()
and add gl.glutInit(); in its place. Your should have this now:

To make it easier to pass the correct parameters into the resType function we
will create a enum with some constant names that are easy to recognize. In order
to have these constants available to all our classes we will place them in the
"stdafx.h" file. Click on the solutions tab, expand the project, expand the
"header files" folder and double click on "stdafx.h". At the end of the file
place:
#ifndef res
typedef enum {RESOLUTION_640X480 = 1,
RESOLUTION_800X600, RESOLUTION_1024X768, RESOLUTION_FULLSCREEN} res;
#endif
We use the "#ifndef" block to prevent multiple declarations of the enum "res".
Your "stdafx.h" should look like this:

Lets add our resolution code to MFCopenGL. Click on the class tab, expand the
project and expand the MFCopenGL class. Double click on setRes(int resType). Add the following code to the setRes
function:
switch(resType) {
case RESOLUTION_640X480 :
sizeX = 640;
sizeY = 480;
fullscreen = false;
break;
case RESOLUTION_800X600 :
sizeX = 800;
sizeY = 600;
fullscreen = false;
break;
case RESOLUTION_1024X768 :
sizeX = 1024;
sizeY = 768;
fullscreen = false;
break;
case RESOLUTION_FULLSCREEN :
fullscreen = true;
break;
default :
MessageBox(NULL, "Invalid setRes value, use int 1-4", "Warning", MB_OK);
break;
}
What we are doing here is receiving an int and checking it against the enum
constant list and looking for a match. When we find an int match we then set the
sizeX and the sizeY. We will use these size variables to set the glut window
size when it is launched. Your resType function should look like this:

We will now add the code which calls this function. Expand the CLesson4Dlg class
and double click on the OnBnClickedRadio640x480() function. You will see the
radio button event handling functions listed there.
Add:
"gl.setRes(RESOLUTION_640X480); " to OnBnClickedRadio640x480()
"gl.setRes(RESOLUTION_800X600);" to OnBnClickedRadio800x600()
"gl.setRes(RESOLUTION_1024X768);" to OnBnClickedRadio1024x768()
"gl.setRes(RESOLUTION_FULLSCREEN);" to OnBnClickedRadioFullscreen()
Your radio button functions should look like this:

Now you can see why we used the enum. It makes the code more readable and there
is no question about what you are asking the function to do.
Compile and run your program and you should see:

Note: Don't test the full screen button yet; it will work but you wont have any
way to get back out of full screen mode and consequently no way to shut down the
program except [ctrl] [alt] [del]. We will add code later to deal with switching
out of full screen mode.
Select the 800 x 600 radio button and hit play.

Now test 640 x 480:

Test 1024 x 768:

In order to allow us to exit from full screen mode we will need to add keyboard
support to our program. Glut has two keyboard callbacks, glutKeyboardFunc and
glutSpecialFunc The glutKeyboardFunc function is for receiving regular
alpha numeric keystrokes, and the glutSpecialFunc function is for capturing
keystrokes from the special keys like the function keys, insert and end. We will
use the escape key to tell the program to shut down. For that we will need
glutKeyboardFunc. I know it seems counterintuitive to have the escape key lumped
into the alphanumeric keys but that's the way the specifications for glut were
written. The glutKeyboardFunc needs a function to hook to so lets add one to our
MFCopenGL. Add void keyboard(unsigned
char key, int x,
int y) to MFCopenGL. The first parameter is the
ANSI code for the key presses. The next two parameters correspond to the (X, Y)
coordinate position of the mouse at the time when the key was pressed. We will
not be using the X, Y information but the parameters still need to be there in
order to match the requirements of the callback. Open up the keyboard function
and add this code:
//hit escape to close program
if (key == 27)
{
exit(0);
// escape key is ascii 27
}
Your keyboard function should look like this:

Now lets define the callback for glut. Add the following code to the top of the
Lesson4Dlg.cpp after the resize callback definition:
void keyboard(unsigned
char key, int x,
int y)
{
gl.keyboard(key, x, y);
}
Your callback definition area should look like this now:

We still have to tell glut about the keyboard callback so in the OnBnClickedOk()
function add "glutKeyboardFunc (keyboard);" after the resize callback settting.
Your OnBnClickedOk() should look like this now:

Compile and run your program. Choose the "Full Screen" button and hit play. Now
hit the escape key and the program should shut down. In fact hitting the escape
key in any of the resolutions will exit the program now.
The next step is to show the correct default radio button pre-selected when the
program launches.
Click on the class tab, expand the project and double click on the CLesson4Dlg
class. After public and just prior to afx_msg void
OnBnClickedOk(); add:
CButton radio640x480;
CButton radio800x600;
CButton radio1024x768;
CButton radioFullScreen;
Your CLesson4Dlg class header should look like this:

In the class tab expand the CLesson4Dlg class and double click on DoDataExchange
and add:
DDX_Control(pDX, IDC_RADIO_640x480, radio640x480);
DDX_Control(pDX, IDC_RADIO_800x600, radio800x600);
DDX_Control(pDX, IDC_RADIO_1024x768, radio1024x768);
DDX_Control(pDX, IDC_RADIO_FULLSCREEN, radioFullScreen);
At the end of the function. Your DoDataExchange should look like this:

This will add a member variable for each radio button and map it to the correct
resource. We can now control the settings of the radio buttons via code.
Double click on OnInitDialog in the class tab. At the end of the function just
prior to the return add:
radio640x480.SetCheck(true);
OnBnClickedRadio640x480();
This will set the radio button to show the black dot and then run the function
associated with that radio button. Your OnInitDialog should look like
this:

Compile your program and you should see this:

Press play and you will see that the glut window starts in the pre-selected
resolution. On your own try setting the pre-select to each of the other radio
buttons in turn to check that you have the variable mapping done correctly.
Next lets add a group box to control the start up glut window position. From the
toolbar select group box and then click on the dialog under the resolution group
box. On the property sheet for the new group box change the caption to
"Position".

Add two radio buttons to the position group box and change their captions to
"Upper Left" and "Center". Set the resource id of Upper Left to
"IDC_RADIO_UPPER_LEFT" and the resource id of center to "IDC_RADIO_CENTER".

Double click on each of these new controls in turn to order to map a function to
the click event of these two new controls.

We need to add variables to our MFCopenGL class to hold the start up window
position and create a function to set them. Click on the class tab and expand
the project. Right click on the MFCopenGL class and choose add variable. Add
three private member variables of type int named posX, posY. and posType. Right
click on MFCopenGL class and choose add and add function. Add a public member
function that returns void named setPos that takes one int parameter named type.
In order to do calculations to find the center of the screen we will need to
find and store the desktop dimensions. Right click on MFCopenGL and choose add
and add variable. Create a private member variable of type RECT named
desktopRec. In order to set this variable when we initialize glut we need to
modify our glutInit member function. Expand the MFCopenGL class and double click
one the glutInit(void) change the function to void
MFCopenGL::glutInit(RECT aRec). Double click on the class name MFCopenGL to open
the header file. Scroll down and change the glutInit function declaration to "void glutInit(RECT aRec);". You class header should
look like this:

In order to make the code more readable we will create another enum. Click on
the solution tab and expand the project. Double click on stdafx.h and add the
following code to the end of the file.
#ifndef pos
typedef enum {POSITION_UPPER_LEFT = 0,
POSITION_CENTER} pos;
#endif
Your stdafx.h should look like this now:

We need to set the default construction now. Click on the class tab expand the
project and expand MFCopenGL. Double click on the default constructor. Add the
following code to the top of the contructor:
//initialized desktopRec to the minimum resolution size
possible
desktopRec.left = 0;
desktopRec.top = 0;
desktopRec.right = 640;
desktopRec.bottom = 480;
Your constructor should look like this:

Next we modify the glutInit function to set the initial window position. Click
on the class tab, expand the project and expand the MFCopenGL class. Double
click on the glutInit member function. add the following code to the top of the
glutInit function:
desktopRec = aRec;
switch(posType) {
case POSITION_UPPER_LEFT :
posX = 0;
posY = 0;
break;
case POSITION_CENTER :
posX = desktopRec.right/2 - sizeX/2;
posY = desktopRec.bottom/2 - sizeY/2;
break;
default :
MessageBox(NULL, "Invalid posType value, use int 0-1", "Warning", MB_OK);
break;
}
Modify the glutInitWindowPosition to read "glutInitWindowPosition (posX,
posY);".
Your glutInit function should look like this:

Now we need to call our new setPos function from our MFC dialog. Click on the
resource tab, expand the project, and expand the dialogs folder. Double click on
IDD_LESSON4_DIALOG. Double click on the Upper Left radio button in the resource
editor. Add "gl.setPos(POSITION_UPPER_LEFT);" to OnBnClickedRadioUpperLeft().
Now add gl.setPos(POSITION_CENTER); to OnBnClickedRadioCenter().

Compile and run and you should see some odd behavior. When we use a group box
all the radio button controls inside will toggle automatically when we click on
one, but it also unchecks the other buttons in the other group box. When we have
more than one group box on a dialog the dialog resource control can have
ambiguous behavior in respect to this toggling. In order to get consistent
behavior we will change our radio buttons so that we toggle them manually from
inside the code. First lets add some variables for our two new radio buttons and
map resources to them. Click on the class tab, expand the project and double
click on the CLesson4Dlg class. In the CLesson4Dlg header add
CButton radioUpperLeft;
CButton radioCenter;
below our other CButton instances.

On the class tab expand the CLesson4Dlg class and double click on DoDataExchange
and add
DDX_Control(pDX, IDC_RADIO_UPPER_LEFT, radioUpperLeft);
DDX_Control(pDX, IDC_RADIO_CENTER, radioCenter);
to the end of the function. Your DoDataExchange should look like this:

Double click on your OnInitDialog and add
radioUpperLeft.SetCheck(true);
OnBnClickedRadioUpperLeft();
just prior to the return. Your OnInitDialog should look like this:

Click on the resources tab, expand the project and expand the dialogs folder.
Double click on IDD_LESSON4_DIALOG to open the resource editor. Highlight each
of the radio buttons in turn and change their auto properties to false. This
will turn off the auto toggle. While your in the properties sheets you can also
set each of the radio buttons tab stop properties to true. Set the 640 x 480
radio button and the Upper Left radio button group property to true. This
will allow navigation through the controls via the tab key and arrow keys once
in a group. For example:

We now need to put the toggle code in our radio button mapped functions. Click
on the class tab expand the project expand the CLesson4Dlg class and double
click on OnBnClickedRadio640x480. Modify you mapped functions to match the
following code:
void CLesson4Dlg::OnBnClickedRadio640x480()
{
// TODO: Add your control notification handler code here
radio640x480.SetCheck(
true);
radio800x600.SetCheck(
false);
radio1024x768.SetCheck(
false);
radioFullScreen.SetCheck(
false);
gl.setRes(RESOLUTION_640X480);
}
void CLesson4Dlg::OnBnClickedRadio800x600()
{
// TODO: Add your control notification handler code here
radio640x480.SetCheck(
false);
radio800x600.SetCheck(
true);
radio1024x768.SetCheck(
false);
radioFullScreen.SetCheck(
false);
gl.setRes(RESOLUTION_800X600);
}
void CLesson4Dlg::OnBnClickedRadio1024x768()
{
// TODO: Add your control notification handler code here
radio640x480.SetCheck(
false);
radio800x600.SetCheck(
false);
radio1024x768.SetCheck(
true);
radioFullScreen.SetCheck(
false);
gl.setRes(RESOLUTION_1024X768);
}
void CLesson4Dlg::OnBnClickedRadioFullscreen()
{
// TODO: Add your control notification handler code here
radio640x480.SetCheck(
false);
radio800x600.SetCheck(
false);
radio1024x768.SetCheck(
false);
radioFullScreen.SetCheck(
true);
gl.setRes(RESOLUTION_FULLSCREEN);
}
void CLesson4Dlg::OnBnClickedRadioUpperLeft()
{
// TODO: Add your control notification handler code here
radioUpperLeft.SetCheck(
true);
radioCenter.SetCheck(
false);
gl.setPos(POSITION_UPPER_LEFT);
}
void CLesson4Dlg::OnBnClickedRadioCenter()
{
// TODO: Add your control notification handler code here
radioUpperLeft.SetCheck(
false);
radioCenter.SetCheck(
true);
gl.setPos(POSITION_CENTER);
}
Compile and run your program and you should be able to toggle the radio buttons
inside of each group box independently of the other group box. Test out the
various radio button options. You should be able to control the position and
size of the glut window.

We have gone through adding two sets of startup option groups. See if you can
add a third group to let the user pick whether the background color of the glut
window will be black or white.
Try it on your own first and then check it against the code I have listed below.
Here is what you need.
1. A group box named "Background Color.
2. Two radio button controls named "Black" and "White". Make their
resource ids, IDC_RADIO_BLACK and IDC_RADIO_WHITE.
3. Set auto to false, tab stop to true and set "Black" group property to true.
4. Add two member variables of type CButton to MFCopenGL named radioBlack and
radioWhite.
5. Map the radioBlack to IDC_RADIO_BLACK and map radioWhite to IDC_RADIO_WHITE
in the DoDataExchange function.
6. Create a class called Color with 4 public member variables of type int named
r, g, b, a.
7. Add a private member variable named backColor of type Color to MFCopenGL.
8. Add a public member function named setBackColor to MFCopenGL that takes one
parameter of type int named color.
9. Add a enum named col to the stdafx.h with constant names COLOR_BLACK AND
COLOR_WHITE.
10. Create a mapped event handling function for the to new radio buttons.
11. Call the setBackColor function from these mapped functions and pass in the
correct constant for the color you want.
12. Add radio button toggling code to the mapped member functions.
12. In the setBackColor create a case statement that checks the color parameter
against the constant list and sets the backColor accordingly.
13. Change the call to glClearColor in the display function so that it uses the
values stored in backColor.
When you are done it should look like this:

The CButton declarations.

Mapping the variables.:

Mapping the functions.

The enum.

The Color header.

Color constructor.

The MFCopenGL header.


The setBackColor with the switch statement.

Calling it from the Dialog.

The modified display function.

We have covered a lot of ground in this lesson. If you got lost in the
middle don't worry it gets easier adding controls with practice.
I have left you some extra space in the Dialog so feel free to add your own
controls.
You can download the source code for this lesson
here.