392 lines
9.9 KiB
C++
392 lines
9.9 KiB
C++
/**
|
|
@file debugAssert.cpp
|
|
|
|
Windows implementation of assertion routines.
|
|
|
|
@maintainer Morgan McGuire, graphics3d.com
|
|
|
|
@created 2001-08-26
|
|
@edited 2009-06-02
|
|
*/
|
|
|
|
#include "G3D/debugAssert.h"
|
|
#include "G3D/platform.h"
|
|
#ifdef G3D_WINDOWS
|
|
#include <tchar.h>
|
|
#endif
|
|
#include "G3D/format.h"
|
|
#include "G3D/prompt.h"
|
|
#include <string>
|
|
#include "G3D/debugPrintf.h"
|
|
#include "G3D/Log.h"
|
|
|
|
#include <cstdlib>
|
|
|
|
#ifdef _MSC_VER
|
|
// disable: "C++ exception handler used"
|
|
# pragma warning (push)
|
|
# pragma warning (disable : 4530)
|
|
#endif
|
|
|
|
using namespace std;
|
|
|
|
namespace G3D { namespace _internal {
|
|
|
|
ConsolePrintHook _consolePrintHook;
|
|
AssertionHook _debugHook = _handleDebugAssert_;
|
|
AssertionHook _failureHook = _handleErrorCheck_;
|
|
|
|
#ifdef G3D_LINUX
|
|
#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
|
|
Display* x11Display = NULL;
|
|
Window x11Window = 0;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#ifdef G3D_WINDOWS
|
|
static void postToClipboard(const char *text) {
|
|
if (OpenClipboard(NULL)) {
|
|
HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, strlen(text) + 1);
|
|
if (hMem) {
|
|
char *pMem = (char*)GlobalLock(hMem);
|
|
strcpy(pMem, text);
|
|
GlobalUnlock(hMem);
|
|
|
|
EmptyClipboard();
|
|
SetClipboardData(CF_TEXT, hMem);
|
|
}
|
|
|
|
CloseClipboard();
|
|
GlobalFree(hMem);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
outTitle should be set before the call
|
|
*/
|
|
static void createErrorMessage(
|
|
const char* expression,
|
|
const std::string& message,
|
|
const char* filename,
|
|
int lineNumber,
|
|
std::string& outTitle,
|
|
std::string& outMessage) {
|
|
|
|
std::string le = "";
|
|
const char* newline = "\n";
|
|
|
|
#ifdef G3D_WINDOWS
|
|
newline = "\r\n";
|
|
|
|
// The last error value. (Which is preserved across the call).
|
|
DWORD lastErr = GetLastError();
|
|
|
|
// The decoded message from FormatMessage
|
|
LPTSTR formatMsg = NULL;
|
|
|
|
if (NULL == formatMsg) {
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
lastErr,
|
|
0,
|
|
(LPTSTR)&formatMsg,
|
|
0,
|
|
NULL);
|
|
}
|
|
|
|
// Make sure the message got translated into something.
|
|
LPTSTR realLastErr;
|
|
if (NULL != formatMsg) {
|
|
realLastErr = formatMsg;
|
|
} else {
|
|
realLastErr = LPTSTR(_T("Last error code does not exist."));
|
|
}
|
|
|
|
if (lastErr != 0) {
|
|
le = G3D::format("Last Error (0x%08X): %s\r\n\r\n", lastErr, (LPCSTR)realLastErr);
|
|
}
|
|
|
|
// Get rid of the allocated memory from FormatMessage.
|
|
if (NULL != formatMsg) {
|
|
LocalFree((LPVOID)formatMsg);
|
|
}
|
|
|
|
char modulePath[MAX_PATH];
|
|
GetModuleFileNameA(NULL, modulePath, MAX_PATH);
|
|
|
|
const char* moduleName = strrchr(modulePath, '\\');
|
|
outTitle = outTitle + string(" - ") + string(moduleName ? (moduleName + 1) : modulePath);
|
|
|
|
#else
|
|
(void)outTitle;
|
|
#endif
|
|
|
|
// Build the message.
|
|
outMessage =
|
|
G3D::format("%s%s%sExpression: %s%s%s:%d%s%s%s",
|
|
message.c_str(), newline, newline, expression, newline,
|
|
filename, lineNumber, newline, newline, le.c_str());
|
|
}
|
|
|
|
|
|
bool _handleDebugAssert_(
|
|
const char* expression,
|
|
const std::string& message,
|
|
const char* filename,
|
|
int lineNumber,
|
|
bool useGuiPrompt) {
|
|
|
|
std::string dialogTitle = "Assertion Failure";
|
|
std::string dialogText = "";
|
|
createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
|
|
|
|
#ifdef G3D_WINDOWS
|
|
DWORD lastErr = GetLastError();
|
|
postToClipboard(dialogText.c_str());
|
|
debugPrintf("\n%s\n", dialogText.c_str());
|
|
#endif
|
|
|
|
const int cBreak = 0;
|
|
const int cIgnore = 1;
|
|
const int cAbort = 2;
|
|
|
|
static const char* choices[] = {"Debug", "Ignore", "Exit"};
|
|
|
|
// Log the error
|
|
Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
|
|
|
|
int result = G3D::prompt(dialogTitle.c_str(), dialogText.c_str(), (const char**)choices, 3, useGuiPrompt);
|
|
|
|
# ifdef G3D_WINDOWS
|
|
// Put the incoming last error back.
|
|
SetLastError(lastErr);
|
|
# endif
|
|
|
|
switch (result) {
|
|
// -1 shouldn't actually occur because it means
|
|
// that we're in release mode.
|
|
case -1:
|
|
case cBreak:
|
|
return true;
|
|
break;
|
|
|
|
case cIgnore:
|
|
return false;
|
|
break;
|
|
|
|
case cAbort:
|
|
exit(-1);
|
|
break;
|
|
}
|
|
|
|
// Should never get here
|
|
return false;
|
|
}
|
|
|
|
|
|
bool _handleErrorCheck_(
|
|
const char* expression,
|
|
const std::string& message,
|
|
const char* filename,
|
|
int lineNumber,
|
|
bool useGuiPrompt) {
|
|
|
|
std::string dialogTitle = "Critical Error";
|
|
std::string dialogText = "";
|
|
|
|
createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText);
|
|
|
|
// Log the error
|
|
Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText);
|
|
#ifdef G3D_WINDOWS
|
|
DWORD lastErr = GetLastError();
|
|
(void)lastErr;
|
|
postToClipboard(dialogText.c_str());
|
|
debugPrintf("\n%s\n", dialogText.c_str());
|
|
#endif
|
|
|
|
static const char* choices[] = {"Ok"};
|
|
|
|
const std::string& m =
|
|
std::string("An internal error has occured in this program and it will now close. "
|
|
"The specific error is below. More information has been saved in \"") +
|
|
Log::getCommonLogFilename() + "\".\n\n" + dialogText;
|
|
|
|
int result = G3D::prompt("Error", m.c_str(), (const char**)choices, 1, useGuiPrompt);
|
|
(void)result;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#ifdef G3D_WINDOWS
|
|
static HCURSOR oldCursor;
|
|
static RECT oldCursorRect;
|
|
static POINT oldCursorPos;
|
|
static int oldShowCursorCount;
|
|
#endif
|
|
|
|
void _releaseInputGrab_() {
|
|
#ifdef G3D_WINDOWS
|
|
|
|
GetCursorPos(&oldCursorPos);
|
|
|
|
// Stop hiding the cursor if the application hid it.
|
|
oldShowCursorCount = ShowCursor(true) - 1;
|
|
|
|
if (oldShowCursorCount < -1) {
|
|
for (int c = oldShowCursorCount; c < -1; ++c) {
|
|
ShowCursor(true);
|
|
}
|
|
}
|
|
|
|
// Set the default cursor in case the application
|
|
// set the cursor to NULL.
|
|
oldCursor = GetCursor();
|
|
SetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
// Allow the cursor full access to the screen
|
|
GetClipCursor(&oldCursorRect);
|
|
ClipCursor(NULL);
|
|
|
|
#elif defined(G3D_LINUX)
|
|
#if 0 /* G3DFIX: Disabled to avoid requirement for X11 libraries */
|
|
if (x11Display != NULL) {
|
|
XUngrabPointer(x11Display, CurrentTime);
|
|
XUngrabKeyboard(x11Display, CurrentTime);
|
|
if (x11Window != 0) {
|
|
//XUndefineCursor(x11Display, x11Window);
|
|
// TODO: Note that we leak this cursor; it should be
|
|
// freed in the restore code.
|
|
Cursor c = XCreateFontCursor(x11Display, 68);
|
|
XDefineCursor(x11Display, x11Window, c);
|
|
}
|
|
XSync(x11Display, false);
|
|
XAllowEvents(x11Display, AsyncPointer, CurrentTime);
|
|
XFlush(x11Display);
|
|
}
|
|
#endif
|
|
#elif defined(G3D_OSX)
|
|
// TODO: OS X
|
|
#endif
|
|
}
|
|
|
|
|
|
void _restoreInputGrab_() {
|
|
#ifdef G3D_WINDOWS
|
|
|
|
// Restore the old clipping region
|
|
ClipCursor(&oldCursorRect);
|
|
|
|
SetCursorPos(oldCursorPos.x, oldCursorPos.y);
|
|
|
|
// Restore the old cursor
|
|
SetCursor(oldCursor);
|
|
|
|
// Restore old visibility count
|
|
if (oldShowCursorCount < 0) {
|
|
for (int c = 0; c > oldShowCursorCount; --c) {
|
|
ShowCursor(false);
|
|
}
|
|
}
|
|
|
|
#elif defined(G3D_LINUX)
|
|
// TODO: Linux
|
|
#elif defined(G3D_OSX)
|
|
// TODO: OS X
|
|
#endif
|
|
}
|
|
|
|
|
|
}; // internal namespace
|
|
|
|
void setAssertionHook(AssertionHook hook) {
|
|
G3D::_internal::_debugHook = hook;
|
|
}
|
|
|
|
AssertionHook assertionHook() {
|
|
return G3D::_internal::_debugHook;
|
|
}
|
|
|
|
void setFailureHook(AssertionHook hook) {
|
|
G3D::_internal::_failureHook = hook;
|
|
}
|
|
|
|
AssertionHook failureHook() {
|
|
return G3D::_internal::_failureHook;
|
|
}
|
|
|
|
|
|
void setConsolePrintHook(ConsolePrintHook h) {
|
|
G3D::_internal::_consolePrintHook = h;
|
|
}
|
|
|
|
ConsolePrintHook consolePrintHook() {
|
|
return G3D::_internal::_consolePrintHook;
|
|
}
|
|
|
|
|
|
std::string __cdecl debugPrint(const std::string& s) {
|
|
# ifdef G3D_WINDOWS
|
|
const int MAX_STRING_LEN = 1024;
|
|
|
|
// Windows can't handle really long strings sent to
|
|
// the console, so we break the string.
|
|
if (s.size() < MAX_STRING_LEN) {
|
|
OutputDebugStringA(s.c_str());
|
|
} else {
|
|
for (unsigned int i = 0; i < s.size(); i += MAX_STRING_LEN) {
|
|
std::string sub = s.substr(i, MAX_STRING_LEN);
|
|
OutputDebugStringA(sub.c_str());
|
|
}
|
|
}
|
|
# else
|
|
fprintf(stderr, "%s", s.c_str());
|
|
fflush(stderr);
|
|
# endif
|
|
|
|
return s;
|
|
}
|
|
|
|
std::string __cdecl debugPrintf(const char* fmt ...) {
|
|
va_list argList;
|
|
va_start(argList, fmt);
|
|
std::string s = G3D::vformat(fmt, argList);
|
|
va_end(argList);
|
|
|
|
return debugPrint(s);
|
|
// return debugPrint(consolePrint(s));
|
|
}
|
|
|
|
std::string consolePrint(const std::string& s) {
|
|
FILE* L = Log::common()->getFile();
|
|
fprintf(L, "%s", s.c_str());
|
|
|
|
if (consolePrintHook()) {
|
|
consolePrintHook()(s);
|
|
}
|
|
|
|
fflush(L);
|
|
return s;
|
|
}
|
|
|
|
|
|
std::string __cdecl consolePrintf(const char* fmt ...) {
|
|
va_list argList;
|
|
va_start(argList, fmt);
|
|
std::string s = G3D::vformat(fmt, argList);
|
|
va_end(argList);
|
|
|
|
return consolePrint(s);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
#ifdef _MSC_VER
|
|
# pragma warning (pop)
|
|
#endif
|