/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Windows timer driver. * * By Stefan Schimanski. * * Enhanced low performance driver by Eric Botcazou. * * See readme.txt for copyright information. */ #include "allegro.h" #include "allegro/internal/aintern.h" #include "allegro/platform/aintwin.h" #ifndef SCAN_DEPEND #ifdef ALLEGRO_MINGW32 #undef MAKEFOURCC #endif #include #include #endif #ifndef ALLEGRO_WINDOWS #error something is wrong with the makefile #endif #define PREFIX_I "al-wtimer INFO: " #define PREFIX_W "al-wtimer WARNING: " #define PREFIX_E "al-wtimer ERROR: " static TIMER_DRIVER timer_win32_high_perf; static TIMER_DRIVER timer_win32_low_perf; _DRIVER_INFO _timer_driver_list[] = { {TIMER_WIN32_HIGH_PERF, &timer_win32_high_perf, TRUE}, {TIMER_WIN32_LOW_PERF, &timer_win32_low_perf, TRUE}, {0, NULL, 0} }; static int tim_win32_high_perf_init(void); static int tim_win32_low_perf_init(void); static void tim_win32_exit(void); static void tim_win32_rest(unsigned int time, AL_METHOD(void, callback, (void))); static TIMER_DRIVER timer_win32_high_perf = { TIMER_WIN32_HIGH_PERF, empty_string, empty_string, "Win32 high performance timer", tim_win32_high_perf_init, tim_win32_exit, NULL, NULL, NULL, NULL, NULL, NULL, tim_win32_rest }; static TIMER_DRIVER timer_win32_low_perf = { TIMER_WIN32_LOW_PERF, empty_string, empty_string, "Win32 low performance timer", tim_win32_low_perf_init, tim_win32_exit, NULL, NULL, NULL, NULL, NULL, NULL, tim_win32_rest }; static HANDLE timer_thread = NULL; /* dedicated timer thread */ static HANDLE timer_stop_event = NULL; /* thread termination event */ /* high performance driver */ static LARGE_INTEGER counter_freq; static LARGE_INTEGER counter_per_msec; /* unit conversion */ #define COUNTER_TO_MSEC(x) ((unsigned long)(x / counter_per_msec.QuadPart)) #define COUNTER_TO_TIMER(x) ((unsigned long)(x * TIMERS_PER_SECOND / counter_freq.QuadPart)) #define MSEC_TO_COUNTER(x) (x * counter_per_msec.QuadPart) #define TIMER_TO_COUNTER(x) (x * counter_freq.QuadPart / TIMERS_PER_SECOND) #define TIMER_TO_MSEC(x) ((unsigned long)(x) / (TIMERS_PER_SECOND / 1000)) /* tim_win32_high_perf_thread: * Thread loop function for the high performance driver. */ static void tim_win32_high_perf_thread(void *unused) { DWORD result; unsigned long delay = 0x8000; LARGE_INTEGER curr_counter; LARGE_INTEGER prev_tick; LARGE_INTEGER diff_counter; /* init thread */ _win_thread_init(); /* get initial counter */ QueryPerformanceCounter(&prev_tick); while (TRUE) { if (!_win_app_foreground) { /* restart counter if the thread was blocked */ if (_win_thread_switch_out()) QueryPerformanceCounter(&prev_tick); } /* get current counter */ QueryPerformanceCounter(&curr_counter); /* get counter units till next tick */ diff_counter.QuadPart = curr_counter.QuadPart - prev_tick.QuadPart; /* save current counter for next tick */ prev_tick.QuadPart = curr_counter.QuadPart; /* call timer proc */ delay = _handle_timer_tick(COUNTER_TO_TIMER(diff_counter.QuadPart)); /* wait calculated time */ result = WaitForSingleObject(timer_stop_event, TIMER_TO_MSEC(delay)); if (result != WAIT_TIMEOUT) { _win_thread_exit(); return; } } } /* tim_win32_low_perf_thread: * Thread loop function for the low performance driver. */ static void tim_win32_low_perf_thread(void *unused) { DWORD result; unsigned long delay = 0x8000; DWORD prev_time; DWORD curr_time; /* in milliseconds */ DWORD diff_time; /* init thread */ _win_thread_init(); /* get initial time */ prev_time = timeGetTime(); while (TRUE) { if (!_win_app_foreground) { /* restart time if the thread was blocked */ if (_win_thread_switch_out()) prev_time = timeGetTime(); } /* get current time */ curr_time = timeGetTime(); /* get time till next tick */ diff_time = curr_time - prev_time; /* save current time for next tick */ prev_time = curr_time; /* call timer proc */ delay = _handle_timer_tick(MSEC_TO_TIMER(diff_time)); /* wait calculated time */ result = WaitForSingleObject(timer_stop_event, TIMER_TO_MSEC(delay)); if (result != WAIT_TIMEOUT) { _win_thread_exit(); return; } } } /* tim_win32_high_perf_init: * Initializes the high performance driver. */ static int tim_win32_high_perf_init(void) { if (!QueryPerformanceFrequency(&counter_freq)) { _TRACE(PREFIX_W "High performance timer not supported\n"); return -1; } /* setup high performance counter */ counter_per_msec.QuadPart = counter_freq.QuadPart / 1000; /* create thread termination event */ timer_stop_event = CreateEvent(NULL, FALSE, FALSE, NULL); /* start timer thread */ timer_thread = (void *)_beginthread(tim_win32_high_perf_thread, 0, 0); /* increase priority of timer thread */ SetThreadPriority(timer_thread, THREAD_PRIORITY_TIME_CRITICAL); return 0; } /* tim_win32_low_perf_init: * Initializes the low performance driver. */ static int tim_win32_low_perf_init(void) { /* create thread termination event */ timer_stop_event = CreateEvent(NULL, FALSE, FALSE, NULL); /* start timer thread */ timer_thread = (void *)_beginthread(tim_win32_low_perf_thread, 0, 0); /* increase priority of timer thread */ SetThreadPriority(timer_thread, THREAD_PRIORITY_TIME_CRITICAL); return 0; } /* tim_win32_exit: * Shuts down either timer driver. */ static void tim_win32_exit(void) { /* acknowledge that the thread should stop */ SetEvent(timer_stop_event); /* wait until thread has ended */ while (WaitForSingleObject(timer_thread, 100) == WAIT_TIMEOUT); /* thread has ended, now we can release all resources */ CloseHandle(timer_stop_event); } /* tim_win32_rest: * Rests the specified amount of milliseconds. */ static void tim_win32_rest(unsigned int time, AL_METHOD(void, callback, (void))) { unsigned int start; unsigned int ms = time; if (callback) { start = timeGetTime(); while (timeGetTime() - start < ms) (*callback)(); } else { Sleep(ms); } }