finetuning of the Win32 simulator

This commit is contained in:
Christian Kroll 2013-11-23 19:29:13 +01:00
parent 266b599cfd
commit 615387ce31
1 changed files with 81 additions and 71 deletions

View File

@ -50,23 +50,24 @@
/** Height of the canvas. */ /** Height of the canvas. */
#define WND_Y_EXTENTS (NUM_ROWS * LED_EXTENT) #define WND_Y_EXTENTS (NUM_ROWS * LED_EXTENT)
/* string constants */ /* string constants */
LPCSTR g_strWindowClass = "BorgSimulatorWindowClass"; LPCSTR g_strWindowClass = "BorgSimulatorWindowClass";
LPCSTR g_strWindowTitle = "Borg Simulator"; LPCSTR g_strWindowTitle = "Borg Simulator";
LPCSTR g_strError = "Error"; LPCSTR g_strErr = "Error";
LPCSTR g_strErrorRegisterWindow = "Could not register window class."; LPCSTR g_strErrTimerResolution = "Could not retrieve minimum timer resolution.";
LPCSTR g_strErrorCreateWindow = "Could not create window."; LPCSTR g_strErrRegisterWindow = "Could not register window class.";
LPCSTR g_strErrorCreateEvent = "Could not create wait event."; LPCSTR g_strErrCreateWindow = "Could not create window.";
LPCSTR g_strErrorCreateThread = "Could not create display loop thread."; LPCSTR g_strErrCreateEvent = "Could not create wait event.";
LPCSTR g_strErrorCreateUITimer = "Could not create UI Timer."; LPCSTR g_strErrCreateThread = "Could not create display loop thread.";
LPCSTR g_strErrCreateUITimer = "Could not create UI Timer.";
/** Minimum timer resolution the system is capable of. */
UINT g_uResolution;
/** Event object for the multimedia timer (wait() function). */ /** Event object for the multimedia timer (wait() function). */
HANDLE g_hWaitEvent; HANDLE g_hWaitEvent;
/** Fake port for simulating joystick input. */ /** Fake port for simulating joystick input. */
volatile unsigned char fakeport; volatile unsigned char fakeport;
/** Flag which indicates if wait should jump to the menu if fire is pressed. */ /** Flag which indicates if wait should jump to the menu if fire is pressed. */
@ -82,7 +83,6 @@ LRESULT CALLBACK simWndProc(HWND hWnd,
WPARAM wParam, WPARAM wParam,
LPARAM lParam); LPARAM lParam);
/** /**
* Creates a new window and makes it visible. * Creates a new window and makes it visible.
* @param hInstance Handle of the instance where this window should belong to. * @param hInstance Handle of the instance where this window should belong to.
@ -110,7 +110,7 @@ HWND simCreateWindow(HINSTANCE hInstance,
if (RegisterClassA(&lpwc) != 0) if (RegisterClassA(&lpwc) != 0)
{ {
/* ensure that the client area has the right proportions */ /* ensure that the client area has the right proportions */
RECT rect = {0, 0, WND_X_EXTENTS * 1.5 - 1, WND_Y_EXTENTS * 1.5 - 1}; RECT rect = {0, 0, WND_X_EXTENTS * 1.5 - 1, WND_Y_EXTENTS * 1.5 - 1};
AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW & ~(WS_OVERLAPPED), FALSE); AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW & ~(WS_OVERLAPPED), FALSE);
/* create window and retrieve its handle */ /* create window and retrieve its handle */
@ -133,13 +133,12 @@ HWND simCreateWindow(HINSTANCE hInstance,
} }
else else
{ {
fprintf(stderr, g_strErrorRegisterWindow); fprintf(stderr, g_strErrRegisterWindow);
} }
return hWnd; return hWnd;
} }
/** /**
* Closes windows and unregisters its associated window class. * Closes windows and unregisters its associated window class.
* @param lphWnd Pointer to window handle. * @param lphWnd Pointer to window handle.
@ -152,7 +151,6 @@ void simDestroyWindow(HWND hWnd,
UnregisterClassA(g_strWindowClass, hInstance); UnregisterClassA(g_strWindowClass, hInstance);
} }
/** /**
* Draws the LED matrix on the given device context. * Draws the LED matrix on the given device context.
* @param hdc The device context where the LED matrix should be drawn on. * @param hdc The device context where the LED matrix should be drawn on.
@ -216,7 +214,6 @@ void simDrawMatrix(HDC hdc)
} }
} }
/** /**
* Retrieves device context from given window, creates a compatible memory * Retrieves device context from given window, creates a compatible memory
* device context for double buffering and hands that thing over to * device context for double buffering and hands that thing over to
@ -363,7 +360,7 @@ LRESULT CALLBACK simWndProc(HWND hWnd,
/* map key releases to fake joystick movements */ /* map key releases to fake joystick movements */
case WM_KEYUP: case WM_KEYUP:
switch(wParam) switch (wParam)
{ {
case VK_SPACE: /* fire */ case VK_SPACE: /* fire */
fakeport &= ~0x01; fakeport &= ~0x01;
@ -411,7 +408,6 @@ LRESULT CALLBACK simWndProc(HWND hWnd,
return lResult; return lResult;
} }
/** /**
* Entry point for starting the the display loop in a thread. * Entry point for starting the the display loop in a thread.
* @param lpParam Free style arguments for the thread function (not used here). * @param lpParam Free style arguments for the thread function (not used here).
@ -423,19 +419,36 @@ DWORD WINAPI simLoop(LPVOID lpParam)
return 0; return 0;
} }
/**
* Retrieves and enforces the minimum timer resolution of the current system.
* @return Result of the multimedia timer operations.
*/
MMRESULT simSetMinimumTimerResolution()
{
TIMECAPS tc;
MMRESULT mmresult;
mmresult = timeGetDevCaps(&tc, sizeof(tc));
if (mmresult == TIMERR_NOERROR)
{
/* retrieve best resolution and configure timer services accordingly */
g_uResolution = min(max(tc.wPeriodMin, 0), tc.wPeriodMax);
mmresult = timeBeginPeriod(g_uResolution);
}
return mmresult;
}
/** /**
* Wait function which utilizes multimedia timers and thread synchronization * Wait function which utilizes multimedia timers and thread synchronization
* objects. Although this is much more complicated than calling the Sleep() * objects. Although this is much more complicated than calling the Sleep()
* function, it is also much more precise. * function, it is also much more precise.
* @param ms The requested delay in milliseconds. * @param ms The requested delay in milliseconds.
* @param uResolution The minimum timer resolution the system is capable of.
*/ */
void wait(int ms) void wait(int ms)
{ {
TIMECAPS tc;
MMRESULT mmresult;
MMRESULT mmTimerEventId; MMRESULT mmTimerEventId;
UINT uResolution;
/* check if fire button is pressed (and if it is, jump to the menu) */ /* check if fire button is pressed (and if it is, jump to the menu) */
if (waitForFire) if (waitForFire)
@ -446,35 +459,20 @@ void wait(int ms)
} }
} }
/* retrieve timer resolution capabilities of the current system */ /* retrieve a multimedia timer */
mmresult = timeGetDevCaps(&tc, sizeof(tc)); mmTimerEventId = timeSetEvent(ms, g_uResolution, g_hWaitEvent, 0,
if (mmresult == TIMERR_NOERROR) TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
if (mmTimerEventId != 0)
{ {
/* retrieve best resolution and configure timer services accordingly */ /* now halt until that timer pulses our wait event object */
uResolution = min(max(tc.wPeriodMin, 0), tc.wPeriodMax); WaitForSingleObject(g_hWaitEvent, INFINITE);
mmresult = timeBeginPeriod(uResolution); ResetEvent(g_hWaitEvent);
if (mmresult == TIMERR_NOERROR)
{
/* actually retrieve a multimedia timer */
mmTimerEventId = timeSetEvent(ms, uResolution, g_hWaitEvent, 0,
TIME_ONESHOT | TIME_CALLBACK_EVENT_SET);
if (mmTimerEventId != 0)
{
/* now halt until that timer pulses our wait event object */
WaitForSingleObject(g_hWaitEvent, INFINITE);
ResetEvent(g_hWaitEvent);
/* relieve the timer from its duties */ /* relieve the timer from its duties */
timeKillEvent(mmTimerEventId); timeKillEvent(mmTimerEventId);
}
/* relax timer service constraints */
timeEndPeriod (uResolution);
}
} }
} }
/** /**
* Main function of the windows simulator. * Main function of the windows simulator.
* @param hInstance Instance handle given by the operating system. * @param hInstance Instance handle given by the operating system.
@ -485,8 +483,8 @@ void wait(int ms)
*/ */
int APIENTRY WinMain(HINSTANCE hInstance, int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, LPSTR lpCmdLine,
int nCmdShow) int nCmdShow)
{ {
HWND hWnd; HWND hWnd;
MSG msg; MSG msg;
@ -496,55 +494,67 @@ int APIENTRY WinMain(HINSTANCE hInstance,
if ((hWnd = simCreateWindow(hInstance, nCmdShow)) != NULL) if ((hWnd = simCreateWindow(hInstance, nCmdShow)) != NULL)
{ {
/* event handle for multimedia timer (for the wait() function) */ /* retrieve minimum timer resolution */
g_hWaitEvent = CreateEventA(NULL, TRUE, FALSE, "Local\\WaitEvent"); if (simSetMinimumTimerResolution() == TIMERR_NOERROR)
if (g_hWaitEvent != NULL)
{ {
/* start the display loop thread */ /* event handle for multimedia timer (for the wait() function) */
hLoopThread = CreateThread(NULL, 0, simLoop, NULL, 0, NULL); g_hWaitEvent = CreateEventA(NULL, TRUE, FALSE, "Local\\WaitEvent");
if (hLoopThread != NULL) if (g_hWaitEvent != NULL)
{ {
SetThreadPriority(hLoopThread, THREAD_PRIORITY_TIME_CRITICAL); /* start the display loop thread */
hLoopThread = CreateThread(NULL, 0, simLoop, NULL, 0, NULL);
/* issue a UI timer message every 40 ms (roughly 25 fps) */ if (hLoopThread != NULL)
uTimerId = SetTimer(hWnd, 23, 40, NULL);
if (uTimerId != 0)
{ {
/* standard Windows(R) message loop */ SetThreadPriority(hLoopThread,
while (GetMessageA(&msg, NULL, 0, 0)) THREAD_PRIORITY_TIME_CRITICAL);
{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
nExitCode = msg.wParam;
KillTimer(hWnd, uTimerId); /* issue a UI timer message every 40 ms (roughly 25 fps) */
uTimerId = SetTimer(hWnd, 23, 40, NULL);
if (uTimerId != 0)
{
/* standard Windows(R) message loop */
while (GetMessageA(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
nExitCode = msg.wParam;
KillTimer(hWnd, uTimerId);
}
else
{
fprintf(stderr, g_strErrCreateUITimer);
}
TerminateThread(hLoopThread, 0);
} }
else else
{ {
fprintf(stderr, g_strErrorCreateUITimer); fprintf(stderr, g_strErrCreateThread);
} }
TerminateThread(hLoopThread, 0); /* relieve wait event object from its duties */
CloseHandle(g_hWaitEvent);
} }
else else
{ {
fprintf(stderr, g_strErrorCreateThread); fprintf(stderr, g_strErrCreateEvent);
} }
/* relieve wait event object from its duties */ /* relieve timer resolution constraints */
CloseHandle(g_hWaitEvent); timeEndPeriod(g_uResolution);
} }
else else
{ {
fprintf(stderr, g_strErrorCreateEvent); fprintf(stderr, g_strErrTimerResolution);
} }
simDestroyWindow(hWnd, hInstance); simDestroyWindow(hWnd, hInstance);
} }
else else
{ {
fprintf(stderr, g_strErrorCreateWindow); fprintf(stderr, g_strErrCreateWindow);
} }
return nExitCode; return nExitCode;