1
0
forked from 0ad/0ad

Display warnings and errors on screen for a short time (fixes #119).

Add warn(), error() functions for simulation scripts.

This was SVN commit r7600.
This commit is contained in:
Ykkrosh 2010-05-31 22:44:59 +00:00
parent 6de0cdc5f9
commit 65cf204423
6 changed files with 167 additions and 24 deletions

View File

@ -205,6 +205,13 @@ Handle unifont_load(const VfsPath& pathname, size_t flags)
LibError unifont_unload(Handle& h)
{
H_DEREF(h, UniFont, f);
// unbind ourself, so people will get errors if
// they draw more text without binding a new font
if (BoundGlyphs == f->glyphs)
BoundGlyphs = NULL;
return h_free(h, H_UniFont);
}

View File

@ -32,6 +32,7 @@
#include "network/NetServer.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Font.h"
#include "ps/Globals.h"
#include "ps/Hotkey.h"
#include "ps/Pyrogenesis.h"
@ -194,6 +195,9 @@ void CConsole::Render()
{
if (! (m_bVisible || m_bToggle) ) return;
CFont font(CONSOLE_FONT);
font.Bind();
// animation: slide in from top of screen
const float MaxY = m_fHeight;
const float DeltaY = (1.0f - m_fVisibleFrac) * MaxY;

View File

@ -20,13 +20,23 @@
#include "CLogger.h"
#include "CConsole.h"
#include "ConfigDB.h"
#include "lib/ogl.h"
#include "lib/path_util.h"
#include "lib/timer.h"
#include "lib/utf8.h"
#include "lib/res/graphics/unifont.h"
#include "lib/sysdep/sysdep.h"
#include "ps/Font.h"
#include <ctime>
#include <ostream>
static const double RENDER_TIMEOUT = 5.0; // seconds before messages are deleted
static const double RENDER_TIMEOUT_RATE = 10.0; // number of timed-out messages deleted per second
static const size_t RENDER_LIMIT = 20; // maximum messages on screen at once
extern int g_xres, g_yres;
// Set up a default logger that throws everything away, because that's
// better than crashing. (This is particularly useful for unit tests which
// don't care about any log output.)
@ -74,6 +84,10 @@ CLogger::CLogger(std::wostream* mainLog, std::wostream* interestingLog, bool tak
m_OwnsStreams = takeOwnership;
m_UseDebugPrintf = useDebugPrintf;
m_RenderLastEraseTime = -1.0;
// this is called too early to allow us to call timer_Time(),
// so we'll fill in the initial value later
Init();
}
@ -126,6 +140,9 @@ void CLogger::WriteMessage(const wchar_t* message)
*m_MainLog << L"<p>" << message << L"</p>\n";
m_MainLog->flush();
// Don't do this since it results in too much noise:
// RenderedMessage r = { Normal, timer_Time(), message };
// m_RenderMessages.push_back(r);
}
void CLogger::WriteError(const wchar_t* message)
@ -140,6 +157,9 @@ void CLogger::WriteError(const wchar_t* message)
*m_MainLog << L"<p class=\"error\">ERROR: "<< message << L"</p>\n";
m_MainLog->flush();
RenderedMessage r = { Error, timer_Time(), message };
m_RenderMessages.push_back(r);
}
void CLogger::WriteWarning(const wchar_t* message)
@ -154,6 +174,9 @@ void CLogger::WriteWarning(const wchar_t* message)
*m_MainLog << L"<p class=\"warning\">WARNING: "<< message << L"</p>\n";
m_MainLog->flush();
RenderedMessage r = { Warning, timer_Time(), message };
m_RenderMessages.push_back(r);
}
// Sends the message to the appropriate piece of code
@ -260,6 +283,81 @@ void CLogger::LogError(const wchar_t* fmt, ...)
WriteError(buffer);
}
void CLogger::Render()
{
CleanupRenderQueue();
CFont font(L"mono-stroke-10");
int lineSpacing = font.GetLineSpacing();
font.Bind();
glPushMatrix();
glScalef(1.0f, -1.0f, 1.0f);
glTranslatef(4.0f, 4.0f + (float)lineSpacing - g_yres, 0.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (std::deque<RenderedMessage>::iterator it = m_RenderMessages.begin(); it != m_RenderMessages.end(); ++it)
{
const wchar_t* type;
if (it->method == Normal)
{
type = L"info";
glColor3f(0.0f, 0.8f, 0.0f);
}
else if (it->method == Warning)
{
type = L"warning";
glColor3f(1.0f, 1.0f, 0.0f);
}
else
{
type = L"error";
glColor3f(1.0f, 0.0f, 0.0f);
}
glPushMatrix();
glwprintf(L"[%8.3f] %ls: ", it->time, type);
// Display the actual message in white so it's more readable
glColor3f(1.0f, 1.0f, 1.0f);
glwprintf(L"%ls", it->message.c_str());
glPopMatrix();
glTranslatef(0.f, (float)lineSpacing, 0.f);
}
glDisable(GL_BLEND);
glPopMatrix();
}
void CLogger::CleanupRenderQueue()
{
if (m_RenderMessages.empty())
return;
double now = timer_Time();
// Initialise the timer on the first call (since we can't do it in the ctor)
if (m_RenderLastEraseTime == -1.0)
m_RenderLastEraseTime = now;
// Delete old messages, approximately at the given rate limit (and at most one per frame)
if (now - m_RenderLastEraseTime > 1.0/RENDER_TIMEOUT_RATE)
{
if (m_RenderMessages[0].time + RENDER_TIMEOUT < now)
{
m_RenderMessages.pop_front();
m_RenderLastEraseTime = now;
}
}
// If there's still too many then delete the oldest
if (m_RenderMessages.size() > RENDER_LIMIT)
m_RenderMessages.erase(m_RenderMessages.begin(), m_RenderMessages.end() - RENDER_LIMIT);
}
TestLogger::TestLogger()
{

View File

@ -73,6 +73,9 @@ public:
void LogMessage(const wchar_t* fmt, ...) WPRINTF_ARGS(2);
void LogWarning(const wchar_t* fmt, ...) WPRINTF_ARGS(2);
void LogError(const wchar_t* fmt, ...) WPRINTF_ARGS(2);
// Render recent log messages onto the screen
void Render();
private:
void Init();
@ -80,6 +83,9 @@ private:
// -- This function has not been removed because the build would break.
void LogUsingMethod(ELogMethod method, const wchar_t* message);
// Delete old timed-out entries from the list of text to render
void CleanupRenderQueue();
// the output streams
std::wostream* m_MainLog;
std::wostream* m_InterestingLog;
@ -97,6 +103,15 @@ private:
// Used to remember LogOnce messages
std::set<std::wstring> m_LoggedOnce;
// Used for Render()
struct RenderedMessage
{
ELogMethod method;
double time;
std::wstring message;
};
std::deque<RenderedMessage> m_RenderMessages;
double m_RenderLastEraseTime;
};
/**

View File

@ -238,39 +238,36 @@ void Render()
ogl_WarnIfError();
PROFILE_START( "render fonts" );
MICROLOG(L"render fonts");
// overlay mode
// set up overlay mode
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.f, (float)g_xres, 0.f, (float)g_yres, -1.f, 1000.f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
PROFILE_END( "render fonts" );
ogl_WarnIfError();
// Temp GUI message GeeTODO
MICROLOG(L"render GUI");
PROFILE_START( "render gui" );
PROFILE_START("render gui");
if(g_DoRenderGui) g_GUI->Draw();
PROFILE_END( "render gui" );
PROFILE_END("render gui");
ogl_WarnIfError();
// Particle Engine Updating
CParticleEngine::GetInstance()->UpdateEmitters();
ogl_WarnIfError();
// Text:
// Use the GL_ALPHA texture as the alpha channel with a flat colouring
@ -280,26 +277,25 @@ void Render()
glEnable(GL_TEXTURE_2D);
// -- GL
ogl_WarnIfError();
glLoadIdentity();
{
PROFILE( "render console" );
glLoadIdentity();
MICROLOG(L"render console");
CFont font(CONSOLE_FONT);
font.Bind();
g_Console->Render();
}
PROFILE_START("render console");
g_Console->Render();
PROFILE_END("render console");
ogl_WarnIfError();
PROFILE_START("render logger");
g_Logger->Render();
PROFILE_END("render logger");
ogl_WarnIfError();
// Profile information
PROFILE_START( "render profiling" );
PROFILE_START("render profiling");
g_ProfileViewer.RenderProfile();
PROFILE_END( "render profiling" );
PROFILE_END("render profiling");
ogl_WarnIfError();

View File

@ -101,16 +101,37 @@ void ErrorReporter(JSContext* UNUSED(cx), const char* message, JSErrorReport* re
// Functions in the global namespace:
JSBool print(JSContext* cx, JSObject* UNUSED(obj), uintN argc, jsval* argv, jsval* UNUSED(rval))
JSBool print(JSContext* cx, uintN argc, jsval* vp)
{
for (uintN i = 0; i < argc; ++i)
{
std::string str;
if (!ScriptInterface::FromJSVal(cx, argv[i], str))
if (!ScriptInterface::FromJSVal(cx, JS_ARGV(cx, vp)[i], str))
return JS_FALSE;
printf("%s", str.c_str());
}
fflush(stdout);
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
JSBool warn(JSContext* cx, uintN UNUSED(argc), jsval* vp)
{
std::wstring str;
if (!ScriptInterface::FromJSVal(cx, JS_ARGV(cx, vp)[0], str))
return JS_FALSE;
LOGWARNING(L"%ls", str.c_str());
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
JSBool error(JSContext* cx, uintN UNUSED(argc), jsval* vp)
{
std::wstring str;
if (!ScriptInterface::FromJSVal(cx, JS_ARGV(cx, vp)[0], str))
return JS_FALSE;
LOGERROR(L"%ls", str.c_str());
JS_SET_RVAL(cx, vp, JSVAL_VOID);
return JS_TRUE;
}
@ -227,7 +248,9 @@ ScriptInterface_impl::ScriptInterface_impl(const char* nativeScopeName, JSContex
m_nativeScope = JS_DefineObject(m_cx, m_glob, nativeScopeName, NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY
| JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_glob, "print", (JSNative)::print, 0, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JS_DefineFunction(m_cx, m_glob, "warn", (JSNative)::warn, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
JS_DefineFunction(m_cx, m_glob, "error", (JSNative)::error, 1, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSFUN_FAST_NATIVE);
}
ScriptInterface_impl::~ScriptInterface_impl()