1
1
forked from 0ad/0ad

Add xmessage-based GUI dialog box for debug messages on Linux (fixes #663).

Switch out of fullscreen before displaying dialog boxes.
Add Engine.DebugWarn for testing.

This was SVN commit r8661.
This commit is contained in:
Ykkrosh 2010-11-20 17:14:45 +00:00
parent e971c85da5
commit 74c19b5015
4 changed files with 148 additions and 7 deletions

View File

@ -356,6 +356,11 @@ int Crash(void* UNUSED(cbdata))
return *(int*)0;
}
void DebugWarn(void* UNUSED(cbdata))
{
debug_warn(L"Warning at user's request.");
}
// Force a JS garbage collection cycle to take place immediately.
// Writes an indication of how long this took to the console.
void ForceGC(void* cbdata)
@ -416,5 +421,6 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<void, int, &SetTurnLength>("SetTurnLength");
scriptInterface.RegisterFunction<void, float, float, float, &SetCameraTarget>("SetCameraTarget");
scriptInterface.RegisterFunction<int, &Crash>("Crash");
scriptInterface.RegisterFunction<void, &DebugWarn>("DebugWarn");
scriptInterface.RegisterFunction<void, &ForceGC>("ForceGC");
}

View File

@ -32,9 +32,13 @@
#include "lib/sysdep/cursor.h"
#include "udbg.h"
#include <boost/algorithm/string/replace.hpp>
#define GNU_SOURCE
#include <dlfcn.h>
#include <sys/wait.h>
#if OS_MACOSX
#define URL_OPEN_COMMAND "open"
#else
@ -49,6 +53,100 @@ void sys_display_msg(const wchar_t* caption, const wchar_t* msg)
fprintf(stderr, "%ls: %ls\n", caption, msg); // must not use fwprintf, since stderr is byte-oriented
}
static ErrorReaction try_gui_display_error(const wchar_t* text, bool manual_break, bool allow_suppress, bool no_continue)
{
pid_t cpid = fork();
if(cpid == -1)
return ER_NOT_IMPLEMENTED;
if(cpid == 0)
{
// This is the child process
// Set ASCII charset, to avoid font warnings from xmessage
setenv("LC_ALL", "C", 1);
LibError err; // ignore UTF-8 errors
std::string message = utf8_from_wstring(text, &err);
// Replace CRLF->LF
boost::algorithm::replace_all(message, "\r\n", "\n");
const char* cmd = "/usr/bin/xmessage";
char buttons[256] = "";
const char* defaultButton = "Exit";
if(!no_continue)
{
strcat_s(buttons, sizeof(buttons), "Continue:100,");
defaultButton = "Continue";
}
if(allow_suppress)
strcat_s(buttons, sizeof(buttons), "Suppress:101,");
strcat_s(buttons, sizeof(buttons), "Break:102,Debugger:103,Exit:104");
// Since execv wants non-const strings, we strdup them here
// and don't care about the memory leak
char* const argv[] = {
strdup(cmd),
strdup("-buttons"), buttons,
strdup("-default"), strdup(defaultButton),
strdup(message.c_str()),
NULL
};
execv(cmd, argv);
// If exec returns, it failed
//fprintf(stderr, "Error running %s: %d\n", cmd, errno);
exit(-1);
}
// This is the parent process
int status = 0;
waitpid(cpid, &status, 0);
// If it didn't exist successfully, fall back to the non-GUI prompt
if(!WIFEXITED(status))
return ER_NOT_IMPLEMENTED;
switch(WEXITSTATUS(status))
{
case 103: // Debugger
udbg_launch_debugger();
//-fallthrough
case 102: // Break
if(manual_break)
return ER_BREAK;
debug_break();
return ER_CONTINUE;
case 100: // Continue
if(!no_continue)
return ER_CONTINUE;
// continue isn't allowed, so this was invalid input.
return ER_NOT_IMPLEMENTED;
case 101: // Suppress
if(allow_suppress)
return ER_SUPPRESS;
// suppress isn't allowed, so this was invalid input.
return ER_NOT_IMPLEMENTED;
case 104: // Exit
abort();
return ER_EXIT; // placebo; never reached
}
// Unexpected return value - fall back to the non-GUI prompt
return ER_NOT_IMPLEMENTED;
}
ErrorReaction sys_display_error(const wchar_t* text, size_t flags)
{
printf("%ls\n\n", text);
@ -57,7 +155,14 @@ ErrorReaction sys_display_error(const wchar_t* text, size_t flags)
const bool allow_suppress = (flags & DE_ALLOW_SUPPRESS) != 0;
const bool no_continue = (flags & DE_NO_CONTINUE ) != 0;
// until valid input given:
// Try the GUI prompt if possible
ErrorReaction ret = try_gui_display_error(text, manual_break, allow_suppress, no_continue);
if (ret != ER_NOT_IMPLEMENTED)
return ret;
// Otherwise fall back to the terminal-based input
// Loop until valid input given:
for(;;)
{
if(!no_continue)
@ -85,7 +190,7 @@ ErrorReaction sys_display_error(const wchar_t* text, size_t flags)
case 'c': case 'C':
if(!no_continue)
return ER_CONTINUE;
return ER_CONTINUE;
// continue isn't allowed, so this was invalid input. loop again.
break;
case 's': case 'S':

View File

@ -403,6 +403,27 @@ static size_t ChooseCacheSize()
}
#endif
ErrorReaction psDisplayError(const wchar_t* UNUSED(text), size_t UNUSED(flags))
{
// If we're fullscreen, then sometimes (at least on some particular drivers on Linux)
// displaying the error dialog hangs the desktop since the dialog box is behind the
// fullscreen window. So we just force the game to windowed mode before displaying the dialog.
// (But only if we're in the main thread, and not if we're being reentrant.)
if (ThreadUtil::IsMainThread())
{
static bool reentering = false;
if (!reentering)
{
reentering = true;
g_VideoMode.SetFullscreen(false);
reentering = false;
}
}
// We don't actually implement the error display here, so return appropriately
return ER_NOT_IMPLEMENTED;
}
static void InitVfs(const CmdLineArgs& args)
{
TIMER(L"InitVfs");
@ -418,6 +439,7 @@ static void InitVfs(const CmdLineArgs& args)
AppHooks hooks = {0};
hooks.bundle_logs = psBundleLogs;
hooks.get_log_dir = psLogDir;
hooks.display_error = psDisplayError;
app_hooks_update(&hooks);
const size_t cacheSize = ChooseCacheSize();

View File

@ -254,7 +254,10 @@ bool CVideoMode::ResizeWindow(int w, int h)
bool CVideoMode::SetFullscreen(bool fullscreen)
{
debug_assert(m_IsInitialised);
// This might get called before initialisation by psDisplayError;
// if so then silently fail
if (!m_IsInitialised)
return false;
// Check whether this is actually a change
if (fullscreen == m_IsFullscreen)
@ -325,12 +328,17 @@ void CVideoMode::UpdateRenderer(int w, int h)
SViewPort vp = { 0, 0, w, h };
g_Renderer.SetViewport(vp);
g_Renderer.Resize(w, h);
if (CRenderer::IsInitialised())
{
g_Renderer.SetViewport(vp);
g_Renderer.Resize(w, h);
}
g_GUI->UpdateResolution();
if (g_GUI)
g_GUI->UpdateResolution();
g_Console->UpdateScreenSize(w, h);
if (g_Console)
g_Console->UpdateScreenSize(w, h);
if (g_Game)
g_Game->GetView()->SetViewport(vp);