/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Old timer interrupt routines emulation. * * By Shawn Hargreaves. * * Synchronization added by Eric Botcazou. * * Converted into an emulation layer by Peter Wang. * * See readme.txt for copyright information. */ #include #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/internal/aintern_thread.h" #define TIMER_TO_MSEC(x) ((long)((x) / 1000)) /* list of active timer handlers */ typedef struct TIMER_QUEUE { AL_TIMER *timer; AL_METHOD(void, proc, (void)); /* timer handler functions */ AL_METHOD(void, param_proc, (void *param)); void *param; /* param for param_proc if used */ } TIMER_QUEUE; TIMER_DRIVER *timer_driver = NULL; /* the active driver */ /* (a dummy in this emulation) */ int _timer_installed = FALSE; static TIMER_QUEUE _timer_queue[MAX_TIMERS]; /* list of active callbacks */ volatile int retrace_count = 0; /* used for retrace syncing */ void (*retrace_proc)(void) = NULL; long _vsync_speed = BPS_TO_TIMER(70); /* retrace speed */ int _timer_use_retrace = FALSE; /* are we synced to the retrace? */ volatile int _retrace_hpp_value = -1; /* to set during next retrace */ static _AL_THREAD timer_thread; /* the timer thread */ static _AL_MUTEX timer_mutex = _AL_MUTEX_UNINITED; /* global timer mutex */ static AL_EVENT_QUEUE *event_queue; /* event queue to collect timer events */ static AL_TIMER *retrace_timer; /* timer to simulate retrace counting */ /* a dummy driver */ static TIMER_DRIVER timerdrv_emu = { AL_ID('T','E','M','U'), empty_string, empty_string, "Emulated timers" }; /* rest_callback: * Waits for time milliseconds. * Note: this can wrap if the process runs for > ~49 days. */ void rest_callback(unsigned int time, void (*callback)(void)) { if (callback) { unsigned long end = al_current_time() + time; while (al_current_time() < end) callback(); } else { al_rest(time); } } /* rest: * Waits for time milliseconds. */ void rest(unsigned int time) { al_rest(time); } /* timer_can_simulate_retrace: [deprecated but used internally] * Checks whether the current driver is capable of a video retrace * syncing mode. */ int timer_can_simulate_retrace() { return FALSE; } /* timer_simulate_retrace: [deprecated but used internally] * Turns retrace simulation on or off, and if turning it on, calibrates * the retrace timer. */ void timer_simulate_retrace(int enable) { } /* timer_is_using_retrace: [deprecated] * Tells the user whether the current driver is providing a retrace * sync. */ int timer_is_using_retrace() { return FALSE; } /* timer_thread_func: [timer thread] * Each "interrupt" callback registered by the user has an associated * AL_TIMER object. All these objects are registered to a global event * queue. This function runs in a background thread and reads timer events * from the event queue, calling the appropriate callbacks. */ static void timer_thread_func(_AL_THREAD *self, void *unused) { while (!_al_thread_should_stop(self)) { AL_EVENT event; if (!al_wait_for_event(event_queue, &event, 50)) continue; if ((AL_TIMER *)event.any.source == retrace_timer) { retrace_count++; /* retrace_proc is just a bad idea -- don't use it! */ if (retrace_proc) retrace_proc(); } else { bool found = false; TIMER_QUEUE copy; int x; memset(©, 0, sizeof(copy)); /* We delay the call until the timer_mutex is unlocked, to * avoid deadlocks. The callback itself can add or remove * timers. Using a recursive mutex isn't enough either. * * FIXME: There is a problem with this approach. If * remove_int() is called from another thread while we are in * the middle of the lock...unlock, the proc being removed * may run one more time after it was supposed to be removed! * This can be troublesome if shortly after remove_int() some * resource needed by the proc is freed up (hopefully rare!). */ _al_mutex_lock(&timer_mutex); { for (x=0; x