finetuning of the Win32 simulator
This commit is contained in:
parent
266b599cfd
commit
615387ce31
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue