Pygame Tutorials
Input Handling

by Pete Shinners
pete@shinners.org

Revision 1.0, March 28th, 2004


Handling Input Events in Pygame is not difficult, but there are several techniques that can be used. You will want to use the correct method for the correct job. The two main techniques are Event Handling and State Checking. This tutorial will look at each of these, as well as show how to handle the keyboard, mouse, and joysticks.
 

State Checking

State Checking simply means calling a function to check the current position or value of an input device. This is usually the simplest way a program can figure out what is going on. Your program calls one of the state functions and knows immediately what the user is doing. The state is connected directly to the input device, so you know exactly what is going on.

Each Input device has an object that provides several methods to check the current state.

But there are several problems writing a game with State Checking as the only input control. First, you have no way to know the order of  actions. When calling button press checking function, there could be 2 or 3 buttons all pressed at once, you have no way to determine the order of pressing. The second problem is you can completely "miss" button pushes. If the user clicks very fast, between your calls to check the button state, you will never know anything changed. These problems are solved with event handling.

Another problem to avoid, if your game calls these checking functions multiple times computing the current frame, the value can switch partway through computing your frame. You may want to save the state into a variable and always check with that variable.
 

Event Handling

 The preferred way of dealing with input is Event Handling. Event handling is commonly used in graphical interface programs. The system keeps a list of things that have happened, and your program can process this, usually once per frame. This list of events is known as the Event Queue, and it allows you to know the order of everything that happened since your last time checking the queue.

ScreenshotThe pygame examples includes an excellent event inspector program named, "eventlist.py"


Pygame has several methods for managing the queue, all in the pygame.event module. Be aware that Pygame's queue is lower level than what you might be used to in bigger GUI frameworks. It is up to your game to manage everything about the queue. The main function you will use is pygame.event.get(), which removes the events in the queue and returns them inside a list.

The events are a single Event object type. Each event has a specific type ID attribute named "id". It also has several other named attributes specific to that type. This outline shows the input event ID's and the attributes they carry.

When you want to move objects while certain buttons are held down, you should check the event queue, and set the state of interesting buttons to global variables. Then your code can check the state of those variables.

Keyboard Control

The keyboard is probably the simplest input device. Keys are represented by their key id value. The only real control you have over the keyboard is setting repeat rates. By default, pygame sends a single KEYDOWN and KEYUP event for every keypress. You can enable key repeating with pygame.key.set_repeat(). The defaults are usually fine, but you can fine tune the repeat behavior. When keys are repeating, you will receive multiple KEYDOWN events for as long as the key is held, and a final KEYUP when it is released.

One other common need is getting text entry from the keyboard. This involved proper capitalization with the shift keys, as well as special input handling on international keyboards. This is a complex task to do by yourself. Fortunately, Pygame already provides these translations with every KEYDOWN event. The unicode attribute is the "system translated" representation of the key. For special control keys it can often be an empty string.

One last keyboard helpful tip. Remember that the keycode for the main ENTER key is K_RETURN. The keycode for the keypad enter key is K_ENTER. See the full list of keyboard keycode's here, in the reference documentation.

Mouse Control

Mouse input is fairly straightforward. There are a couple extra features you may want to be aware of. The mouse wheel is emulated in pygame through buttons 4 and 5. The only way to receive these events is from the MOUSEBUTTONDOWN events. This also means Pygame doesn't handle extended mouse buttons beyend the regular 3.

Pygame can enable a special "virtual infinite area", best used for fullscreen games. Typical behavior is the mouse is trapped to the screen edges in fullscreen mode. If you only care about relative mouse movement, this can present a problem. Even if the mouse cursor is hidden, you will stop receiving relative motion past the edges of the screen. To enable the "virtual infinite area" you must set the mouse cursor to invisible, then also grab the input focus with pygame.event.set_grab(1).

Managing the Event Queue

Your program must deal with the input event queue as it runs. Even if your program doesn't use events, there are a few things you'll need to watch for. The first thing to be aware of is that the queue does not have infinite size. Once the queue fills up, new events can no longer be created. The queue is plenty large to hold events for a single frame, but you if you ignore it for too long it will fill up. Especially with MOUSEMOTION events that get created frequently.

Another thing to be aware of is that your application needs to do some coordination with the graphic environment it lives in. This is especially important for windowed games, but still necessary for fullscreen games as well. When you call the Pygame event queue functions, Pygame will take a moment to cooperate with the graphics environment, and potentially even create new events on the queue itself. These are events like QUIT,  VIDEORESIZE, and others that let your program know what is going on. This makes it important that your application calls at least one pygame.event function, usually once per frame. If you do not care at all about the Pygame queue, you can call pygame.event.pump(), which will allow Pygame to do its necessary processing. You may also consider pygame.event.clear() to do the same thing, but keep the queue empty.

Some programs and games reach a point where they are waiting on user input. This is common in image viewers, simple paint programs, or even turn based games. If you are creating this type of program and would like to be extremely cooperative with other running applications, you can use pygame.event.wait(). If no events are available, your program will be put to sleep by the operating system, until some events becomes available. This means your program will take 0% cpu time while it is waiting for the users actions.

Pygame's event queue also has several ways to deal with events of a specific type. First you can call pygame.event.set_blocked(events), which prevents certain types of events from even entering the queue. You can also pass a list of interesting event types to pygame.event.get(), and you will only receive events of the type you asked for. Be careful passing this mask into pygame.event.get. All other event types will remain on the queue, and will eventually fill it up if you never ask for them.

Custom Events

Pygame reserves a set of event ID values for custom use in your program. These are values between USEREVENT and NUMEVENTS. It is up to your program to coordinate how these events are used. Certain pygame functions will require an event ID for creating their own events. For example, you can have Sound objects create an event of any type when they are finished playing.

To create your own events you first create an Event object, and pass it to pygame.event.post(). The Event objects are easy to create, you simply pass an event type ID and a list of keyword named arguments. Examples may make this easier.
PLAYERDEAD = USEREVENT+2
deadevent = pygame.event.Event(PLAYERDEAD, player=1, score=game.player1.score)
pygame.event.post(deadevent)

It is also simple to set up custom timer events, that are added to the queue for every time interval. You need to call pygame.time.set_timer(USEREVENT, delay). The delay is in milliseconds, and the given event type will appear on the event queue after each amount of time in the delay argument.