Christian Kroll
12 years ago
10 changed files with 1080 additions and 464 deletions
@ -1,9 +0,0 @@ |
|||||
#include "joystick.h" |
|
||||
|
|
||||
unsigned char fakeport; |
|
||||
|
|
||||
|
|
||||
// fake function since our keybord doesn't need any initialisation
|
|
||||
void joy_init() |
|
||||
{ |
|
||||
} |
|
@ -1,15 +0,0 @@ |
|||||
#ifndef JOYSTICK_H_ |
|
||||
#define JOYSTICK_H_ |
|
||||
|
|
||||
extern unsigned char fakeport; |
|
||||
|
|
||||
#define JOYISFIRE (0x01 & fakeport) |
|
||||
#define JOYISLEFT (0x02 & fakeport) |
|
||||
#define JOYISRIGHT (0x04 & fakeport) |
|
||||
#define JOYISDOWN (0x08 & fakeport) |
|
||||
#define JOYISUP (0x10 & fakeport) |
|
||||
|
|
||||
unsigned char waitForFire; |
|
||||
|
|
||||
|
|
||||
#endif /*JOYSTICK_H_*/ |
|
@ -1,26 +0,0 @@ |
|||||
#ifdef _WIN32 |
|
||||
#include <windows.h> |
|
||||
#endif |
|
||||
|
|
||||
#include <stdlib.h> |
|
||||
#include <sys/time.h> |
|
||||
#include <sys/types.h> |
|
||||
#include <unistd.h> |
|
||||
#include <setjmp.h> |
|
||||
#include "joystick.h" |
|
||||
|
|
||||
extern jmp_buf newmode_jmpbuf; |
|
||||
|
|
||||
void wait(unsigned int ms) { |
|
||||
if (waitForFire) { |
|
||||
if (JOYISFIRE) { |
|
||||
longjmp(newmode_jmpbuf, 43); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#ifdef _WIN32 |
|
||||
Sleep(ms); |
|
||||
#else |
|
||||
usleep(ms*1000); |
|
||||
#endif |
|
||||
} |
|
@ -0,0 +1,492 @@ |
|||||
|
/**
|
||||
|
* \defgroup winsimulator Simulation of the Borg API for the Win32 platform. |
||||
|
*/ |
||||
|
/*@{*/ |
||||
|
|
||||
|
/**
|
||||
|
* This is a native Win32 port of the Borgware-2D API simulator. Although the |
||||
|
* OpenGL based simulator is in fact platform independent, there are some |
||||
|
* obstacles regarding Cygwin's OpenGL support. |
||||
|
* |
||||
|
* Earlier versions of Cygwin used to ship bindings to Win32's native OpenGL |
||||
|
* libraries. Unfortunately some of those native components (GLUT in particular) |
||||
|
* weren't maintained for years so it was decided to cease support for them. |
||||
|
* |
||||
|
* The reasons are explained in more detail at |
||||
|
* http://cygwin.com/ml/cygwin/2012-05/msg00276.html
|
||||
|
* |
||||
|
* The OpenGL bindings which are now shipped with Cygwin require a running |
||||
|
* X-Server which I consider clumsy to use on a Windows platform (especially for |
||||
|
* a small application like this simulator). So I decided to write a native |
||||
|
* Win32 application to free Windows developers from the hassles of rolling out |
||||
|
* a complete X11 setup. |
||||
|
* |
||||
|
* The native simulator feels like the OpenGL based one, with the exception that |
||||
|
* you can't rotate the matrix (I'm using the plain GDI32 API for the graphics). |
||||
|
* |
||||
|
* @file winmain.c |
||||
|
* @brief Simulator for the Win32 platform. |
||||
|
* @author Christian Kroll |
||||
|
*/ |
||||
|
|
||||
|
#include <windows.h> |
||||
|
#include <setjmp.h> |
||||
|
#include "../config.h" |
||||
|
#include "../display_loop.h" |
||||
|
|
||||
|
/** Number of bytes per row. */ |
||||
|
#define LINEBYTES ((NUM_COLS + 1) / 8) |
||||
|
|
||||
|
/** The width (in pixels) of the margin around a LED. */ |
||||
|
#define LED_MARGIN 1 |
||||
|
/** The diameter (in pixels) of a LED. */ |
||||
|
#define LED_DIAMETER 14 |
||||
|
/** The extend of the whole LED including its margin. */ |
||||
|
#define LED_EXTENT (2 * LED_MARGIN + LED_DIAMETER) |
||||
|
|
||||
|
/** Width of the canvas. */ |
||||
|
#define WND_X_EXTENTS (NUM_COLS * LED_EXTENT) |
||||
|
/** Height of the canvas. */ |
||||
|
#define WND_Y_EXTENTS (NUM_ROWS * LED_EXTENT) |
||||
|
|
||||
|
|
||||
|
/* string constants */ |
||||
|
LPCSTR g_strWindowClass = "BorgSimulatorWindowClass"; |
||||
|
LPCSTR g_strWindowTitle = "Borg Simulator"; |
||||
|
|
||||
|
LPCSTR g_strError = "Error"; |
||||
|
LPCSTR g_strErrorRegisterWindow = "Error: Could not register window class."; |
||||
|
LPCSTR g_strErrorCreateWindow = "Error: Could not create window."; |
||||
|
|
||||
|
|
||||
|
/** Event object for the multimedia timer (wait() function). */ |
||||
|
HANDLE g_hWaitEvent; |
||||
|
|
||||
|
|
||||
|
/** Fake port for simulating joystick input. */ |
||||
|
volatile unsigned char fakeport; |
||||
|
/** Flag which indicates if wait should jump to the menu if fire is pressed. */ |
||||
|
volatile unsigned char waitForFire; |
||||
|
/** The simulated frame buffer of the borg. */ |
||||
|
volatile unsigned char pixmap[NUMPLANE][NUM_ROWS][LINEBYTES]; |
||||
|
/** Jump buffer which leads directly the menu. */ |
||||
|
extern jmp_buf newmode_jmpbuf; |
||||
|
|
||||
|
/* forward declarations */ |
||||
|
LRESULT CALLBACK simWndProc(HWND hWnd, |
||||
|
UINT msg, |
||||
|
WPARAM wParam, |
||||
|
LPARAM lParam); |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Registers a window class (necessary for creating a window). |
||||
|
* @param lpwc Pointer to WNDCLASS struct. |
||||
|
* @param hInstance Handle of the instance where this window class belongs to. |
||||
|
* @return TRUE if successful, otherwise FALSE. |
||||
|
*/ |
||||
|
BOOL simRegisterWindowClass(WNDCLASSA *const lpwc, |
||||
|
HINSTANCE hInstance) |
||||
|
{ |
||||
|
lpwc->style = 0; |
||||
|
lpwc->lpfnWndProc = simWndProc; |
||||
|
lpwc->cbClsExtra = 0; |
||||
|
lpwc->cbWndExtra = 0; |
||||
|
lpwc->hInstance = hInstance; |
||||
|
lpwc->hIcon = LoadIcon(NULL, IDI_WINLOGO); |
||||
|
lpwc->hCursor = LoadCursor(NULL, IDC_ARROW); |
||||
|
lpwc->hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); |
||||
|
lpwc->lpszMenuName = NULL; |
||||
|
lpwc->lpszClassName = g_strWindowClass; |
||||
|
|
||||
|
return (RegisterClassA(lpwc) != 0); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Creates a new window and makes it visible. |
||||
|
* @param lphWnd Pointer to window handle. |
||||
|
* @param hInstance Handle of the instance where this window belongs to. |
||||
|
* @param nCmdShow Flag for showing the window minimized, maximized etc. |
||||
|
* @return TRUE if successful, otherwise FALSE. |
||||
|
*/ |
||||
|
BOOL simCreateWindow(HWND *lphWnd, |
||||
|
HINSTANCE hInstance, |
||||
|
int nCmdShow) |
||||
|
{ |
||||
|
/* create window and retrieve its handle */ |
||||
|
*lphWnd = CreateWindow(g_strWindowClass, g_strWindowTitle, |
||||
|
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, |
||||
|
WND_X_EXTENTS * 2, WND_Y_EXTENTS * 2, HWND_DESKTOP, |
||||
|
NULL, hInstance, NULL); |
||||
|
|
||||
|
/* mske it visible */ |
||||
|
if (*lphWnd != NULL) |
||||
|
{ |
||||
|
ShowWindow(*lphWnd, nCmdShow); |
||||
|
UpdateWindow(*lphWnd); |
||||
|
return TRUE; |
||||
|
} |
||||
|
|
||||
|
return FALSE; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Draws the LED matrix on the given device context. |
||||
|
* @param hdc The device context where the LED matrix should be drawn on. |
||||
|
*/ |
||||
|
void simDrawMatrix(HDC hdc) |
||||
|
{ |
||||
|
COLORREF colorLed; |
||||
|
HBRUSH hBrushLed; |
||||
|
HGDIOBJ hGdiOld; |
||||
|
unsigned int c, p, x, y, absX; |
||||
|
int left, right, top, bottom; |
||||
|
static unsigned char const shl_map[8] = |
||||
|
{0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80}; |
||||
|
|
||||
|
/* clear background */ |
||||
|
FloodFill(hdc, 0, 0, RGB(0, 0, 0)); |
||||
|
|
||||
|
/* go through every plane */ |
||||
|
for (p = 0; p < NUMPLANE; ++p) |
||||
|
{ |
||||
|
/* create and select red brush into device context */ |
||||
|
colorLed = RGB((255.0 / NUMPLANE) * (p + 1), 0, 0); |
||||
|
hBrushLed = CreateSolidBrush(colorLed); |
||||
|
hGdiOld = SelectObject(hdc, hBrushLed); |
||||
|
|
||||
|
/* translate pixmap into LEDs */ |
||||
|
for (y = 0; y < NUM_ROWS; ++y) |
||||
|
{ |
||||
|
for (c = 0; c < LINEBYTES; ++c) |
||||
|
{ |
||||
|
for (x = 0; x < 8; ++x) |
||||
|
{ |
||||
|
if (pixmap[p][y][c] & shl_map[x]) |
||||
|
{ |
||||
|
// eventually draw a LED, mirroring its coordinates
|
||||
|
absX = (c * 8 + x) * LED_EXTENT + LED_MARGIN; |
||||
|
left = WND_X_EXTENTS - absX; |
||||
|
right = WND_X_EXTENTS - absX - LED_DIAMETER + 1; |
||||
|
top = y * LED_EXTENT + LED_MARGIN; |
||||
|
bottom = top + LED_DIAMETER - 1; |
||||
|
Ellipse(hdc, left, top, right, bottom); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* dispose that brush */ |
||||
|
DeleteObject(SelectObject(hdc, hGdiOld)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Retrieves device context from given window, creates a compatible memory |
||||
|
* device context for double buffering and hands that thing over to |
||||
|
* simDrawMatrix(). |
||||
|
* @param hWnd The window where the LED-Matrix should be displayed. |
||||
|
*/ |
||||
|
void simDisplay(HWND hWnd) |
||||
|
{ |
||||
|
RECT rect; |
||||
|
HDC hdc; |
||||
|
HDC hMemDc; |
||||
|
HBITMAP hBmp; |
||||
|
HBITMAP hOldBmp; |
||||
|
|
||||
|
/* retrieve window dimensions */ |
||||
|
if (GetClientRect(hWnd, &rect)) |
||||
|
{ |
||||
|
int const cx = rect.right - rect.left; |
||||
|
int const cy = rect.bottom - rect.top; |
||||
|
|
||||
|
/* retrieve device context */ |
||||
|
if ((hdc = GetDC(hWnd)) != NULL) |
||||
|
{ |
||||
|
/* make window contents scalable */ |
||||
|
SetMapMode(hdc, MM_ANISOTROPIC); |
||||
|
SetWindowExtEx(hdc, WND_X_EXTENTS, WND_Y_EXTENTS, NULL); |
||||
|
SetViewportExtEx(hdc, cx, cy, NULL); |
||||
|
|
||||
|
/* create memory device context for double buffering */ |
||||
|
hMemDc = CreateCompatibleDC(hdc); |
||||
|
if (hMemDc != NULL) |
||||
|
{ |
||||
|
/* contents of the memory DC should be scaled as well */ |
||||
|
SetMapMode(hMemDc, MM_ANISOTROPIC); |
||||
|
SetWindowExtEx(hMemDc, WND_X_EXTENTS, WND_Y_EXTENTS, NULL); |
||||
|
SetViewportExtEx(hMemDc, cx, cy, NULL); |
||||
|
|
||||
|
/* create a bitmap to be associated with the memory DC... */ |
||||
|
hBmp = CreateCompatibleBitmap(hdc, cx, cy); |
||||
|
if (hBmp != NULL) |
||||
|
{ |
||||
|
/* ...and selct that into that DC */ |
||||
|
hOldBmp = (HBITMAP)SelectObject(hMemDc, hBmp); |
||||
|
|
||||
|
/* finally *sigh* draw the LED matrix */ |
||||
|
simDrawMatrix(hMemDc); |
||||
|
|
||||
|
/* and blit that into the window DC */ |
||||
|
BitBlt(hdc, 0, 0, cx, cy, hMemDc, 0, 0, SRCCOPY); |
||||
|
|
||||
|
/* clean up */ |
||||
|
DeleteObject(SelectObject(hMemDc, hOldBmp)); |
||||
|
} |
||||
|
DeleteDC(hMemDc); |
||||
|
} |
||||
|
ReleaseDC(hWnd, hdc); |
||||
|
} |
||||
|
InvalidateRect(hWnd, &rect, FALSE); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Message handler for the main window. |
||||
|
* @param hWnd The window whose messages should be processed. |
||||
|
* @param msg The message fired from the operating system. |
||||
|
* @param wParam First message parameter. |
||||
|
* @param lParam Second message parameter. |
||||
|
*/ |
||||
|
LRESULT CALLBACK simWndProc(HWND hWnd, |
||||
|
UINT msg, |
||||
|
WPARAM wParam, |
||||
|
LPARAM lParam) |
||||
|
{ |
||||
|
PAINTSTRUCT ps; |
||||
|
HDC hdc; |
||||
|
LPMINMAXINFO lpminmax; |
||||
|
|
||||
|
switch (msg) |
||||
|
{ |
||||
|
/* enforce minimum window size */ |
||||
|
case WM_GETMINMAXINFO: |
||||
|
lpminmax = (LPMINMAXINFO)lParam; |
||||
|
lpminmax->ptMinTrackSize.x = WND_X_EXTENTS * 2; |
||||
|
lpminmax->ptMinTrackSize.y = WND_Y_EXTENTS * 2; |
||||
|
break; |
||||
|
|
||||
|
/* paint window contents */ |
||||
|
case WM_PAINT: |
||||
|
hdc = BeginPaint(hWnd, &ps); |
||||
|
if (hdc != NULL) |
||||
|
{ |
||||
|
simDisplay(hWnd); |
||||
|
EndPaint(hWnd, &ps); |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
/* map key presses to fake joystick movements */ |
||||
|
case WM_KEYDOWN: |
||||
|
switch (wParam) |
||||
|
{ |
||||
|
case VK_ESCAPE: |
||||
|
case 'Q': |
||||
|
PostQuitMessage(0); |
||||
|
break; |
||||
|
|
||||
|
case VK_SPACE: |
||||
|
fakeport |= 0x01; |
||||
|
break; |
||||
|
|
||||
|
case 'A': |
||||
|
fakeport |= 0x02; |
||||
|
break; |
||||
|
|
||||
|
case 'D': |
||||
|
fakeport |= 0x04; |
||||
|
break; |
||||
|
|
||||
|
case 'S': |
||||
|
fakeport |= 0x08; |
||||
|
break; |
||||
|
|
||||
|
case 'W': |
||||
|
fakeport |= 0x10; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
return DefWindowProcA(hWnd, msg, wParam, lParam); |
||||
|
break; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
/* map key releases to fake joystick movements */ |
||||
|
case WM_KEYUP: |
||||
|
switch(wParam) |
||||
|
{ |
||||
|
case VK_SPACE: |
||||
|
fakeport &= ~0x01; |
||||
|
break; |
||||
|
|
||||
|
case 'A': |
||||
|
fakeport &= ~0x02; |
||||
|
break; |
||||
|
|
||||
|
case 'D': |
||||
|
fakeport &= ~0x04; |
||||
|
break; |
||||
|
|
||||
|
case 'S': |
||||
|
fakeport &= ~0x08; |
||||
|
break; |
||||
|
|
||||
|
case 'W': |
||||
|
fakeport &= ~0x10; |
||||
|
break; |
||||
|
|
||||
|
default: |
||||
|
return DefWindowProcA(hWnd, msg, wParam, lParam); |
||||
|
break; |
||||
|
} |
||||
|
break; |
||||
|
|
||||
|
/* refresh the LED matrix every 40 ms */ |
||||
|
case WM_TIMER: |
||||
|
simDisplay(hWnd); |
||||
|
break; |
||||
|
|
||||
|
/* quit application */ |
||||
|
case WM_DESTROY: |
||||
|
PostQuitMessage(0); |
||||
|
break; |
||||
|
|
||||
|
/* Windows' default handler */ |
||||
|
default: |
||||
|
return DefWindowProcA(hWnd, msg, wParam, lParam); |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Entry point for starting the the display loop in a thread. |
||||
|
* @param lpParam Free style arguments for the thread function (not used here). |
||||
|
* @return Always zero. |
||||
|
*/ |
||||
|
DWORD WINAPI simLoop(LPVOID lpParam) |
||||
|
{ |
||||
|
display_loop(); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Wait function which utilizes multimedia timers and thread synchronization |
||||
|
* objects. Although this is much more complicated than calling the Sleep() |
||||
|
* function, it is also much more precise. |
||||
|
* @param ms The requested delay in milliseconds. |
||||
|
*/ |
||||
|
void wait(int ms) |
||||
|
{ |
||||
|
TIMECAPS tc; |
||||
|
MMRESULT mmresult; |
||||
|
MMRESULT mmTimerEventId; |
||||
|
UINT uResolution; |
||||
|
|
||||
|
/* check if fire button is pressed (and if it is, jump to the menu) */ |
||||
|
if (waitForFire) { |
||||
|
if (fakeport & 0x01) { |
||||
|
longjmp(newmode_jmpbuf, 43); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* retrieve timer resolution capabilities of the current system */ |
||||
|
mmresult = timeGetDevCaps(&tc, sizeof(tc)); |
||||
|
if (mmresult == TIMERR_NOERROR) |
||||
|
{ |
||||
|
/* retrieve best resolution and configure timer services accordingly */ |
||||
|
uResolution = min(max(tc.wPeriodMin, 0), tc.wPeriodMax); |
||||
|
mmresult = timeBeginPeriod(uResolution); |
||||
|
if (mmresult == TIMERR_NOERROR) |
||||
|
{ |
||||
|
/* actually retrieve a multimedia timer */ |
||||
|
mmTimerEventId = timeSetEvent(ms, uResolution, g_hWaitEvent, NULL, |
||||
|
TIME_ONESHOT | TIME_CALLBACK_EVENT_PULSE); |
||||
|
if (mmTimerEventId != NULL) |
||||
|
{ |
||||
|
/* now halt until that timer pulses our wait event object */ |
||||
|
WaitForSingleObject(g_hWaitEvent, INFINITE); |
||||
|
|
||||
|
/* relieve the timer from its duties */ |
||||
|
timeKillEvent(mmTimerEventId); |
||||
|
} |
||||
|
|
||||
|
/* relax timer service constraints */ |
||||
|
timeEndPeriod (uResolution); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Main function of the windows simulator. |
||||
|
* @param hInstance Instance handle given by the operating system. |
||||
|
* @param hPrevInstance This parameter has no meaning in Win32. |
||||
|
* @param lpCmdLine Pointer to a null terminated command line string. |
||||
|
* @param nCmdShow Flags for showing the window minimized, maximized and so on. |
||||
|
* @return Exit code, always 0 here. |
||||
|
*/ |
||||
|
int APIENTRY WinMain(HINSTANCE hInstance, |
||||
|
HINSTANCE hPrevInstance, |
||||
|
LPSTR lpCmdLine, |
||||
|
int nCmdShow) |
||||
|
{ |
||||
|
WNDCLASS wc; |
||||
|
HWND hWnd; |
||||
|
MSG msg; |
||||
|
HANDLE hThread; |
||||
|
|
||||
|
/* regster window class (with nice black background!) */ |
||||
|
if (simRegisterWindowClass(&wc, hInstance)) |
||||
|
{ |
||||
|
/* actually create the window and make it visible */ |
||||
|
if (simCreateWindow(&hWnd, hInstance, nCmdShow)) |
||||
|
{ |
||||
|
/* event handle for multimedia timer (for the wait() function) */ |
||||
|
g_hWaitEvent = CreateEventA(NULL, FALSE, FALSE, "Local\\WaitEvent"); |
||||
|
if (g_hWaitEvent != NULL) |
||||
|
{ |
||||
|
/* start the display loop thread */ |
||||
|
hThread = CreateThread(NULL, 0, simLoop, NULL, 0, NULL); |
||||
|
if (hThread != NULL) |
||||
|
{ |
||||
|
/* ensure that the display loop stays responsive */ |
||||
|
SetThreadPriority(hThread, THREAD_PRIORITY_HIGHEST); |
||||
|
|
||||
|
/* issue a timer message every 40 ms (roughly 25 fps) */ |
||||
|
/* NOTE: this has nothing to do with the multimedia timer */ |
||||
|
SetTimer(hWnd, 23, 40, NULL); |
||||
|
|
||||
|
/* standard Windows(R) message loop */ |
||||
|
/* (runs as long as the window hasn't been closed) */ |
||||
|
while (GetMessageA(&msg, NULL, 0, 0)) |
||||
|
{ |
||||
|
TranslateMessage(&msg); |
||||
|
DispatchMessageA(&msg); |
||||
|
} |
||||
|
|
||||
|
/* stop the display loop */ |
||||
|
TerminateThread(hThread, 0); |
||||
|
} |
||||
|
|
||||
|
/* relieve wait event object from its duties */ |
||||
|
CloseHandle(g_hWaitEvent); |
||||
|
} |
||||
|
|
||||
|
return msg.wParam; |
||||
|
} |
||||
|
MessageBoxA(HWND_DESKTOP, g_strErrorCreateWindow, g_strError, MB_OK); |
||||
|
} |
||||
|
MessageBoxA(HWND_DESKTOP, g_strErrorRegisterWindow, g_strError, MB_OK); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/*@}*/ |
Loading…
Reference in new issue