About
The Allegro tutorial at http://wiki.allegro.cc/index.php?title=Allegro_5_Tutorial/Threads gives an example of using the Allegro threading interface.Purpose of the example
The example linked from the above Wiki is to teach how to use threading in Allegro. The functionality of the example is simple: a "bouncer" box is moved from the upper-left of the screen to the lower-left of the screen. One thread shall modify the bouncer's X coordinate, and another thread shall modify the bouncer's Y coordinate. The main function shall handle timer events and redraw the screen as appropriate.Refactoring the example
The original example works and is comprehensible, but I had two problems with it: 1. It is written as "C-Style C++ code", which I found unintuitive. 2. There is no visible modularization, even though dividing into modules, each with a specific responsibility, may help in understanding the Allegro system.The below refactored code is presented below. Sections which are traditionally in their own file (header and implementation files) are commented out in blocks and named as "modules". This organization may serve as useful to build more complex examples.
Modules and explanation
MAIN
initialize Allegro and call the EVENT HANDLER and REDRAW HANDLEREVENT HANDLER
handle events by deciding what action to take when certain events come inREDRAW HANDLER
handle redrawing the screenDATA
define a data structure used by othe rmodules to communicate state informationSource Listing
#include <stdio.h>
#include <allegro5/allegro.h>
#include <stdbool.h>
#include <assert.h>
#include <stdlib.h>
// ---------------------------------------------------------------------------
// Header of DATA module
// ---------------------------------------------------------------------------
typedef struct {
ALLEGRO_MUTEX *mutex;
ALLEGRO_COND *cond;
float posiX;
float posiY;
char modifyWhich; // which variable to modify, 'X' or 'Y'
bool ready;
} DATA;
#define DATA_NEWINIT (\
(DATA){ \
.mutex=NULL, .cond=NULL, .posiX=0, .posiY=0, \
.modifyWhich='\0', .ready=false \
} \
)
DATA *DATA_new(void);
void DATA_delete(DATA *self);
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Header of MAIN module
// ---------------------------------------------------------------------------
const float FPS = 30;
const int SCREEN_W = 640;
const int SCREEN_H = 480;
const int BOUNCER_SIZE = 32;
#define BLACK (al_map_rgb(0, 0, 0))
#define MAGENTA (al_map_rgb(255, 0, 255))
static void *Func_Thread(ALLEGRO_THREAD * thr, void *arg);
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Header of EVENT HANDLER Module
// ---------------------------------------------------------------------------
#define MIN(x,y) ( (x) < (y) ? (x) : (y) )
#define MAX(x,y) ( (x) > (y) ? (x) : (y) )
#define CODE_CONTINUE (MIN(EXIT_SUCCESS,EXIT_FAILURE) - 1)
#define CODE_SUCCESS EXIT_SUCCESS
#define CODE_FAILURE EXIT_FAILURE
typedef int code;
code HandleEvent(ALLEGRO_EVENT ev);
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Header of REDRAW HANDLER Module
// ---------------------------------------------------------------------------
bool REDRAW_IS_READY;
void RedrawSetReady(void);
void RedrawClearReady(void);
bool RedrawIsReady(void);
void RedrawDo(); // to be defined in Implementation of EVENT HANDLER Module
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Implementation of MAIN Module
// ---------------------------------------------------------------------------
int main()
{
// Initialize Allegro, mouse, timer, display and bitmap
// XXX - skimping on Allegro error-checking
al_init();
al_install_mouse();
ALLEGRO_TIMER *timer = al_create_timer(1.0 / FPS);
ALLEGRO_DISPLAY *display = al_create_display(SCREEN_W, SCREEN_H);
ALLEGRO_BITMAP *bouncer = al_create_bitmap(BOUNCER_SIZE, BOUNCER_SIZE);
al_set_target_bitmap(bouncer);
al_clear_to_color(MAGENTA);
al_set_target_bitmap(al_get_backbuffer(display));
ALLEGRO_EVENT_QUEUE *event_queue = al_create_event_queue();
al_register_event_source(event_queue,
al_get_display_event_source(display));
al_register_event_source(event_queue,
al_get_timer_event_source(timer));
al_register_event_source(event_queue, al_get_mouse_event_source());
al_clear_to_color(BLACK);
al_flip_display();
DATA *data = DATA_new();
// Start timer
al_start_timer(timer);
// Set shared DATA
al_lock_mutex(data->mutex);
data->modifyWhich = 'X';
data->ready = false;
al_unlock_mutex(data->mutex);
// Initialize and start thread_1
ALLEGRO_THREAD *thread_1 = al_create_thread(Func_Thread, data);
al_start_thread(thread_1);
al_lock_mutex(data->mutex);
while (!data->ready) {
al_wait_cond(data->cond, data->mutex);
}
al_unlock_mutex(data->mutex);
// Set shared DATA
al_lock_mutex(data->mutex);
data->modifyWhich = 'Y';
data->ready = false;
al_unlock_mutex(data->mutex);
// Initialize and start thread_2
ALLEGRO_THREAD *thread_2 = al_create_thread(Func_Thread, data);
al_start_thread(thread_2);
al_lock_mutex(data->mutex);
while (!data->ready) {
al_wait_cond(data->cond, data->mutex);
}
al_unlock_mutex(data->mutex);
// Event loop
int code = CODE_CONTINUE;
while (code == CODE_CONTINUE) {
if (RedrawIsReady() && al_is_event_queue_empty(event_queue))
RedrawDo(data, bouncer);
ALLEGRO_EVENT ev;
al_wait_for_event(event_queue, &ev);
code = HandleEvent(ev);
}
// Clean up resources and exit with appropriate code
al_destroy_thread(thread_1);
al_destroy_thread(thread_2);
al_destroy_bitmap(bouncer);
al_destroy_timer(timer);
al_destroy_display(display);
al_destroy_event_queue(event_queue);
return code;
}
#define INCVAL (0.1f)
#define RESTVAL (0.01f)
#define UNKNOWN_ERROR (0)
static void *Func_Thread(ALLEGRO_THREAD *thr, void *arg)
{
DATA *data = arg;
al_lock_mutex(data->mutex);
char mw = data->modifyWhich;
data->ready = true;
al_broadcast_cond(data->cond);
al_unlock_mutex(data->mutex);
while (!al_get_thread_should_stop(thr)) {
al_lock_mutex(data->mutex);
if (mw == 'X')
data->posiX += INCVAL;
else if (mw == 'Y')
data->posiY += INCVAL;
else
assert(UNKNOWN_ERROR);
al_unlock_mutex(data->mutex);
al_rest(RESTVAL);
}
return NULL;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Implementation of EVENT HANDLER Module
// ---------------------------------------------------------------------------
code HandleEvent(ALLEGRO_EVENT ev)
{
switch(ev.type) {
case ALLEGRO_EVENT_TIMER:
RedrawSetReady();
break;
case ALLEGRO_EVENT_DISPLAY_CLOSE:
return EXIT_SUCCESS;
case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN:
return EXIT_SUCCESS;
default:
break;
}
return CODE_CONTINUE;
}
void RedrawDo(DATA *data, ALLEGRO_BITMAP *bouncer)
{
al_lock_mutex(data->mutex);
float X = data->posiX;
float Y = data->posiY;
al_unlock_mutex(data->mutex);
al_draw_bitmap(bouncer, X, Y, 0);
al_flip_display();
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Implementation of DATA module
// ---------------------------------------------------------------------------
DATA *DATA_new(void) {
DATA *self = NULL;
self = malloc(sizeof(*self));
assert(self);
*self = DATA_NEWINIT;
self->mutex = al_create_mutex();
self->cond = al_create_cond();
assert(self->mutex);
assert(self->cond);
return self;
}
void DATA_delete(DATA *self) {
al_destroy_mutex(self->mutex);
al_destroy_cond(self->cond);
free(self);
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Implementation of REDRAW HANDLER Module
// ---------------------------------------------------------------------------
bool REDRAW_IS_READY = false;
void RedrawSetReady(void) { REDRAW_IS_READY = true; }
void RedrawClearReady(void) { REDRAW_IS_READY = false; }
bool RedrawIsReady(void) {
switch (REDRAW_IS_READY) {
case true:
RedrawClearReady();
return true;
default:
return false;
}
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
Keine Kommentare:
Kommentar veröffentlichen