Purple Martians
Technical Code Descriptions
Events
Overview
Processing the event queue
Processing events
Handling multiple resize events
Overview
Allegro 5 is event based. When I converted my game from Allegro 4, I had to do a lot of things differently.
Allegro 5 has an event queue that needs to be processed.
In the examples I've seen, the event queue proccessing is the main control loop of the program, like this:
// example event processing loop
while (1)
{
al_wait_for_event(queue, &event);
if (event.type == ALLEGRO_EVENT_TIMER)
{
handle_game_tick();
need_redraw = true;
}
if (need_draw && al_event_queue_is_empty(queue))
{
render_last_frame();
need_redraw = false;
}
}
This is not how I process the event queue in my game.
I didn't want to make such a huge change to my code, so I came up with an alternative approach.
I have always had a function called 'proc_controllers()' that I call once per frame to handle input.
For Allegro 5, I re-purposed it to also process the event queue, and it seems to work just fine.
I never call al_wait_for_event(). Once every game loop, I process all events in the queue and move on.
When the game is not running and I'm in a menu or the level editor, or whatever, I just call 'proc_controllers()' in whatever loop I'm in.
The speed of the game and menu are controlled by timers.
Processing the event queue
Once every frame I read all the events from the queue and process them.
Most events are passed on to 'proc_events()', except for TIMER and DISPLAY RESIZE which are handled differently.
int proc_controllers()
{
int ret = 0;
int menu_timer_block = 1;
while (menu_timer_block)
{
while (!al_is_event_queue_empty(event_queue))
{
ALLEGRO_EVENT ev;
if (al_get_next_event(event_queue, &ev))
{
if (ev.type == ALLEGRO_EVENT_TIMER) menu_timer_block = 0;
if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE)
{
// check to see if we have more resize events piling up
ALLEGRO_EVENT ev2;
while (al_get_next_event(event_queue, &ev2))
{
if (ev2.type == ALLEGRO_EVENT_DISPLAY_RESIZE) ev = ev2;
else ret = proc_events(ev2, ret);
}
proc_screen_change(ev.display.width, ev.display.height, disp_x_curr, disp_y_curr, fullscreen);
}
else ret = proc_events(ev, ret);
}
}
if (game_exit) // if called from menu only do key check for active local player
{
//....
}
else
{
menu_timer_block = 0;
}
}
return ret;
}
Processing events
Here is how most events are processed:
int proc_events(ALLEGRO_EVENT ev, int ret)
{
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) fast_exit(0);
if (ev.type == ALLEGRO_EVENT_MOUSE_WARPED)
{
mouse_x = ev.mouse.x / display_transform_double;
mouse_y = ev.mouse.y / display_transform_double;
}
if (ev.type == ALLEGRO_EVENT_MOUSE_AXES)
{
mouse_x = ev.mouse.x / display_transform_double;
mouse_y = ev.mouse.y / display_transform_double;
mouse_z = ev.mouse.z / display_transform_double;
mouse_dx = ev.mouse.dx;
mouse_dy = ev.mouse.dy;
mouse_dz = ev.mouse.dz;
}
if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
{
if (ev.mouse.button == 1) mouse_b1 = 1;
if (ev.mouse.button == 2) mouse_b2 = 1;
if (ev.mouse.button == 3) mouse_b3 = 1;
if (ev.mouse.button == 4) mouse_b4 = 1;
}
if (ev.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
{
if (ev.mouse.button == 1) mouse_b1 = 0;
if (ev.mouse.button == 2) mouse_b2 = 0;
if (ev.mouse.button == 3) mouse_b3 = 0;
if (ev.mouse.button == 4) mouse_b4 = 0;
}
if (ev.type == ALLEGRO_EVENT_KEY_DOWN)
{
int k = ev.keyboard.keycode;
key[k] = true;
ret = k;
}
if (ev.type == ALLEGRO_EVENT_KEY_UP)
{
int k = ev.keyboard.keycode;
key[k] = false;
if (k == ALLEGRO_KEY_PRINTSCREEN) key[k] = true; // special exception to make PRINTSCREEN work
}
if (ev.type == ALLEGRO_EVENT_KEY_CHAR)
{
Key_pressed_ASCII = ev.keyboard.unichar;
serial_key_check(Key_pressed_ASCII);
}
if (ev.type == ALLEGRO_EVENT_JOYSTICK_AXIS)
{
int jy = getJoystickNum(ev.joystick.id);
int jo = 0; // offset
if (jy == 0) jo = 0;
if (jy == 1) jo = 20;
int ax = ev.joystick.axis;
float pos = ev.joystick.pos;
if (ax == 0) // x axis
{
key[130+jo] = false;
key[131+jo] = false;
if (pos > 0) key[131+jo] = true;
if (pos < 0) key[130+jo] = true;
}
if (ax == 1) // y axis
{
key[128+jo] = false;
key[129+jo] = false;
if (pos > 0) key[129+jo] = true;
if (pos < 0) key[128+jo] = true;
}
}
if (ev.type == ALLEGRO_EVENT_JOYSTICK_BUTTON_DOWN)
{
int jy = getJoystickNum(ev.joystick.id);
int sc = get_scan_code_from_joystick(jy, 1, ev.joystick.button);
key[sc] = true;
}
if (ev.type == ALLEGRO_EVENT_JOYSTICK_BUTTON_UP)
{
int jy = getJoystickNum(ev.joystick.id);
int sc = get_scan_code_from_joystick(jy, 1, ev.joystick.button);
key[sc] = false;
}
return ret;
}
Handling multiple resize events
When the display changes, an ALLEGRO_EVENT_DISPLAY_RESIZE event is generated.
This is usually when the window is resized by the user.
It is possible to get a lot of these events as the window is being resized.
Also when the display changes, all video bitmaps are destroyed and must be rebuilt. See: Rebuilding Bitmaps
This takes longer than a normal frame, and may cause delays, especially if I do it for every resize event in a long string of them.
So I implemented an algorithm in the event queue processing code to ignore all resize events except for the last one.
Here is a description of the algorithm:
If I get an ALLEGRO_EVENT_DISPLAY_RESIZE event, the first thing I do is check to see there are more events in the queue.
If there are no more events in the queue, I process the ALLEGRO_EVENT_DISPLAY_RESIZE event.
If there are more events in the queue, then I check to see if they are also ALLEGRO_EVENT_DISPLAY_RESIZE events.
If they are not ALLEGRO_EVENT_DISPLAY_RESIZE events, I process them right away.
If they are ALLEGRO_EVENT_DISPLAY_RESIZE events, I set the first event (ev) to the second one (ev2), overwriting and losing the first one.
When I no longer have any events in the queue, then I process the resize event in ev.
This method allows me to ignore all but the last ALLEGRO_EVENT_DISPLAY_RESIZE event, while still processing all other events.
while (!al_is_event_queue_empty(event_queue))
{
ALLEGRO_EVENT ev;
if (al_get_next_event(event_queue, &ev))
{
if (ev.type == ALLEGRO_EVENT_DISPLAY_RESIZE)
{
// check to see if we have more resize events piling up
ALLEGRO_EVENT ev2;
while (al_get_next_event(event_queue, &ev2))
{
if (ev2.type == ALLEGRO_EVENT_DISPLAY_RESIZE) ev = ev2;
else ret = proc_events(ev2, ret);
}
proc_screen_change(ev.display.width, ev.display.height, disp_x_curr, disp_y_curr, fullscreen);
}
else ret = proc_events(ev, ret);
}
}
See also: proc_screen_change() proc_events()