|
@ -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); |
|
|
|
|
|
|
|
|
|
|
|
/* 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 |
|
|
{ |
|
|
{ |
|
|
TranslateMessage(&msg); |
|
|
fprintf(stderr, g_strErrCreateUITimer); |
|
|
DispatchMessageA(&msg); |
|
|
|
|
|
} |
|
|
} |
|
|
nExitCode = msg.wParam; |
|
|
|
|
|
|
|
|
|
|
|
KillTimer(hWnd, uTimerId); |
|
|
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; |
|
|