3. OpenGL Keyboard Interaction (Version 2.0)
Introduction
The keyboard is one of those things that is essential to computer gaming, and computer use in general. Find the perfect one for your needs at https://qwertybro.com. 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 ("Your 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 }
Is there a way to make a key_down event, as this code checks for how long a key is pressed. What if I want to know if a key is pressed only once?? 🙁
How do we check if the escape key was pressed? 🙁
By the way, I love your tutorials! 😀
If anyone was wondering why trying to test keypresses (via using std::cout output) didn’t seem to work (like I was) unless the window is redrawn, via pressing the key while moving the window about.
I believe this is fixed in tutorial 7, regarding glutIdleFunc.
Once I had reached this point I noticed keypresses were being registered without having to redraw the window.
A side effect is that pressing the key once very quickly results in a lot of keypresses being registered (once for each call to display is my guess during the time the button is down).
Slightly off topic below:
My guess is the buttons could be run through a debounce filter function.
Alternatively the character movement could be setup to use a velocity value rather than a direct move-left, move-right, etc.. value.
thank you for these good tutorials 🙂
they are really helping me to learn and understand OpenGL
keep up the good work 🙂
dont know if youre still following this but you have a typo
bool* keySpecialStates = new bool[246]; // Create an array of boolean values of length 256 (0-255)
should be 256 not 246
Hello, your tutorils help a lot. But I have some problems about this keyboard interation.Here is a part of my program:
if (keyStates[‘r’])
{
glClear(GL_COLOR_BUFFER_BIT); //Clear the colour buffer (more buffers later on)
glClearColor(1.0f, 0.0f, 0.0f, 0.0f); // Clear the background of our window to red
glLoadIdentity(); // Load the Identity Matrix to reset our drawing locations
glFlush(); // Flush the OpenGL buffers to the window
}
I also include some other keyboards(‘g’ and ‘b’ to display red, green,and blue on the screen) as well. And it compiles correctly. But when I begin to run it, the problem occors: at the begining, when I press the ‘b’ or some other keyboard down ,the screen displays red .After I press sometimes, the program runs correctly a bit,but not absolutely, because I have press two times if I want to chan the screen color. So, I need your help, can you tell me where goes wrong? Last , thankyou very much.~~
You are properbly not in need for an answer any longer, but to others: Try initializing the bool array with false values with ()
bool* keyStates = new bool[256]();
The error is because the value at keyStates[‘r’] is a null-pointer, and when checking if
keyStates['r']
is true or false, the value is not 0 (false) and so the if statement is true, even though no key has been pressed. The following would also work if you don’t want to initialize the array with false:if(keyStates['r'] == true) {..}
very good tutorial for me tanx .
Hi, i’ve tried to implement this keyboard control method but i have a problem where at the start of the program when i run it, my box (which i control with keyboard) shoots straight up off the screen and i have to quickly bring it back down again.
I can control it fine after ive pressed a key but it just goes mental when i first run it. So something must be telling the UP key that its being held down at the start and im not quite sure how to change it?
Don’t worry now i’ve sorted it, i just needed to set all my values in the key array to false:
bool keySpecialStates [256] = {false};
You explained it easily.Thank you.
Hi,
Anybody can help Dynamically getting Points ,to How to draw a line in OpenGL2.0
I really wish you would learn the difference between “you’re” and “your”
Could it have crossed your mind that based on the grammar on my website, this was most likely a typo? I really wish you knew how to give constructive criticism.
Thank you so much, that was really really helpful.
I’m designing a Tank game, I have had a problem with multiple keys events, but now I think I can manage it.
Thanks again for this topic, keep it up please 🙂
The code is good , except he forgot to initialize the pointer array.
Try to set all the values in the array to false at the begining of your program, say in the init(){} function. This will help to prevent any auto movement as soon as you start your program. You may also need to decrease moving speed. Otherwise, object will move out of the window during the period you press and release a botton.
I am just learning OpenGL and this tutorial has been very helpful. I transfered the glClearColor() method into the keyOperations method so that when I press ‘a’ the window will change color to red (just trying to get used to all the methods), and it only works occasionally. I added a console dump to see if I was actually looping and checking for new key presses, but when it prints to the console the display function is only called 1, 2, or 3 times (it’s not consistent) and then it just hangs up and does nothing. Do you know what would cause the glut main loop to hang up and not accept keyboard input? I’m developing on Linux.
Thanks.
I’m developing on linux as well. It seems the display function is only called when when something post a request of calling it (e.g. reshape), until then it’s in the idle function. Put this line at the end of your keyboard function to fix it:
glutPostRedisplay();
(or specify your idle function to the glut and move the keyOperations() there)
I used the method of handling keys showed in this tutorial but now for some reason I can’t get the key repeat to turn off. I tried using glutIgnoreKeyRepeat(true); everywhere but no luck. Anyone know of a solution? I’m using windows btw.
It just dawned to me that the problem is that since the function keyOperations() is inside the display function, the key operation repeats itself until released, where it changes it’s state to false again. A solution I figured was to move the keyOperations() call to inside the keyPressed() function.
Thanks for the tip. It’s working now. I was wondering why it wasn’t detecting the key I was checking for inside keyOperations() and it is indeed true that its place is inside keyPressed() and not inside display(). Now I just have to figure out the thing with the special keys since I’m using Python and it’s a little bit different there when it comes to characters, key codes etc.
That might be a solution but the actual problem is that the author of this code didn’t use `idle()` to re-render the windows (recall the `display()` repeatly) . This is why `keyOperations()` is called once. This is a deadly mistake, hope he fixes it asap.
I hope you are still replying to these. I have a problem where the keyboard command doesn’t update until a new key is pressed. If it would help i could send you the code. Thanks
Hi. To handle multiple keys, you need to have the key repeat off. This can b done by adding “glutSetKeyRepeat(GLUT_KEY_REPEAT_OFF);” before the “glutMainLoop()” call.
Key repeat might be off by default for most people, but for me (and others) it wasn’t (this was on linux).
Hi Alex,
Thanks for that! Definitely handy for confirming this will work.
Cheers,
Swiftless
thank you for your tutorials, they really help me a lot! Hope you keep updating your tutorial, it’s awesome!
Hi
I had a problem working with keys in my project.
I need the gl cube to rotate when a key is pressed by the user.
I tried using the function described above(ought to say its good tutorial. 🙂 ) but I am stuck with what to use as the case of switch statement.
Like I want the cube to rotate clockwise by 5 degrees when ‘w’ key is pressed. I increased the angle variable by 5.0f of the function glRotatef() and called again the function glutDisplayFunc(drawScene).. But there is no effect of pressing it.
Someone please help
thanks
hi its good
Hi, I copy paste your tutorial and found a problem. Every time my program is being run, ‘A’ is always in the state of being pressed (I perform operation printing in the console to identify this problem). I assume boolean is always being initialized as “true” (though when printed it in the console, 205 value appears).
Swapping the true-false, solve the problem though:
void keyPressed(unsigned char key, int x, int y){
keyStates[key] = false; //instead of true
}
void keyUp(unsigned char key, int x, int y){
keyStates[key] = true; //instead of false
}
void keyOperations(void){
if(!keyStates[‘a’]){
//If false, perform some operations
}
}
Hi Fiona,
It sounds like when we setup the array, it is not being initialised to all values being 0. In the comments it has already been mentioned that there should be another loop in the code that specifically sets all parts of the array to 0/false before it is used.
Cheers,
Swiftless
Another good tutorial.But readers must be aware of the fact that key functions are incomplete.They shouldn’t complain for no-response on keypresses.Because this tutorial(as I understood) is intended to be a pre-work for further tutorials.I say this because there are such readers.Anyway,thanks.
Hi,
I need a help regarding one of my projects. It is supposed to have 9 rectangles with numbers 1-8 in random order. With one empty rectangle which moves, I should bring these 8 rectangles in order.
But, I am having problem with getting started. How to move those rectangles. With keyboard functions, I am not able to make them move.
Could you please suggest some ideas. Thank you in advance.
Hi Pratibha,
To move shapes, you need to use variables which store the position of the shape (x, y, z) coordinates and then use the glTranslate functions with these variables.
Use the keyboard controls to update the position variables.
Cheers,
Swiftless
Hi man I have a problem… I used your keybuffer method in my code and after few compiles stupid errors started appearing. and it always says permission denied , id returned 1 exit status. I was searching thorugh ineterent and I think its a problem with a heap but I cant solve it. whatever I do this error doesnt want to go. help me please!
Hi blacktener,
That doesn’t sound like a heap problem, that would be a linker problem. I have seen similar errors when the compiler doesn’t have permissions to overwrite the executable file you are creating (such as when you use sudo on unix systems initially, but then try to recompile without sudo privileges), but I can’t guarantee that this is the same problem.
Cheers,
Swiftless
Thank you so much for the buffer help i was going nuts trying to figure that out!
Hey. Thanks for posting up the tutorials. They’re very helpful. I actually have a question. I’m working on a cube function, where you can move the cube left or right if you press the left or right arrow keys respectively and I running into trouble as to how to the cube to move left or right. If you could help me out with this dilemma, it would be greatly appreciated.
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
hey ,
i need anybody help , i have an opengl project and it has to be done by sunday and i dont have any idea at all about how i can do it and im really really desperate plz plz plz help me 🙁 …
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..
hey ,
i need anybody help … i have an opengl project and i need someone to help me to do it and it have to be done by sunday and im desperate … plz plz help me 🙁 …
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.