0ad/source/lib/sysdep/win/win.cpp
Ykkrosh 35e91718c5 # Added tool for viewing models and animations outside the game.
Atlas: Added ActorViewer. Moved GL canvas into separate class for shared
use. Disabled message-handling callback while blocked on the game, and
stopped creating dialog boxes inside the game thread in order to avoid
deadlocks (hopefully). Support multiple Views (for independent sets of
camera/update/render code). Recalculate territory boundaries when
necessary. Changed default list of animations to match those currently
used by actors.
# Tidied up more code.
Moved some more #includes out of .h files, to minimise unnecessary
compilation.
MathUtil: Deleted unused/unuseful macros (M_PI (use PI instead), M_PI_2
(use PI/2), MAX3, ABS (use abs)).
ObjectManager: Removed some ScEd-specific things.
Unit: Moved creation out of UnitManager, so units can be created without
adding to the manager. Changed CStr8 to the more conventional CStr.
app_hooks: Removed warning for setting multiple times.
win: Restored SEH catcher.
GameSetup, GameView: Removed RenderNoCull, because it doesn't seem to do
what it says it does ("force renderer to load everything") since we're
loading-on-demand most stuff and it doesn't seem especially useful since
we'd prefer to minimise loading times (but feel free to correct me if
I'm wrong). (And because it crashes when things need to be initialised
in a different order, so it's easier to remove than to understand and
fix it.)
PatchRData, Renderer: Work sensibly when there's no game (hence no LOS
manager, water, etc).
LOSManager: Use entity position instead of actor position when possible.
TerritoryManager: Allow delayed recalculations (so Atlas can issue lots
of move+recalculate commands per frame).
Cinematic: Non-pointer wxTimer, so it doesn't leak and doesn't have to
be deleted manually.

This was SVN commit r4261.
2006-08-28 17:36:42 +00:00

367 lines
10 KiB
C++

