3. OpenGL Keyboard Interaction (Version 2.0)
Posted on : 25-03-2010 | By : Swiftless | In : OpenGL
Tags: glut, interaction, keyboard, OpenGL, programming, tutorial
18
Introduction
The keyboard is one of those things that is essential to computer gaming, and computer use in general. Could you imagine trying to type with the mouse? Or the number of buttons that would be required on a mouse to allow for movement *and* shooting in a First Person Shooter? The good news is that keyboard interaction, thanks to GLUT, is an extremely easy thing to handle. I am going to look at 2 different GLUT methods for key presses, one for when a key is first pressed, and one for when a key is released. I will then be showing how to do some key buffering to allow multiple key presses at once.
Coding
Key Press
To get started with the keyboard, just like we created display and reshape methods for GLUT to use, we are going to need to create another method. The first method we are going to deal with is the key pressed method; this method will tell us which keys are currently pressed.
I am going to name this method keyPressed and GLUT states that it has 3 parameters, one for the key that is currently pressed, and two more that give us the location of the mouse when the key was pressed.
void keyPressed (unsigned char key, int x, int y) {
}
Now that we have an empty method, let’s go ahead and give this information to GLUT to use. Down in our main method, we will access the glutKeyboardFunc method and supply it with our method.
glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
And that is pretty much all there is to it.
In order to access they keys pressed, inside your keyPressed method you can use either a switch statement or an ‘if’ statement, which checks they key variable against which ASCII character you want to check, and it will look something like this:
If (key == ‘a’) { // If they ‘a’ key was pressed
// Perform action associated with the ‘a’ key
}
Key Up
The key up function registers when a key is released. This can be very useful for setting some value when the key is first pressed, and wanting to keep that value until the key is released. Once again, as this is handled by GLUT, we will need to go and create a method to handle this, and just like the keyPressed method, this one will take in 3 parameters. These three parameters are exactly the same as 3 in the keyPressed method. But we are going to call this method keyUp. So let’s go ahead and create a method for this.
void keyUp (unsigned char key, int x, int y) {
}
And once again we need to register this method with GLUT, and this time we will use the glutKeyboardUpFunc method.
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events
To use the keyUp method, is exactly the same as using the keyPressed method, so we can call the exact same code if we wanted.
if (key == 'a') { // If they ‘a’ key was released
// Perform action associated with the ‘a’ key
}
Key Buffering
The next step is of course, to do some key buffering, which will allow us to handle multiple key presses later on in our OpenGL projects. As you might be aware, a char variable is the same as a byte and can take an integer value from 0 to 255. This is because a char variable has a size of 2^8, or 256. This allows us to handle up to 256 different keys on a keyboard, or the entire ASCII set of characters.
So let’s start off by creating an array of Boolean values that will hold the states of our keys. True will be pressed, and False will be not pressed. I’m going to call this variable keyStates as it is easy to understand.
bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255)
And now in our keyPressed method, let’s add a new line, which will set the value of the key we have pressed. This just sets the current position in the array of keyStates, at the position of the currently pressed key, to true.
void keyPressed (unsigned char key, int x, int y) {
keyStates[key] = true; // Set the state of the current key to pressed
}
And now we just need to add almost the exact same code to the keyUp method we created earlier. Only this time, we will be setting the state of the current key to false, meaning it is no longer pressed.
void keyUp (unsigned char key, int x, int y) {
keyStates[key] = false; // Set the state of the current key to not pressed
}
All that is left now for us to learn is how to actually use this. The good news is that now that you have the key information, you can use it however you want; you just have to setup somewhere in your code to manage this. I’ll go through an example on this to get you started.
To do this, I am going to create another method, this time called keyOperations, where we are going to check for which keys have been pressed, and then perform their actions. This method doesn’t need to return anything, and doesn’t need to accept any parameters. Just declare it before your display() method, as we are actually going to call it from our display method.
void keyOperations (void) {
}
And as we are calling this from our display method, go ahead and call it from the very start of our display method, preferably before everything else. The reason I say to call it before everything else, is if you call it halfway through your rendering, then half of your render will be affected by any values updated by the key presses, and the other half of your render will not be affected, giving a mismatch and possible artifacts.
void display (void) {
keyOperations();
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
…
}
Finally, we fill in our keyOperations method with a whole bunch of if statements to check if the keys we want are pressed, and then perform the required actions.
void keyOperations (void) {
if (keyStates['a']) { // If the 'a' key has been pressed
// Perform 'a' key operations
}
}
Special Keys
While all of the above is perfectly fine for using regular key presses, there are some keys you may want to use, for example, the arrow keys, which can be represented differently on multiple systems. Luckily, GLUT allows us to use these keys, as long as we call a different method to glutKeyboardFunc. Instead we must use glutSpecialFunc which checks for presses of special keys. For example, if we want to check if the left arrow has been pressed, we can use GLUT_KEY_LEFT. The best news is that the method you create to handle your special keys, will be almost identical to the glutKeyboardFunc method you created earlier. Below is an example of using special keys.
First off, create a new method which I am going to call keySpecial, which takes three parameters, all of them being an integer value, the first for the key pressed, and the second two are for the x and y position of the mouse at the time the method was called. And then call this method in our main method along with the rest of our key presses.
void keySpecial (int key, int x, int y) {
}
And then for the up function for special keys, declare a new method for handling the up event for special keys.
void keySpecialUp (int key, int x, int y) {
}
int main (int argc, char **argv) {
...
glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping
glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events
glutSpecialFunc(keySpecial); // Tell GLUT to use the method "keySpecial" for special key presses
glutSpecialUpFunc(keySpecialUp); // Tell GLUT to use the method "keySpecialUp" for special up key events
glutMainLoop(); // Enter GLUT's main loop
}
If you want to buffer these keys, you can create another array just like we did for regular key buffering, and dedicate this array to any special keys pressed.
bool* keySpecialStates = new bool[246]; // Create an array of boolean values of length 256 (0-255)
After you create this array, make sure you set the values to true and false, just as you did in the regular key methods, and then create another method to control what happens when we press our special keys, I am calling mine keySpecialOperations, which already has code for checking if the left arrow key has been pressed.
void keySpecialOperations(void) {
if (keySpecialStates[GLUT_KEY_LEFT]) { // If the left arrow key has been pressed
// Perform left arrow key operations
}
}
Hopefully this has helped you get an understanding of how to manage key presses in GLUT and OpenGL so you can get some keyboard interaction going in the later tutorials.
If you have any questions, please email me at swiftless@gmail.com
Tutorial Code
#include <GL/glew.h> // Include the GLEW header file
#include <GL/glut.h> // Include the GLUT header file
bool* keyStates = new bool[256]; // Create an array of boolean values of length 256 (0-255)
void keyOperations (void) {
if (keyStates['a']) { // If the 'a' key has been pressed
// Perform 'a' key operations
}
}
void display (void) {
keyOperations();
glClearColor(1.0f, 0.0f, 0.0f, 1.0f); // Clear the background of our window to red
glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations
glFlush(); // Flush the OpenGL buffers to the window
}
void reshape (int width, int height) {
glViewport(0, 0, (GLsizei)width, (GLsizei)height); // Set our viewport to the size of our window
glMatrixMode(GL_PROJECTION); // Switch to the projection matrix so that we can manipulate how our scene is viewed
glLoadIdentity(); // Reset the projection matrix to the identity matrix so that we don't get any artifacts (cleaning up)
gluPerspective(60, (GLfloat)width / (GLfloat)height, 1.0, 100.0); // Set the Field of view angle (in degrees), the aspect ratio of our window, and the new and far planes
glMatrixMode(GL_MODELVIEW); // Switch back to the model view matrix, so that we can start drawing shapes correctly
}
void keyPressed (unsigned char key, int x, int y) {
keyStates[key] = true; // Set the state of the current key to pressed
}
void keyUp (unsigned char key, int x, int y) {
keyStates[key] = false; // Set the state of the current key to not pressed
}
int main (int argc, char **argv) {
glutInit(&argc, argv); // Initialize GLUT
glutInitDisplayMode (GLUT_SINGLE); // Set up a basic display buffer (only single buffered for now)
glutInitWindowSize (500, 500); // Set the width and height of the window
glutInitWindowPosition (100, 100); // Set the position of the window
glutCreateWindow ("You’re first OpenGL Window"); // Set the title for the window
glutDisplayFunc(display); // Tell GLUT to use the method "display" for rendering
glutReshapeFunc(reshape); // Tell GLUT to use the method "reshape" for reshaping
glutKeyboardFunc(keyPressed); // Tell GLUT to use the method "keyPressed" for key presses
glutKeyboardUpFunc(keyUp); // Tell GLUT to use the method "keyUp" for key up events
glutMainLoop(); // Enter GLUT's main loop
}
Related posts:







