forked from 0ad/0ad
- sysdep: moved posix versions of display_msg etc. to new unix.cpp
- add new portable display_error (and convenience macro DISPLAY_ERROR, if advanced params aren't needed; don't want to break C compat). this is the old assert dialog and more; allows ErrorReaction (see sysdep.h) to be carried out according to users wishes. - add win_is_locked for deadlock avoidance This was SVN commit r2420.
This commit is contained in:
parent
0a0699c212
commit
28e50ab752
@ -93,7 +93,7 @@ static void cat_atow(FILE* out, const char* in_filename)
|
||||
}
|
||||
|
||||
|
||||
int debug_write_crashlog(const wchar_t* description, const wchar_t* locus, const wchar_t* stack_trace)
|
||||
int debug_write_crashlog(const wchar_t* text)
|
||||
{
|
||||
const wchar_t divider[] = L"\n\n====================================\n\n";
|
||||
#define WRITE_DIVIDER fwprintf(f, divider);
|
||||
@ -104,9 +104,7 @@ int debug_write_crashlog(const wchar_t* description, const wchar_t* locus, const
|
||||
|
||||
fputwc(0xfeff, f); // BOM
|
||||
|
||||
fwprintf(f, L"Unhandled exception: %s.\n", description);
|
||||
fwprintf(f, L"Location: %s\n", locus);
|
||||
fwprintf(f, L"Stack trace: %s\n", stack_trace);
|
||||
fwprintf(f, L"%s\n", text);
|
||||
WRITE_DIVIDER
|
||||
|
||||
|
||||
@ -450,3 +448,83 @@ const char* debug_get_symbol_string(void* symbol, const char* name, const char*
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ErrorReaction display_error(const wchar_t* description, int flags,
|
||||
uint skip, void* context, const char* file, int line)
|
||||
{
|
||||
if(!file || file[0] == '\0')
|
||||
file = "unknown";
|
||||
if(line <= 0)
|
||||
line = 0;
|
||||
|
||||
// display in output window; double-click will navigate to error location.
|
||||
char* slash = strrchr(file, DIR_SEP);
|
||||
const char* filename = slash? slash+1 : file;
|
||||
debug_wprintf(L"%hs(%d): %s\n", filename, line, description);
|
||||
|
||||
wchar_t* text;
|
||||
const size_t MAX_CHARS = 64*1024;
|
||||
void* mem = malloc(MAX_CHARS*sizeof(wchar_t));
|
||||
if(mem)
|
||||
{
|
||||
text = (wchar_t*)mem;
|
||||
static const wchar_t fmt[] = L"%s\r\n\r\nCall stack:\r\n\r\n";
|
||||
int len = swprintf(text, MAX_CHARS, fmt, description);
|
||||
|
||||
debug_dump_stack(text+len, MAX_CHARS-len, skip+1, context);
|
||||
// in-place
|
||||
}
|
||||
else
|
||||
text = L"(insufficient memory to display error message)";
|
||||
|
||||
debug_write_crashlog(text);
|
||||
ErrorReaction er = display_error_impl(text, flags);
|
||||
|
||||
// note: debug_break-ing here to make sure the app doesn't continue
|
||||
// running is no longer necessary. display_error now determines our
|
||||
// window handle and is modal.
|
||||
|
||||
// handle "break" request unless the caller wants to (doing so here
|
||||
// instead of within the dlgproc helps makes debugging easier)
|
||||
if(er == ER_BREAK && !(flags & DE_MANUAL_BREAK))
|
||||
{
|
||||
debug_break();
|
||||
er = ER_CONTINUE;
|
||||
}
|
||||
|
||||
free(mem);
|
||||
// after debug_break to ease debugging, but before exit to avoid leak.
|
||||
|
||||
// exit requested. do so here to disburden callers.
|
||||
if(er == ER_EXIT)
|
||||
{
|
||||
// disable memory-leak reporting to avoid a flood of warnings
|
||||
// (lots of stuff will leak since we exit abnormally).
|
||||
debug_disable_leak_reporting();
|
||||
#ifdef HAVE_MMGR
|
||||
mmgr_set_options(0);
|
||||
#endif
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return er;
|
||||
}
|
||||
|
||||
|
||||
// notify the user that an assertion failed; displays a stack trace with
|
||||
// local variables.
|
||||
ErrorReaction debug_assert_failed(const char* file, int line, const char* expr)
|
||||
{
|
||||
uint skip = 1; void* context = 0;
|
||||
wchar_t buf[200];
|
||||
swprintf(buf, ARRAY_SIZE(buf), L"Assertion failed in %hs, line %d: \"%hs\"", file, line, expr);
|
||||
return display_error(buf, DE_ALLOW_SUPPRESS|DE_MANUAL_BREAK, skip, context, file, line);
|
||||
}
|
@ -18,6 +18,7 @@
|
||||
#ifndef DEBUG_H_INCLUDED
|
||||
#define DEBUG_H_INCLUDED
|
||||
|
||||
#include "sysdep.h" // ErrorReaction
|
||||
#ifdef _WIN32
|
||||
# include "win/wdbg.h"
|
||||
#else
|
||||
@ -29,26 +30,7 @@
|
||||
// errors are reported by the CRT, e.g. via assert.
|
||||
extern void debug_check_heap(void);
|
||||
|
||||
|
||||
// user choices in the assert/unhandled exception dialog.
|
||||
enum ErrorReaction
|
||||
{
|
||||
// ignore, continue as if nothing happened.
|
||||
ER_CONTINUE = 1,
|
||||
// note: don't start at 0 because that is interpreted as a
|
||||
// DialogBoxParam failure.
|
||||
|
||||
// ignore and do not report again. only works with assert2.
|
||||
ER_SUPPRESS,
|
||||
// note: non-persistent; only applicable during this program run.
|
||||
|
||||
// trigger breakpoint, i.e. enter debugger.
|
||||
ER_BREAK,
|
||||
|
||||
// exit the program immediately.
|
||||
ER_EXIT,
|
||||
// note: never returned; carried out immediately to disburden callers.
|
||||
};
|
||||
extern void debug_disable_leak_reporting();
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -59,7 +41,7 @@ enum ErrorReaction
|
||||
// notify the user that an assertion failed; displays a
|
||||
// stack trace with local variables.
|
||||
// returns one of UserErrorReaction.
|
||||
extern ErrorReaction debug_assert_failed(const char* source_file, int line, const char* assert_expr);
|
||||
extern enum ErrorReaction debug_assert_failed(const char* source_file, int line, const char* assert_expr);
|
||||
|
||||
// recommended use: assert2(expr && "descriptive string")
|
||||
#define assert2(expr)\
|
||||
@ -85,6 +67,7 @@ STMT(\
|
||||
|
||||
// write to the debugger output window (may take ~1 ms!)
|
||||
extern void debug_printf(const char* fmt, ...);
|
||||
extern void debug_wprintf(const wchar_t* fmt, ...);
|
||||
|
||||
// write to memory buffer (fast)
|
||||
// used for "last activity" reporting in the crashlog.
|
||||
@ -94,7 +77,7 @@ extern void debug_wprintf_mem(const wchar_t* fmt, ...);
|
||||
#define debug_warn(str) assert2(0 && (str))
|
||||
|
||||
// TODO
|
||||
extern int debug_write_crashlog(const wchar_t* description, const wchar_t* locus, const wchar_t* stack_trace);
|
||||
extern int debug_write_crashlog(const wchar_t* text);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
@ -154,5 +137,6 @@ extern void* debug_get_nth_caller(uint n);
|
||||
|
||||
extern int debug_resolve_symbol(void* ptr_of_interest, char* sym_name, char* file, int* line);
|
||||
|
||||
extern const wchar_t* debug_dump_stack(wchar_t* buf, size_t max_chars, uint skip, void* context);
|
||||
|
||||
#endif // #ifndef DEBUG_H_INCLUDED
|
||||
|
@ -5,39 +5,6 @@
|
||||
#include <memory.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
// portable debug output routines. Win32 offers better versions, which
|
||||
// override these.
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
|
||||
// misc. portable versions (win.cpp overrides these)
|
||||
|
||||
void display_msg(const char* caption, const char* msg)
|
||||
{
|
||||
fprintf(stderr, "%s: %s\n", caption, msg);
|
||||
}
|
||||
|
||||
void wdisplay_msg(const wchar_t* caption, const wchar_t* msg)
|
||||
{
|
||||
fwprintf(stderr, L"%ls: %ls\n", caption, msg);
|
||||
}
|
||||
|
||||
|
||||
int get_executable_name(char* n_path, size_t buf_size)
|
||||
{
|
||||
UNUSED(n_path);
|
||||
UNUSED(buf_size);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#endif // #ifndef _WIN32
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
||||
double round(double x)
|
||||
|
@ -1,5 +1,5 @@
|
||||
#ifndef SYSDEP_H__
|
||||
#define SYSDEP_H__
|
||||
#ifndef SYSDEP_H_INCLUDED
|
||||
#define SYSDEP_H_INCLUDED
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@ -12,6 +12,10 @@
|
||||
#include "unix/unix.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// vsnprintf2: handles positional parameters and %lld.
|
||||
// already available on *nix, emulated on Win32.
|
||||
@ -21,11 +25,47 @@ extern int vsnprintf2(char* buffer, size_t count, const char* format, va_list ar
|
||||
#define vsnprintf2 vsnprintf
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
enum DisplayErrorFlags
|
||||
{
|
||||
DE_ALLOW_SUPPRESS = 1,
|
||||
DE_NO_CONTINUE = 2,
|
||||
DE_MANUAL_BREAK = 4
|
||||
};
|
||||
|
||||
// user choices in the assert/unhandled exception dialog.
|
||||
enum ErrorReaction
|
||||
{
|
||||
// ignore, continue as if nothing happened.
|
||||
ER_CONTINUE = 1,
|
||||
// note: don't start at 0 because that is interpreted as a
|
||||
// DialogBoxParam failure.
|
||||
|
||||
// ignore and do not report again.
|
||||
// only returned if DE_ALLOW_SUPPRESS was passed.
|
||||
ER_SUPPRESS,
|
||||
// note: non-persistent; only applicable during this program run.
|
||||
|
||||
// trigger breakpoint, i.e. enter debugger.
|
||||
// only returned if DE_MANUAL_BREAK was passed; otherwise,
|
||||
// display_error will trigger a breakpoint itself.
|
||||
ER_BREAK,
|
||||
|
||||
// exit the program immediately.
|
||||
// never returned; display_error exits immediately.
|
||||
ER_EXIT
|
||||
};
|
||||
|
||||
|
||||
extern ErrorReaction display_error_impl(const wchar_t* text, int flags);
|
||||
|
||||
#define DISPLAY_ERROR(text) display_error(text, 0, 0, 0, __FILE__, __LINE__)
|
||||
|
||||
extern ErrorReaction display_error(const wchar_t* text, int flags,
|
||||
uint skip, void* context, const char* file, int line);
|
||||
|
||||
extern void display_msg(const char* caption, const char* msg);
|
||||
extern void wdisplay_msg(const wchar_t* caption, const wchar_t* msg);
|
||||
|
||||
@ -107,4 +147,4 @@ namespace __gnu_cxx
|
||||
#endif // !__GNUC__
|
||||
|
||||
|
||||
#endif // #ifndef SYSDEP_H__
|
||||
#endif // #ifndef SYSDEP_H_INCLUDED
|
||||
|
@ -7,4 +7,6 @@
|
||||
|
||||
extern void unix_debug_break(void);
|
||||
|
||||
extern void udbg_launch_debugger();
|
||||
|
||||
#endif // #ifndef UDBG_H__
|
||||
|
80
source/lib/sysdep/unix/unix.cpp
Normal file
80
source/lib/sysdep/unix/unix.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "udbg.h"
|
||||
|
||||
// these are basic POSIX-compatible backends for the sysdep.h functions.
|
||||
// Win32 has better versions which override these.
|
||||
|
||||
void display_msg(const char* caption, const char* msg)
|
||||
{
|
||||
fprintf(stderr, "%s: %s\n", caption, msg);
|
||||
}
|
||||
|
||||
void wdisplay_msg(const wchar_t* caption, const wchar_t* msg)
|
||||
{
|
||||
fwprintf(stderr, L"%ls: %ls\n", caption, msg);
|
||||
}
|
||||
|
||||
|
||||
int get_executable_name(char* n_path, size_t buf_size)
|
||||
{
|
||||
UNUSED(n_path);
|
||||
UNUSED(buf_size);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
|
||||
ErrorReaction display_error_impl(const wchar_t* text, int flags)
|
||||
{
|
||||
wprintf("%s\n", text);
|
||||
|
||||
const bool manual_break = flags & DE_MANUAL_BREAK;
|
||||
const bool allow_suppress = flags & DE_ALLOW_SUPPRESS;
|
||||
const bool no_continue = flags & DE_NO_CONTINUE;
|
||||
|
||||
// until valid input given:
|
||||
for(;;)
|
||||
{
|
||||
if(!no_continue)
|
||||
printf("(C)ontinue, ");
|
||||
if(allow_suppress)
|
||||
printf("(S)uppress, ");
|
||||
printf("(B)reak, Launch (D)ebugger, or (E)xit?\n");
|
||||
// TODO Should have some kind of timeout here.. in case you're unable to
|
||||
// access the controlling terminal (As might be the case if launched
|
||||
// from an xterm and in full-screen mode)
|
||||
int c = getchar();
|
||||
// note: don't use tolower because it'll choke on EOF
|
||||
switch(c)
|
||||
{
|
||||
case EOF:
|
||||
case 'd': case 'D':
|
||||
udbg_launch_debugger();
|
||||
//-fallthrough
|
||||
|
||||
case 'b': case 'B':
|
||||
if(manual_break)
|
||||
return ER_BREAK;
|
||||
debug_break();
|
||||
return ER_CONTINUE;
|
||||
|
||||
case 'c': case 'C':
|
||||
if(!no_continue)
|
||||
return ER_CONTINUE;
|
||||
// continue isn't allowed, so this was invalid input. loop again.
|
||||
break;
|
||||
case 's': case 'S':
|
||||
if(allow_suppress)
|
||||
return ER_SUPPRESS;
|
||||
// suppress isn't allowed, so this was invalid input. loop again.
|
||||
break;
|
||||
|
||||
case 'e': case 'E':
|
||||
abort();
|
||||
return ER_EXIT; // placebo; never reached
|
||||
}
|
||||
}
|
||||
}
|
@ -17,30 +17,82 @@
|
||||
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "lib.h"
|
||||
#include "posix.h"
|
||||
#include "win_internal.h"
|
||||
|
||||
#include <crtdbg.h> // malloc debug
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // __argc
|
||||
|
||||
#include "win_internal.h" // includes windows.h; must come before these
|
||||
#include <crtdbg.h> // malloc debug
|
||||
#include <malloc.h>
|
||||
#include <shlobj.h> // pick_dir
|
||||
|
||||
#include "lib.h"
|
||||
#include "posix.h"
|
||||
#include "error_dialog.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "shell32.lib") // for pick_directory SH* calls
|
||||
#endif
|
||||
|
||||
|
||||
void sle(int x)
|
||||
{
|
||||
SetLastError((DWORD)x);
|
||||
}
|
||||
|
||||
|
||||
#ifdef HAVE_DEBUGALLOC
|
||||
// Enable heap corruption checking after every allocation. Has the same
|
||||
// effect as PARANOIA in pre_main_init, but lets you switch it on anywhere
|
||||
// so that you can skip checking the whole of the initialisation code.
|
||||
// The debugger will break in the allocation just after the one that
|
||||
// corrupted the heap, so check its ID and then _CrtSetBreakAlloc(...)
|
||||
// on the previous one and try again.
|
||||
// Warning: This makes things rather slow.
|
||||
void memory_debug_extreme_turbo_plus()
|
||||
{
|
||||
_CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_DELAY_FREE_MEM_DF );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// we need to know the app's main window for the error dialog, so that
|
||||
// it is modal and actually stops the app. if it keeps running while
|
||||
// we're reporting an error, it'll probably crash and take down the
|
||||
// error window before it is seen (since we're in the same process).
|
||||
|
||||
static BOOL CALLBACK is_this_our_window(HWND hWnd, LPARAM lParam)
|
||||
{
|
||||
DWORD pid;
|
||||
DWORD tid = GetWindowThreadProcessId(hWnd, &pid);
|
||||
UNUSED(tid); // the function can't fail
|
||||
|
||||
DWORD our_pid = GetProcessId(GetCurrentProcess());
|
||||
if(pid == GetProcessId(GetCurrentProcess()))
|
||||
{
|
||||
*(HWND*)lParam = hWnd;
|
||||
return FALSE; // done
|
||||
}
|
||||
|
||||
return TRUE; // keep calling
|
||||
}
|
||||
|
||||
|
||||
// try to determine the app's main window by enumerating all
|
||||
// top-level windows and comparing their PIDs.
|
||||
// returns 0 if not found, e.g. if the app doesn't have one yet.
|
||||
HWND win_get_app_main_window()
|
||||
{
|
||||
HWND our_window = 0;
|
||||
DWORD ret = EnumWindows(is_this_our_window, (LPARAM)&our_window);
|
||||
UNUSED(ret);
|
||||
// the callback returns FALSE when it has found the window
|
||||
// (so as not to waste time); EnumWindows then returns 0.
|
||||
// therefore, we can't check this; just return our_window.
|
||||
return our_window;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// safe allocator that may be used independently of libc malloc
|
||||
// (in particular, before _cinit and while calling static dtors).
|
||||
@ -71,12 +123,12 @@ char win_exe_dir[MAX_PATH+1];
|
||||
|
||||
void display_msg(const char* caption, const char* msg)
|
||||
{
|
||||
MessageBoxA(0, msg, caption, MB_ICONEXCLAMATION);
|
||||
MessageBoxA(0, msg, caption, MB_ICONEXCLAMATION|MB_TASKMODAL|MB_SETFOREGROUND);
|
||||
}
|
||||
|
||||
void wdisplay_msg(const wchar_t* caption, const wchar_t* msg)
|
||||
{
|
||||
MessageBoxW(0, msg, caption, MB_ICONEXCLAMATION);
|
||||
MessageBoxW(0, msg, caption, MB_ICONEXCLAMATION|MB_TASKMODAL|MB_SETFOREGROUND);
|
||||
}
|
||||
|
||||
|
||||
@ -148,6 +200,217 @@ int pick_directory(char* path, size_t buf_size)
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// "program error" dialog (triggered by assert and exception)
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//
|
||||
// support for resizing the dialog / its controls
|
||||
// (have to do this manually - grr)
|
||||
//
|
||||
|
||||
static POINTS dlg_client_origin;
|
||||
static POINTS dlg_prev_client_size;
|
||||
|
||||
const int ANCHOR_LEFT = 0x01;
|
||||
const int ANCHOR_RIGHT = 0x02;
|
||||
const int ANCHOR_TOP = 0x04;
|
||||
const int ANCHOR_BOTTOM = 0x08;
|
||||
const int ANCHOR_ALL = 0x0f;
|
||||
|
||||
static void dlg_resize_control(HWND hDlg, int dlg_item, int dx,int dy, int anchors)
|
||||
{
|
||||
HWND hControl = GetDlgItem(hDlg, dlg_item);
|
||||
RECT r;
|
||||
GetWindowRect(hControl, &r);
|
||||
|
||||
int w = r.right - r.left, h = r.bottom - r.top;
|
||||
int x = r.left - dlg_client_origin.x, y = r.top - dlg_client_origin.y;
|
||||
|
||||
if(anchors & ANCHOR_RIGHT)
|
||||
{
|
||||
// right only
|
||||
if(!(anchors & ANCHOR_LEFT))
|
||||
x += dx;
|
||||
// horizontal (stretch width)
|
||||
else
|
||||
w += dx;
|
||||
}
|
||||
|
||||
if(anchors & ANCHOR_BOTTOM)
|
||||
{
|
||||
// bottom only
|
||||
if(!(anchors & ANCHOR_TOP))
|
||||
y += dy;
|
||||
// vertical (stretch height)
|
||||
else
|
||||
h += dy;
|
||||
}
|
||||
|
||||
SetWindowPos(hControl, 0, x,y, w,h, SWP_NOZORDER);
|
||||
}
|
||||
|
||||
|
||||
static void dlg_resize(HWND hDlg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
// 'minimize' was clicked. we need to ignore this, otherwise
|
||||
// dx/dy would reduce some control positions to less than 0.
|
||||
// since Windows clips them, we wouldn't later be able to
|
||||
// reconstruct the previous values when 'restoring'.
|
||||
if(wParam == SIZE_MINIMIZED)
|
||||
return;
|
||||
|
||||
// first call for this dialog instance. WM_MOVE hasn't been sent yet,
|
||||
// so dlg_client_origin are invalid => must not call resize_control().
|
||||
// we need to set dlg_prev_client_size for the next call before exiting.
|
||||
bool first_call = (dlg_prev_client_size.y == 0);
|
||||
|
||||
POINTS dlg_client_size = MAKEPOINTS(lParam);
|
||||
int dx = dlg_client_size.x - dlg_prev_client_size.x;
|
||||
int dy = dlg_client_size.y - dlg_prev_client_size.y;
|
||||
dlg_prev_client_size = dlg_client_size;
|
||||
|
||||
if(first_call)
|
||||
return;
|
||||
|
||||
dlg_resize_control(hDlg, IDC_CONTINUE, dx,dy, ANCHOR_LEFT|ANCHOR_BOTTOM);
|
||||
dlg_resize_control(hDlg, IDC_SUPPRESS, dx,dy, ANCHOR_LEFT|ANCHOR_BOTTOM);
|
||||
dlg_resize_control(hDlg, IDC_BREAK , dx,dy, ANCHOR_LEFT|ANCHOR_BOTTOM);
|
||||
dlg_resize_control(hDlg, IDC_EXIT , dx,dy, ANCHOR_LEFT|ANCHOR_BOTTOM);
|
||||
dlg_resize_control(hDlg, IDC_COPY , dx,dy, ANCHOR_RIGHT|ANCHOR_BOTTOM);
|
||||
dlg_resize_control(hDlg, IDC_EDIT1 , dx,dy, ANCHOR_ALL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct DialogParams
|
||||
{
|
||||
const wchar_t* text;
|
||||
int flags;
|
||||
};
|
||||
|
||||
|
||||
static int CALLBACK error_dialog_proc(HWND hDlg, unsigned int msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch(msg)
|
||||
{
|
||||
case WM_INITDIALOG:
|
||||
{
|
||||
const DialogParams* params = (const DialogParams*)lParam;
|
||||
|
||||
// need to reset for new instance of dialog
|
||||
dlg_client_origin.x = dlg_client_origin.y = 0;
|
||||
dlg_prev_client_size.x = dlg_prev_client_size.y = 0;
|
||||
|
||||
if(!(params->flags & DE_ALLOW_SUPPRESS))
|
||||
{
|
||||
HWND h = GetDlgItem(hDlg, IDC_SUPPRESS);
|
||||
EnableWindow(h, FALSE);
|
||||
}
|
||||
|
||||
SetDlgItemTextW(hDlg, IDC_EDIT1, params->text);
|
||||
return TRUE; // set default keyboard focus
|
||||
}
|
||||
|
||||
case WM_SYSCOMMAND:
|
||||
// close dialog if [X] is clicked (doesn't happen automatically)
|
||||
// note: lower 4 bits are reserved
|
||||
if((wParam & 0xFFF0) == SC_CLOSE)
|
||||
{
|
||||
EndDialog(hDlg, 0);
|
||||
return 0; // processed
|
||||
}
|
||||
break;
|
||||
|
||||
// return 0 if processed, otherwise break
|
||||
case WM_COMMAND:
|
||||
switch(wParam)
|
||||
{
|
||||
case IDC_COPY:
|
||||
{
|
||||
const size_t max_chars = 100000;
|
||||
wchar_t* buf = (wchar_t*)malloc(max_chars*sizeof(wchar_t));
|
||||
if(buf)
|
||||
{
|
||||
GetDlgItemTextW(hDlg, IDC_EDIT1, buf, max_chars);
|
||||
clipboard_set(buf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
case IDC_CONTINUE:
|
||||
EndDialog(hDlg, ER_CONTINUE);
|
||||
return 0;
|
||||
case IDC_SUPPRESS:
|
||||
EndDialog(hDlg, ER_SUPPRESS);
|
||||
return 0;
|
||||
case IDC_BREAK:
|
||||
EndDialog(hDlg, ER_BREAK);
|
||||
return 0;
|
||||
case IDC_EXIT:
|
||||
EndDialog(hDlg, ER_EXIT);
|
||||
return 0;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_MOVE:
|
||||
dlg_client_origin = MAKEPOINTS(lParam);
|
||||
break;
|
||||
|
||||
case WM_GETMINMAXINFO:
|
||||
{
|
||||
// we must make sure resize_control will never set negative coords -
|
||||
// Windows would clip them, and its real position would be lost.
|
||||
// restrict to a reasonable and good looking minimum size [pixels].
|
||||
MINMAXINFO* mmi = (MINMAXINFO*)lParam;
|
||||
mmi->ptMinTrackSize.x = 407;
|
||||
mmi->ptMinTrackSize.y = 159; // determined experimentally
|
||||
return 0;
|
||||
}
|
||||
|
||||
case WM_SIZE:
|
||||
dlg_resize(hDlg, wParam, lParam);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// we didn't process the message; caller will perform default action.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
// show error dialog with the given text and return user's reaction.
|
||||
// exits directly if 'exit' is clicked.
|
||||
ErrorReaction display_error_impl(const wchar_t* text, int flags)
|
||||
{
|
||||
const DialogParams params = { text, flags };
|
||||
|
||||
const HINSTANCE hInstance = GetModuleHandle(0);
|
||||
LPCSTR lpTemplateName = MAKEINTRESOURCE(IDD_DIALOG1);
|
||||
const HWND hWndParent = win_get_app_main_window();
|
||||
// can't just pass 0 or desktop because this dialog must be
|
||||
// modal (the app must not crash/continue to run before this is seen).
|
||||
|
||||
INT_PTR ret = DialogBoxParam(hInstance, lpTemplateName, hWndParent, error_dialog_proc, (LPARAM)¶ms);
|
||||
// failed; warn user and make sure we return an ErrorReaction.
|
||||
if(ret == 0 || ret == -1)
|
||||
{
|
||||
wdisplay_msg(L"Error", L"Unable to display detailed error dialog.");
|
||||
// TODO: i18n
|
||||
return ER_CONTINUE;
|
||||
}
|
||||
return (ErrorReaction)ret;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// clipboard
|
||||
@ -276,17 +539,17 @@ typedef int(*_PIFV)(void);
|
||||
// pointers to start and end of function tables.
|
||||
// note: COFF throws 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(".LIB$WCA")
|
||||
#pragma data_seg(WIN_CALLBACK_PRE_LIBC(a))
|
||||
_PIFV pre_libc_begin[] = { 0 };
|
||||
#pragma data_seg(".LIB$WCZ")
|
||||
#pragma data_seg(WIN_CALLBACK_PRE_LIBC(z))
|
||||
_PIFV pre_libc_end[] = { 0 };
|
||||
#pragma data_seg(".LIB$WIA")
|
||||
#pragma data_seg(WIN_CALLBACK_PRE_MAIN(a))
|
||||
_PIFV pre_main_begin[] = { 0 };
|
||||
#pragma data_seg(".LIB$WIZ")
|
||||
#pragma data_seg(WIN_CALLBACK_PRE_MAIN(a))
|
||||
_PIFV pre_main_end[] = { 0 };
|
||||
#pragma data_seg(".LIB$WTA")
|
||||
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(a))
|
||||
_PIFV shutdown_begin[] = { 0 };
|
||||
#pragma data_seg(".LIB$WTZ")
|
||||
#pragma data_seg(WIN_CALLBACK_POST_ATEXIT(z))
|
||||
_PIFV shutdown_end[] = { 0 };
|
||||
#pragma data_seg()
|
||||
|
||||
@ -309,8 +572,9 @@ static void call_func_tbl(_PIFV* begin, _PIFV* end)
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// several init functions are before _cinit.
|
||||
// can't guarantee POSIX static mutex init has been done by then.
|
||||
// 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;
|
||||
@ -329,6 +593,18 @@ void win_unlock(uint idx)
|
||||
LeaveCriticalSection(&cs[idx]);
|
||||
}
|
||||
|
||||
int win_is_locked(uint idx)
|
||||
{
|
||||
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++)
|
||||
@ -357,6 +633,8 @@ static void cs_shutdown()
|
||||
// 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.
|
||||
@ -369,11 +647,52 @@ static void at_exit(void)
|
||||
}
|
||||
|
||||
|
||||
#ifndef NO_MAIN_REDIRECT
|
||||
static
|
||||
#endif
|
||||
void win_pre_main_init()
|
||||
{
|
||||
#ifdef HAVE_DEBUGALLOC
|
||||
uint flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
||||
// Always enable leak detection in debug builds
|
||||
flags |= _CRTDBG_LEAK_CHECK_DF;
|
||||
#ifdef PARANOIA
|
||||
// force malloc et al. to check the heap every call.
|
||||
// slower, but reports errors closer to where they occur.
|
||||
flags |= _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_DELAY_FREE_MEM_DF;
|
||||
#endif // PARANOIA
|
||||
_CrtSetDbgFlag(flags);
|
||||
#endif // HAVE_DEBUGALLOC
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
|
||||
#ifndef SCED
|
||||
|
||||
#undef main
|
||||
extern int app_main(int argc, char* argv[]);
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
win_pre_main_init();
|
||||
return app_main(argc, argv);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// 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()
|
||||
{
|
||||
#if WINVER >= 0x0501
|
||||
// enable low-fragmentation heap
|
||||
#if WINVER >= 0x0501
|
||||
HMODULE hKernel32Dll = LoadLibrary("kernel32.dll");
|
||||
if(hKernel32Dll)
|
||||
{
|
||||
@ -403,48 +722,19 @@ static inline void pre_libc_init()
|
||||
call_func_tbl(pre_libc_begin, pre_libc_end);
|
||||
}
|
||||
|
||||
static inline void pre_main_init()
|
||||
{
|
||||
#ifdef HAVE_DEBUGALLOC
|
||||
uint flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
||||
// Always enable leak detection in debug builds
|
||||
flags |= _CRTDBG_LEAK_CHECK_DF;
|
||||
#ifdef PARANOIA
|
||||
// force malloc et al. to check the heap every call.
|
||||
// slower, but reports errors closer to where they occur.
|
||||
flags |= _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_DELAY_FREE_MEM_DF;
|
||||
#endif // PARANOIA
|
||||
_CrtSetDbgFlag(flags);
|
||||
#endif // HAVE_DEBUGALLOC
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
#ifdef HAVE_DEBUGALLOC
|
||||
// Enable heap corruption checking after every allocation. Has the same
|
||||
// effect as PARANOIA in pre_main_init, but lets you switch it on anywhere
|
||||
// so that you can skip checking the whole of the initialisation code.
|
||||
// The debugger will break in the allocation just after the one that
|
||||
// corrupted the heap, so check its ID and then _CrtSetBreakAlloc(...)
|
||||
// on the previous one and try again.
|
||||
// Warning: This makes things rather slow.
|
||||
void memory_debug_extreme_turbo_plus()
|
||||
{
|
||||
_CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_DELAY_FREE_MEM_DF );
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
int entry()
|
||||
{
|
||||
pre_libc_init();
|
||||
return WinMainCRTStartup(); // calls _cinit, and then WinMain
|
||||
int ret = -1;
|
||||
__try
|
||||
{
|
||||
pre_libc_init();
|
||||
ret = mainCRTStartup(); // calls _cinit and then our main
|
||||
}
|
||||
__except(wdbg_exception_filter(GetExceptionInformation()))
|
||||
{
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -454,11 +744,3 @@ void sced_init()
|
||||
pre_main_init();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef SCED
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
pre_main_init();
|
||||
return main(__argc, __argv);
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user