/**
* =========================================================================
* File : win.cpp
* Project : 0 A.D.
* Description : various Windows-specific code and program entry point.
*
* @author Jan.Wassenberg@stud.uni-karlsruhe.de
* =========================================================================
*/
/*
* Copyright (c) 2003-2004 Jan Wassenberg
*
* Redistribution and/or modification are also permitted under the
* terms of the GNU General Public License as published by the
* Free Software Foundation (version 2 or later, at your option).
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "precompiled.h"
#include <stdio.h>
#include <stdlib.h> // __argc
#include "win_internal.h"
#include "lib/path_util.h"
#if MSC_VERSION >= 1400
#include <process.h> // __security_init_cookie
#define NEED_COOKIE_INIT
#endif
char win_sys_dir[MAX_PATH+1];
char win_exe_dir[MAX_PATH+1];
// only call after a Win32 function indicates failure.
static LibError LibError_from_GLE(bool warn_if_failed = true)
{
LibError err;
switch(GetLastError())
{
case ERROR_OUTOFMEMORY:
err = ERR_NO_MEM; break;
case ERROR_INVALID_PARAMETER:
err = ERR_INVALID_PARAM; break;
case ERROR_INSUFFICIENT_BUFFER:
err = ERR_BUF_SIZE; break;
case ERROR_ACCESS_DENIED:
err = ERR_FILE_ACCESS; break;
case ERROR_FILE_NOT_FOUND:
case ERROR_PATH_NOT_FOUND:
err = ERR_TNODE_NOT_FOUND; break;
default:
err = ERR_FAIL; break;
}
if(warn_if_failed)
DEBUG_WARN_ERR(err);
return err;
}
// return the LibError equivalent of GetLastError(), or ERR_FAIL if
// there's no equal.
// you should SetLastError(0) before calling whatever will set ret
// to make sure we do not return any stale errors.
LibError LibError_from_win32(DWORD ret, bool warn_if_failed)
{
if(ret != FALSE)
return INFO_OK;
return LibError_from_GLE(warn_if_failed);
}
//-----------------------------------------------------------------------------
//
// safe allocator that may be used independently of libc malloc
// (in particular, before _cinit and while calling static dtors).
// used by wpthread critical section code.
//
void* win_alloc(size_t size)
{
const DWORD flags = HEAP_ZERO_MEMORY;
return HeapAlloc(GetProcessHeap(), flags, size);
}
void win_free(void* p)
{
const DWORD flags = 0;
HeapFree(GetProcessHeap(), flags, p);
}
///////////////////////////////////////////////////////////////////////////////
//
// module init and shutdown mechanism
//
///////////////////////////////////////////////////////////////////////////////
// init and shutdown mechanism: register a function to be called at
// pre-libc init, pre-main init or shutdown.
//
// each module has the linker add a pointer to its init or shutdown
// function to a table (at a user-defined position).
// zero runtime overhead, and there's no need for a central dispatcher
// that knows about all the modules.
//
// disadvantage: requires compiler support (MS VC-specific).
//
// alternatives:
// - initialize via constructor. however, that would leave the problem of
// shutdown order and timepoint, which is also taken care of here.
// - register init/shutdown functions from a NLSO constructor:
// clunky, and setting order is more difficult.
// - on-demand initialization: complicated; don't know in what order
// things happen. also, no way to determine how long init takes.
//
// the "segment name" determines when and in what order the functions are
// called: "LIB$W{type}{group}", where {type} is C for pre-libc init,
// I for pre-main init, or T for terminators (last of the atexit handlers).
// {group} is [B, Y]; groups are called in alphabetical order, but
// call order within the group itself is unspecified.
//
// define the segment via #pragma data_seg(name), register any functions
// to be called via WIN_REGISTER_FUNC, and then restore the previous segment
// with #pragma data_seg() .
//
// note: group must be [B, Y]. data declared in groups A or Z may
// be placed beyond the table start/end by the linker, since the linker's
// ordering WRT other source files' data is undefined within a segment.
typedef LibError (*_PIFV)(void);
// pointers to start and end of function tables.
// note: COFF tosses out empty segments, so we have to put in one value
// (zero, because call_func_tbl has to ignore NULL entries anyway).
#pragma data_seg(WIN_CALLBACK_PRE_LIBC(a))
_PIFV pre_libc_begin[] = { 0 };
#pragma data_seg(WIN_CALLBACK_PRE_LIBC(z))
_PIFV pre_libc_end[] = { 0 };
#pragma data_seg(WIN_CALLBACK_PRE_MAIN(a))
_PIFV pre_main_begin[] = { 0 };
#pragma data_seg(WIN_CALLBACK_PRE_MAIN(z))
_PIFV pre_main_end[] = { 0 };
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(a))
_PIFV shutdown_begin[] = { 0 };
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(z))
_PIFV shutdown_end[] = { 0 };
#pragma data_seg()
#pragma comment(linker, "/merge:.LIB=.data")
// call all non-NULL function pointers in [begin, end).
// note: the range may be larger than expected due to section padding.
// that (and the COFF empty section problem) is why we need to ignore zeroes.
static void call_func_tbl(_PIFV* begin, _PIFV* end)
{
for(_PIFV* p = begin; p < end; p++)
if(*p)
(*p)();
}
//-----------------------------------------------------------------------------
// locking for win-specific code
//-----------------------------------------------------------------------------
// several init functions are before called before _cinit.
// POSIX static mutex init may not have been done by then,
// so we need our own lightweight functions.
static CRITICAL_SECTION cs[NUM_CS];
static bool cs_valid;
void win_lock(uint idx)
{
debug_assert(idx < NUM_CS && "win_lock: invalid critical section index");
if(cs_valid)
EnterCriticalSection(&cs[idx]);
}
void win_unlock(uint idx)
{
debug_assert(idx < NUM_CS && "win_unlock: invalid critical section index");
if(cs_valid)
LeaveCriticalSection(&cs[idx]);
}
int win_is_locked(uint idx)
{
debug_assert(idx < NUM_CS && "win_is_locked: invalid critical section index");
if(!cs_valid)
return -1;
BOOL got_it = TryEnterCriticalSection(&cs[idx]);
if(got_it)
LeaveCriticalSection(&cs[idx]);
return !got_it;
}
static void cs_init()
{
for(int i = 0; i < NUM_CS; i++)
InitializeCriticalSection(&cs[i]);
cs_valid = true;
}
static void cs_shutdown()
{
cs_valid = false;
for(int i = 0; i < NUM_CS; i++)
DeleteCriticalSection(&cs[i]);
memset(cs, 0, sizeof(cs));
}
//-----------------------------------------------------------------------------
// startup
//-----------------------------------------------------------------------------
// entry -> pre_libc -> WinMainCRTStartup -> WinMain -> pre_main -> main
// at_exit is called as the last of the atexit handlers
// (assuming, as documented in lib.cpp, constructors don't use atexit!)
//
// rationale: we need to gain control after _cinit and before main() to
// complete initialization.
// note: this way of getting control before main adds overhead
// (setting up the WinMain parameters), but is simpler and safer than
// SDL-style #define main SDL_main.
// explained where used.
static HMODULE hUser32Dll;
static void at_exit(void)
{
call_func_tbl(shutdown_begin, shutdown_end);
cs_shutdown();
// free the reference taken in win_pre_libc_init;
// this avoids Boundschecker warnings at exit.
FreeLibrary(hUser32Dll);
}
void win_pre_main_init()
{
// enable memory tracking and leak detection;
// no effect if !HAVE_VC_DEBUG_ALLOC.
#if CONFIG_PARANOIA
debug_heap_enable(DEBUG_HEAP_ALL);
#elif !defined(NDEBUG)
debug_heap_enable(DEBUG_HEAP_NORMAL);
#endif
call_func_tbl(pre_main_begin, pre_main_end);
atexit(at_exit);
// no point redirecting stdout yet - the current directory
// may be incorrect (file_set_root not yet called).
// (w)sdl will take care of it anyway.
}
// perform all initialization that needs to run before _cinit
// (which calls C++ ctors).
// be very careful to avoid non-stateless libc functions!
static inline void pre_libc_init()
{
// enable low-fragmentation heap
#if WINVER >= 0x0501
HMODULE hKernel32Dll = LoadLibrary("kernel32.dll");
if(hKernel32Dll)
{
BOOL (WINAPI* pHeapSetInformation)(HANDLE, HEAP_INFORMATION_CLASS, void*, size_t);
*(void**)&pHeapSetInformation = GetProcAddress(hKernel32Dll, "HeapSetInformation");
if(pHeapSetInformation)
{
ULONG flags = 2; // enable LFH
pHeapSetInformation(GetProcessHeap(), HeapCompatibilityInformation, &flags, sizeof(flags));
}
FreeLibrary(hKernel32Dll);
}
#endif // #if WINVER >= 0x0501
cs_init();
GetSystemDirectory(win_sys_dir, sizeof(win_sys_dir));
if(GetModuleFileName(GetModuleHandle(0), win_exe_dir, MAX_PATH) != 0)
path_strip_fn(win_exe_dir);
// HACK: make sure a reference to user32 is held, even if someone
// decides to delay-load it. this fixes bug #66, which was the
// Win32 mouse cursor (set via user32!SetCursor) appearing as a
// black 32x32(?) rectangle. underlying cause was as follows:
// powrprof.dll was the first client of user32, causing it to be
// loaded. after we were finished with powrprof, we freed it, in turn
// causing user32 to unload. later code would then reload user32,
// which apparently terminally confused the cursor implementation.
//
// since we hold a reference here, user32 will never unload.
// of course, the benefits of delay-loading are lost for this DLL,
// but that is unavoidable. it is safer to force loading it, rather
// than documenting the problem and asking it not be delay-loaded.
hUser32Dll = LoadLibrary("user32.dll");
call_func_tbl(pre_libc_begin, pre_libc_end);
}
static int SEH_wrapped_entry()
{
int ret;
__try
{
pre_libc_init();
#ifdef USE_WINMAIN
ret = WinMainCRTStartup(); // calls _cinit and then our main
#else
ret = mainCRTStartup(); // calls _cinit and then our main
#endif
}
__except(wdbg_exception_filter(GetExceptionInformation()))
{
ret = -1;
}
return ret;
}
int entry()
{
#ifdef NEED_COOKIE_INIT
// 2006-02-16 workaround for R6035 on VC8:
//
// SEH code compiled with /GS pushes a "security cookie" onto the
// stack. since we're called before _cinit, the cookie won't have
// been initialized yet, which would cause the CRT to FatalAppExit.
// to solve this, we must call __security_init_cookie before any
// hidden compiler-generated SEH registration code runs,
// which means the __try block must be moved into a helper function.
//
// NB: entry() must not contain local string buffers, either -
// /GS would install a cookie here as well (same problem).
//
// see http://msdn2.microsoft.com/en-US/library/ms235603.aspx
__security_init_cookie();
#endif
return SEH_wrapped_entry();
}