Heyy, I love the tutorials you provide
They’re a huge help for learners! I’ve got a question. Why don’t you place the multikeys conditions in the KeyPress function itself. Is there a reason for this? Or is it for pure clean coding purposes?
Hey A Great Learner,
This is more for synchronisation issues between rendering and key presses. If you can imagine, if a key is pressed and registered twice in the time it takes to render one frame, then the key logic will be implemented twice, and everything will end up out of time for what you want. This ensures that by calling the key logic on the render pass, we only call it once per frame.
Cheers,
Swiftless
Hi! Great tutorials, but I noticed the special key boolean array in the tutorial code is 246 booleans long. I assume this should also be 256? As stated in the comment.
1) There is need to initialize the bool array (for me default values are ‘true’.
Efficient initialization of bool array:
std::fill_n( keyStates, 256, false);
std::fill_n( keySpecialStates, 256, false);
2) If you do not have some animation that calls your display method. You need to put
glutPostRedisplay();
function in ‘keyPressed’ and ‘keySpecial’ methods.
If not the ‘keyOperations’ is not called.
Hi Denn,
When I knock off the first 4-5 OpenGL 4 tutorials, I will be updating this one and it will initialise the array.
In regards to no. 2, if both your glutDisplayFunc and glutIdleFunc point to the same method, then you do not require glutPostRedisplay() as the idle function takes care of all non-opengl methods. glutPostRedisplay tells GLUT that we want to re-draw our scene and would make sense if you are applying the GLUT way of programming, and use the Idle function for non-opengl calls, and the display function for only rendering.
Cheers,
Swiftless
I have to agree with Denn on his 2nd point. Without that call to glutPostRedisplay(), my key presses aren’t being recognised (or rather, the effects aren’t being translated through to the display). Trying to set both glutDisplayFunc and glutIdleFunc to point to the display method causes the program to eat up all the CPU time, become unresponsive and not update on any of the key presses. I’m guessing that’s because the display function is then being called during the main loop as well as whenever the program is idle, which is effectively calling it constantly, thereby eating up all CPU time and causing unresponsive behaviour.
Does that call to glutPostRedisplay in the keyPressed function (and keySpecial function) have any effect on the rendering speed of the program? For example, if I had an animation loop set to render (call the display function) every 1/30th second, would pressing keys, and therefore calling glutPostRedisplay, accelerate, or otherwise alter, the rendering speed?
Feedback appreciated. Thanks in advance.
Slice
Hey Slice,
Can I ask what version of FreeGLUT and what OS you are working on? I am not experiencing any problems on my Windows box, or my Macbook, in either the regular GLUT or FreeGLUT, but I can confirm that it chews up CPU time. It will use up all the CPU, but it should also cause it to constantly render, and you also shouldn’t have to worry about sync between your variables and your renders, plus it should also give you maximum framerates. If you are experiencing issues though, definitely go with what works for you.
Calls to glutPostRedisplay should not have any effect on rendering speed, I believe it just sets a flag that tells GLUT that it needs to re-render the scene.
Cheers,
Swiftless
Hi man,
you have an error in the listing after: and the other half of your render will not be affected, giving a mismatch and possible artifacts.
Some HTML code is shown in there.
And in this one:
1. bool* keySpecialStates = new bool[246]; // Create an array of boolean values of length 256 (0-255)
There should be a 256, not a 246.
BTW: I think you have to delete those global arrays if you create them in the heap. But using static memory would be simpler, and wouldn’t have anything bad..
Is it necessary to clear the key buffer array when the app is first loaded?
like the following function called at the beginning of main:
void keyStates()
{
for (char i=0; i<256; i++) keyStates[i] = false;
}
or is this unnecessary
Hi Newby,
That is a darn good question, and I myself looked up just to make sure of this.
In the case of this tutorials, I am working with boolean types in that pointer array. Booleans in C++ can have values of either ‘TRUE’ or ‘FALSE’, but what we have to remember is that the underlying boolean values for ‘TRUE’ and ‘FALSE’ are 1 and 0 respectively. Meaning ‘TRUE’ has a value of 1 and ‘FALSE’ has a value of 0.
Now if we go look at how arrays and pointers work in C++, when an array is created, each object is set to NULL until an actual object is created and set. This is where a technicality comes into play. NULL in C++ is equivalent to 0 in standard C++. Which means that while every value in the boolean array is initially set to NULL, it is actually set to 0 which is FALSE in regards to boolean values.
But I will definitely say that you should initialise every value in an array before it is used in the case you could get NULL pointer exception, or if the memory is written incorrectly and you may have an incorrect value upon launch.
Cheers,
Swiftless
What you are saying is not completely correct.
Dynamic variables are taken from the heap, you cannot guarantee their initial value unless they have an explicit constructor.
Try:
int* v= new int;
*v= 1234;
std::cout << *v << std::endl;
delete v;
int * w= new int;
std::cout << *w << std::endl;
delete w;
You will get strange values for "w".
Java initializes variables to zero, not C or C++.
What I did not understand is why you used a dynamic array for keyState.
bool* keyState = new bool[256];
Wouldn't it be equally effective and better to use a static array instead?
bool keyState[256];
Again, static arrays should be initialized.
Hey Juan,
I still recommend initialising arrays, as I mention in an earlier comment, and it probably is better to use a static array, although it won’t make any difference to the application.
But for initialisation, you also need to look at the difference between local and global arrays in C++. Global arrays in C++ are initialised to a default value of 0 for all fundamental data types.
Cheers,
Swiftless
Beautiful tutorial brother. However it would be awesome if you offered some printable (pdf) version for this for archiving purposes…
That’s a great idea Adam. I have been writing them up in Microsoft Word with nice formatting and everything before I make the online version, and I can turn these into pdf’s with the click of a button. I might do that when I get some time.
Cheers,
Swiftless
Try LaTeX, it’s 100000000000000% better.
I use MacTex for serious documents, but for quick WYSIWYG documents, still stick to Word on Windows and Pages on OSX.
Cheers,
Swiftless
Hi Hans,
That looks like a mistake on my part that I will fix up today. For the arrow keys to work, you need to use the method glutSpecialFunc instead of glutKeyboard Func in the main method, which will handle all GLUT_KEY_* key presses.
Cheers,
Swiftless
Hello. The tutorials are fine, but i can’t get the arrow keys to work when i duplicate your code. The other keys work just fine. Thank you.