Every operating system has its own file structure and Vista/Win 7 and beyond don't like writing to the application path. And it doesn't make sense to write intentional user stuff in the same place as potential engine temp files (i.e. I download a map or a install a mod, that isn't the same as my config ... I can re-download Travail, but I can't re-download a demo I made).
Code: Select all
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// These are the subfolders used by the engine on Windows
#define ENGINE_WIN32_CACHES_SUBPATH "caches"
#define ENGINE_WIN32_DATA_SUBPATH "Gamedata"
char binaryPath[MAX_OSPATH_IS_1024];
// Stage 1: Finalize versioning names
Host_SetIdentity (System_OS_Version());
// Stage 2: Finalize path names
StringLCopy (binaryPath, System_GetBinaryFullPath());
String_Edit_Remove_FileName (binaryPath);
Host_SetDirectories (
/* Binary path */ System_GetBinaryFullPath(),
/* Input data path */ va ("%s/%s", binaryPath, ENGINE_WIN32_DATA_SUBPATH),
/* Write docs path */ va ("%s/%s", System_GetAppDataPath (), host.appName),
/* Cache write dir */ va ("%s/%s/%s", System_GetAppDataPath (), host.appName, ENGINE_WIN32_CACHES_SUBPATH)
);
// Stage 3: Wire it up
// Fill in any Windows specific stuff here
w32_plat.hInstance = hInstance;
w32_plat.registeredClassName = host.appVersionString;
w32_plat.hIcon = LoadIcon (w32_plat.hInstance, MAKEINTRESOURCE (IDI_ICON1)); // ID1_ICON1 is defined in resource.h
Host_Init (); // Keep separate in case we need access to memory, messages, earlier.
Host_Main_Loop (); // Main loop
Host_Shutdown (); // Shut everything down
return (0); // Success
}Code: Select all
void Host_Main_Loop (void)
{
unsigned previous_time_milliseconds = System_Milliseconds (); // Note ... this will initialize time, which does not count as a subsystem at least at this time
// Main program loop
while (!host.terminate)
{
Platform_Run_Loop_Events (); // Let system handle messages, decide to sleep, whatever
host.time_milliseconds = System_Milliseconds ();
host.timeslice = host.time_milliseconds - previous_time_milliseconds;
previous_time_milliseconds = host.time_milliseconds;
Host_Frame (host.timeslice);
}
}And taking all the operating system specific crap, isolating it in one place ...
Code: Select all
typedef struct
{
// Required to construct a window
HINSTANCE hInstance;
HICON hIcon; // Window icon
const char *registeredClassName; // Set to the application version string
// Window extra details ...
HWND mainwindow; // HWND for our window
HDC maindc; // device context for our window
HGLRC mainRC; // rendering context
} platform_t;Throw everything that can possibly be system neutral, but global, into its own "haystack".
Code: Select all
typedef struct
{
const char *operatingSysText; // Text description of operating system
const char *appName; // Application name
const char *appVersion; // The version string
const char *appVersionString; // Application version name
// All these directories are full operating system path ...
const char *binaryFileName; // (Not fullpath)
const char *binaryDir; // Path to the binary (yeah, it may not be an ".exe")
const char *bundleDir; // Input. <Program Files/whatever> Data that the application needs. (pak files)
const char *documentsDir; // Output. <My Documents Something>User created files (save games, configs, etc.)
const char *cacheDir; // Temp files, downloaded maps and such. <App Data something> Non-user created content directory
// Display environment ... on Windows this is the desktop for the primary monitor
int deviceWidth;
int deviceHeight;
int deviceBitsPerPixel;
int deviceRefreshRate;
float deviceScale;
// Display state info ...
fbool cl_isFullScreen; // Are we displaying fullscreen (therefore we probably changed the display settings
int cl_width; // Client width
int cl_height; // Client height
int cl_bitsPerPixel; // Client bits per pixel
int cl_refreshRate; // Refresh rate in Hz
char cl_describeVideo[64]; // Description
fRECT windowArea;
fRECT clientArea;
int borderAreaWidth;
int borderAreaHeight;
int mouse_center_x; // Center of window to keep mouse centered
int mouse_center_y; // Center of window to keep mouse centered
// Messaging state ...
fbool isAltTabCapable; // If we have no video window, we can't ALT-TAB
fbool isMinimized;
fbool isActiveApp;
fbool isVideoSuspended;
fbool isSoundSuspended;
// Generate start
fbool startup_complete;
// Timing
fbool terminate; // If set to true a normal "Quit" has been issued. Finishes frame. Exits ..
unsigned time_milliseconds;
unsigned timeslice;
} host_t;Code: Select all
LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// MSDSN: If an application processes this message, it should return zero to continue
// check for input messages
if (Win32_ReadInputMessages (hWnd, uMsg, wParam, lParam)) return 0;
switch (uMsg)
{
case WM_ACTIVATE: // WM_ACTIVATE: Sent to both the window being activated and the window being deactivated.
Platform_Focus_Change (LOWORD(wParam) != WA_INACTIVE, (fbool)HIWORD(wParam));
return 0;
case WM_KILLFOCUS: // WM_KILLFOCUS: Sent to a window immediately before it loses the keyboard focus.
if (host.cl_isFullScreen)
ShowWindow (w32_plat.mainwindow, SW_SHOWMINNOACTIVE); // Minmize if fullscreen
return 0;
case WM_CLOSE: // WM_CLOSE: Signal that a window/application should terminate.
Host_Normal_Quit ();
return 0;
case WM_CREATE: // WM_CREATE: After window created, but before is visible
case WM_DESTROY: // WM_DESTROY: Sent when a window immediately after removed from screen
case WM_SYSCHAR: // WM_SYSCHAR: keep Alt-Space from happening and any ALT + letter combination
return 0;
case WM_SIZE: // WM_SIZE: Sent to a window after its size has changed.
Platform_Window_Update_Info ();
Host_Render_Frame ();
return 0;
case WM_MOVE: // WM_MOVE: Sent after a window has been moved.
Platform_Window_Update_Info ();
return 0;
case WM_GETMINMAXINFO: // WM_GETMINMAXINFO: Sent before size change; can be used to override default mins/maxs
if (host.isAltTabCapable) // If we aren't ALT-TAB capable, we cannot resize.
{
MINMAXINFO *mmi = (MINMAXINFO *) lParam;
mmi->ptMinTrackSize.x = 320 + host.borderAreaWidth;
mmi->ptMinTrackSize.y = 200 + host.borderAreaHeight;
}
return 0;
}
// pass all unhandled messages to DefWindowProc
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}This is because I'm not going to use a Windows RECT to store the client area or the Window area. Those are not really Windows-specific pieces of information, so I use an fRECT where f = Fake but the same and I can use that data structure on anything. I also avoid qboolean data type because it is {false, true} and that is a C++ datatype. So I use {False, True}, which is annoying because I prefer "true and false" ... but "True and False" don't conflict with C++ and I do want to potentially be able to utilize C++ code if the situation fits, and if I use "true and false" ... a mess begins being unable to share structs.
Code: Select all
// Refresh rects and mouse center
// Called at window resize/move and window construction.
void Platform_Window_Update_Info (void)
{
WINDOWINFO windowinfo;
RECT twindowArea;
windowinfo.cbSize = sizeof (WINDOWINFO);
GetWindowRect (w32_plat.mainwindow, &twindowArea);
GetWindowInfo (w32_plat.mainwindow, &windowinfo);
host.windowArea.top = twindowArea.top;
host.windowArea.bottom = twindowArea.bottom;
host.windowArea.left = twindowArea.left;
host.windowArea.right = twindowArea.right;
host.clientArea.top = windowinfo.rcClient.top;
host.clientArea.bottom = windowinfo.rcClient.bottom;
host.clientArea.left = windowinfo.rcClient.left;
host.clientArea.right = windowinfo.rcClient.right;
host.mouse_center_x = host.clientArea.left + (RECT_WIDTH(host.clientArea) >> 1);
host.mouse_center_y = host.clientArea.top + (RECT_HEIGHT(host.clientArea) >> 1);
host.borderAreaWidth = RECT_WIDTH(host.windowArea) - RECT_WIDTH(host.clientArea);
host.borderAreaHeight = RECT_HEIGHT(host.windowArea) - RECT_HEIGHT(host.clientArea);
// Fill in the description
host.cl_width = windowinfo.rcClient.right - windowinfo.rcClient.left;
host.cl_height = windowinfo.rcClient.bottom - windowinfo.rcClient.top;
SNPrintf (host.cl_describeVideo, sizeof(host.cl_describeVideo), "%i x %i %i bpp rate = %i Hz (%s)", host.cl_width, host.cl_height, host.cl_bitsPerPixel, host.cl_refreshRate, host.cl_isFullScreen ? "fullscreen": "windowed");
}Code: Select all
typedef fbool (*func_begin) (void);
typedef void (*func_end) (void);
typedef struct
{
func_begin Startup;
func_end Shutdown;
fbool initialized;
} subsystem_t;
subsystem_t subsystems [] =
{
{ Platform_Init, Platform_Shutdown, False }, // Gets desktop size
{ Platform_Window_Create, NULL, False }, // Sets up video
{ Renderer_OpenGL_Init, Renderer_OpenGL_Shutdown, False }, // Initializes OpenGL
};
int num_sub_systems = sizeof (subsystems) / sizeof(subsystems[0]);
// Initialize sub-systems
void Host_Init (void)
{
int i;
// Initialize each sub system
for (i = 0; i < num_sub_systems; i ++)
{
subsystems[i].initialized = subsystems[i].Startup ();
// if (subsystems[i].initialized)
// Printf something
}
host.startup_complete = True;
}
// Shutdown sub-systems
void Host_Shutdown (void)
{
// Last in, first out ...
int i, j;
for (j = 0; j < num_sub_systems; j ++)
{
i = (num_sub_systems - j) - 1; // ie 10 - 0 - 1 = starts at 9
if (!subsystems[i].initialized || subsystems[i].Shutdown == NULL)
continue;
subsystems[i].Shutdown ();
// Printf something
}
}End blah ...