static const double RENDER_TIMEOUT = 10.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
// 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.)
struct BlackHoleStreamBuf : public std::streambuf
{
} blackHoleStreamBuf;
std::ostream blackHoleStream(&blackHoleStreamBuf);
CLogger nullLogger(&blackHoleStream, &blackHoleStream, false, true);
CLogger* g_Logger = &nullLogger;
const char* html_header0 =
"\n"
"\n"
"Pyrogenesis Log\n"
"\n"
"0 A.D. ";
const char* html_header1 = "
\n";
CLogger::CLogger()
{
OsPath mainlogPath(psLogDir()/"mainlog.html");
m_MainLog = new std::ofstream(OsString(mainlogPath).c_str(), std::ofstream::out | std::ofstream::trunc);
OsPath interestinglogPath(psLogDir()/"interestinglog.html");
m_InterestingLog = new std::ofstream(OsString(interestinglogPath).c_str(), std::ofstream::out | std::ofstream::trunc);
m_OwnsStreams = true;
m_UseDebugPrintf = true;
Init();
}
CLogger::CLogger(std::ostream* mainLog, std::ostream* interestingLog, bool takeOwnership, bool useDebugPrintf)
{
m_MainLog = mainLog;
m_InterestingLog = interestingLog;
m_OwnsStreams = takeOwnership;
m_UseDebugPrintf = useDebugPrintf;
Init();
}
void CLogger::Init()
{
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
m_NumberOfMessages = 0;
m_NumberOfErrors = 0;
m_NumberOfWarnings = 0;
//Write Headers for the HTML documents
*m_MainLog << html_header0 << "Main log" << html_header1;
//Write Headers for the HTML documents
*m_InterestingLog << html_header0 << "Main log (warnings and errors only)" << html_header1;
}
CLogger::~CLogger()
{
char buffer[128];
sprintf_s(buffer, ARRAY_SIZE(buffer), " with %d message(s), %d error(s) and %d warning(s).", m_NumberOfMessages,m_NumberOfErrors,m_NumberOfWarnings);
time_t t = time(NULL);
struct tm* now = localtime(&t);
char currentDate[17];
sprintf_s(currentDate, ARRAY_SIZE(currentDate), "%04d-%02d-%02d", 1900+now->tm_year, 1+now->tm_mon, now->tm_mday);
char currentTime[10];
sprintf_s(currentTime, ARRAY_SIZE(currentTime), "%02d:%02d:%02d", now->tm_hour, now->tm_min, now->tm_sec);
//Write closing text
*m_MainLog << "Engine exited successfully on " << currentDate;
*m_MainLog << " at " << currentTime << buffer << "
\n";
*m_InterestingLog << "Engine exited successfully on " << currentDate;
*m_InterestingLog << " at " << currentTime << buffer << "
\n";
if (m_OwnsStreams)
{
SAFE_DELETE(m_InterestingLog);
SAFE_DELETE(m_MainLog);
}
}
static std::string ToHTML(const char* message)
{
std::string cmessage = message;
boost::algorithm::replace_all(cmessage, "&", "&");
boost::algorithm::replace_all(cmessage, "<", "<");
return cmessage;
}
void CLogger::WriteMessage(const char* message, bool doRender = false)
{
std::string cmessage = ToHTML(message);
CScopeLock lock(m_Mutex);
++m_NumberOfMessages;
// if (m_UseDebugPrintf)
// debug_printf("MESSAGE: %s\n", message);
*m_MainLog << "" << cmessage << "
\n";
m_MainLog->flush();
if (doRender)
{
if (g_Console) g_Console->InsertMessage(std::string("INFO: ") + message);
PushRenderMessage(Normal, message);
}
}
void CLogger::WriteError(const char* message)
{
std::string cmessage = ToHTML(message);
CScopeLock lock(m_Mutex);
++m_NumberOfErrors;
if (m_UseDebugPrintf)
debug_printf("ERROR: %s\n", message);
if (g_Console) g_Console->InsertMessage(std::string("ERROR: ") + message);
*m_InterestingLog << "ERROR: " << cmessage << "
\n";
m_InterestingLog->flush();
*m_MainLog << "ERROR: " << cmessage << "
\n";
m_MainLog->flush();
PushRenderMessage(Error, message);
}
void CLogger::WriteWarning(const char* message)
{
std::string cmessage = ToHTML(message);
CScopeLock lock(m_Mutex);
++m_NumberOfWarnings;
if (m_UseDebugPrintf)
debug_printf("WARNING: %s\n", message);
if (g_Console) g_Console->InsertMessage(std::string("WARNING: ") + message);
*m_InterestingLog << "WARNING: " << cmessage << "
\n";
m_InterestingLog->flush();
*m_MainLog << "WARNING: " << cmessage << "
\n";
m_MainLog->flush();
PushRenderMessage(Warning, message);
}
void CLogger::Render()
{
PROFILE3_GPU("logger");
CleanupRenderQueue();
CStrIntern font_name("mono-stroke-10");
CFontMetrics font(font_name);
int lineSpacing = font.GetLineSpacing();
CShaderTechniquePtr textTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text);
textTech->BeginPass();
CTextRenderer textRenderer(textTech->GetShader());
textRenderer.Font(font_name);
textRenderer.Color(1.0f, 1.0f, 1.0f);
// Offset by an extra 35px vertically to avoid the top bar.
textRenderer.Translate(4.0f, 35.0f + lineSpacing, 0.0f);
// (Lock must come after loading the CFont, since that might log error messages
// and attempt to lock the mutex recursively which is forbidden)
CScopeLock lock(m_Mutex);
- for (std::deque::iterator it = m_RenderMessages.begin(); it != m_RenderMessages.end(); ++it)
+ for (const RenderedMessage& msg : m_RenderMessages)
{
const char* type;
- if (it->method == Normal)
+ if (msg.method == Normal)
{
type = "info";
textRenderer.Color(0.0f, 0.8f, 0.0f);
}
- else if (it->method == Warning)
+ else if (msg.method == Warning)
{
type = "warning";
textRenderer.Color(1.0f, 1.0f, 0.0f);
}
else
{
type = "error";
textRenderer.Color(1.0f, 0.0f, 0.0f);
}
CMatrix3D savedTransform = textRenderer.GetTransform();
- textRenderer.PrintfAdvance(L"[%8.3f] %hs: ", it->time, type);
+ textRenderer.PrintfAdvance(L"[%8.3f] %hs: ", msg.time, type);
// Display the actual message in white so it's more readable
textRenderer.Color(1.0f, 1.0f, 1.0f);
- textRenderer.Put(0.0f, 0.0f, it->message.c_str());
+ textRenderer.Put(0.0f, 0.0f, msg.message.c_str());
textRenderer.SetTransform(savedTransform);
textRenderer.Translate(0.0f, (float)lineSpacing, 0.0f);
}
textRenderer.Render();
textTech->EndPass();
}
void CLogger::PushRenderMessage(ELogMethod method, const char* message)
{
double now = timer_Time();
// Add each message line separately
const char* pos = message;
const char* eol;
while ((eol = strchr(pos, '\n')) != NULL)
{
if (eol != pos)
{
RenderedMessage r = { method, now, std::string(pos, eol) };
m_RenderMessages.push_back(r);
}
pos = eol + 1;
}
// Add the last line, if we didn't end on a \n
if (*pos != '\0')
{
RenderedMessage r = { method, now, std::string(pos) };
m_RenderMessages.push_back(r);
}
}
void CLogger::CleanupRenderQueue()
{
CScopeLock lock(m_Mutex);
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()
{
m_OldLogger = g_Logger;
g_Logger = new CLogger(&m_Stream, &blackHoleStream, false, false);
}
TestLogger::~TestLogger()
{
delete g_Logger;
g_Logger = m_OldLogger;
}
std::string TestLogger::GetOutput()
{
return m_Stream.str();
}
TestStdoutLogger::TestStdoutLogger()
{
m_OldLogger = g_Logger;
g_Logger = new CLogger(&std::cout, &blackHoleStream, false, false);
}
TestStdoutLogger::~TestStdoutLogger()
{
delete g_Logger;
g_Logger = m_OldLogger;
}
Index: ps/trunk/source/ps/ConfigDB.cpp
===================================================================
--- ps/trunk/source/ps/ConfigDB.cpp (revision 16887)
+++ ps/trunk/source/ps/ConfigDB.cpp (revision 16888)
@@ -1,405 +1,399 @@
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
+#include "ConfigDB.h"
+
#include
-#include "CLogger.h"
-#include "ConfigDB.h"
-#include "Filesystem.h"
-#include "ThreadUtil.h"
#include "lib/allocators/shared_ptr.h"
+#include "ps/CLogger.h"
+#include "ps/Filesystem.h"
+#include "ps/ThreadUtil.h"
typedef std::map TConfigMap;
TConfigMap CConfigDB::m_Map[CFG_LAST];
VfsPath CConfigDB::m_ConfigFile[CFG_LAST];
static pthread_mutex_t cfgdb_mutex = PTHREAD_MUTEX_INITIALIZER;
CConfigDB::CConfigDB()
{
// Recursive mutex needed for WriteFile
pthread_mutexattr_t attr;
int err;
err = pthread_mutexattr_init(&attr);
ENSURE(err == 0);
err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
ENSURE(err == 0);
err = pthread_mutex_init(&cfgdb_mutex, &attr);
ENSURE(err == 0);
err = pthread_mutexattr_destroy(&attr);
ENSURE(err == 0);
}
#define CHECK_NS(rval)\
do {\
if (ns < 0 || ns >= CFG_LAST)\
{\
debug_warn(L"CConfigDB: Invalid ns value");\
return rval;\
}\
} while (false)
namespace {
template void Get(const CStr& value, T& ret)
{
std::stringstream ss(value);
ss >> ret;
}
template<> void Get<>(const CStr& value, bool& ret)
{
ret = value == "true";
}
template<> void Get<>(const CStr& value, std::string& ret)
{
ret = value;
}
std::string EscapeString(const CStr& str)
{
std::string ret;
for (size_t i = 0; i < str.length(); ++i)
{
if (str[i] == '\\')
ret += "\\\\";
else if (str[i] == '"')
ret += "\\\"";
else
ret += str[i];
}
return ret;
}
} // namespace
#define GETVAL(type)\
void CConfigDB::GetValue(EConfigNamespace ns, const CStr& name, type& value)\
{\
CHECK_NS(;);\
CScopeLock s(&cfgdb_mutex);\
TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name);\
if (it != m_Map[CFG_COMMAND].end())\
{\
Get(it->second[0], value);\
return;\
}\
- for (int search_ns = ns; search_ns >= 0; search_ns--)\
+ for (int search_ns = ns; search_ns >= 0; --search_ns)\
{\
it = m_Map[search_ns].find(name);\
if (it != m_Map[search_ns].end())\
{\
Get(it->second[0], value);\
return;\
}\
}\
}
GETVAL(bool)
GETVAL(int)
GETVAL(float)
GETVAL(double)
GETVAL(std::string)
#undef GETVAL
-void CConfigDB::GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values)
+void CConfigDB::GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values) const
{
CHECK_NS(;);
CScopeLock s(&cfgdb_mutex);
TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name);
if (it != m_Map[CFG_COMMAND].end())
{
values = it->second;
return;
}
- for (int search_ns = ns; search_ns >= 0; search_ns--)
+ for (int search_ns = ns; search_ns >= 0; --search_ns)
{
it = m_Map[search_ns].find(name);
if (it != m_Map[search_ns].end())
{
values = it->second;
return;
}
}
}
-EConfigNamespace CConfigDB::GetValueNamespace(EConfigNamespace ns, const CStr& name)
+EConfigNamespace CConfigDB::GetValueNamespace(EConfigNamespace ns, const CStr& name) const
{
CHECK_NS(CFG_LAST);
CScopeLock s(&cfgdb_mutex);
TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name);
if (it != m_Map[CFG_COMMAND].end())
return CFG_COMMAND;
- for (int search_ns = ns; search_ns >= 0; search_ns--)
+ for (int search_ns = ns; search_ns >= 0; --search_ns)
{
it = m_Map[search_ns].find(name);
if (it != m_Map[search_ns].end())
return (EConfigNamespace)search_ns;
}
return CFG_LAST;
}
-std::map CConfigDB::GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix)
+std::map CConfigDB::GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix) const
{
CScopeLock s(&cfgdb_mutex);
std::map ret;
CHECK_NS(ret);
// Loop upwards so that values in later namespaces can override
// values in earlier namespaces
- for (int search_ns = 0; search_ns <= ns; search_ns++)
- {
- for (TConfigMap::iterator it = m_Map[search_ns].begin(); it != m_Map[search_ns].end(); ++it)
- {
- if (boost::algorithm::starts_with(it->first, prefix))
- ret[it->first] = it->second;
- }
- }
-
- for (TConfigMap::iterator it = m_Map[CFG_COMMAND].begin(); it != m_Map[CFG_COMMAND].end(); ++it)
- {
- if (boost::algorithm::starts_with(it->first, prefix))
- ret[it->first] = it->second;
- }
+ for (int search_ns = 0; search_ns <= ns; ++search_ns)
+ for (const std::pair& p : m_Map[search_ns])
+ if (boost::algorithm::starts_with(p.first, prefix))
+ ret[p.first] = p.second;
+
+ for (const std::pair& p : m_Map[CFG_COMMAND])
+ if (boost::algorithm::starts_with(p.first, prefix))
+ ret[p.first] = p.second;
return ret;
}
void CConfigDB::SetValueString(EConfigNamespace ns, const CStr& name, const CStr& value)
{
CHECK_NS(;);
CScopeLock s(&cfgdb_mutex);
TConfigMap::iterator it = m_Map[ns].find(name);
if (it == m_Map[ns].end())
it = m_Map[ns].insert(m_Map[ns].begin(), make_pair(name, CConfigValueSet(1)));
it->second[0] = value;
}
void CConfigDB::SetConfigFile(EConfigNamespace ns, const VfsPath& path)
{
CHECK_NS(;);
CScopeLock s(&cfgdb_mutex);
m_ConfigFile[ns] = path;
}
bool CConfigDB::Reload(EConfigNamespace ns)
{
CHECK_NS(false);
CScopeLock s(&cfgdb_mutex);
shared_ptr buffer;
size_t buflen;
{
// Handle missing files quietly
if (g_VFS->GetFileInfo(m_ConfigFile[ns], NULL) < 0)
{
LOGMESSAGE("Cannot find config file \"%s\" - ignoring", m_ConfigFile[ns].string8());
return false;
}
LOGMESSAGE("Loading config file \"%s\"", m_ConfigFile[ns].string8());
Status ret = g_VFS->LoadFile(m_ConfigFile[ns], buffer, buflen);
if (ret != INFO::OK)
{
LOGERROR("CConfigDB::Reload(): vfs_load for \"%s\" failed: return was %lld", m_ConfigFile[ns].string8(), (long long)ret);
return false;
}
}
TConfigMap newMap;
char *filebuf = (char*)buffer.get();
char *filebufend = filebuf+buflen;
bool quoted = false;
CStr header;
CStr name;
CStr value;
int line = 1;
std::vector values;
for (char* pos = filebuf; pos < filebufend; ++pos)
{
switch (*pos)
{
case '\n':
case ';':
break; // We finished parsing this line
case ' ':
case '\r':
case '\t':
continue; // ignore
case '[':
header.clear();
for (++pos; pos < filebufend && *pos != '\n' && *pos != ']'; ++pos)
header.push_back(*pos);
if (pos == filebufend || *pos == '\n')
{
LOGERROR("Config header with missing close tag encountered on line %d in '%s'", line, m_ConfigFile[ns].string8());
header.clear();
++line;
continue;
}
LOGMESSAGE("Found config header '%s'", header.c_str());
header.push_back('.');
while (++pos < filebufend && *pos != '\n' && *pos != ';')
if (*pos != ' ' && *pos != '\r')
{
LOGERROR("Config settings on the same line as a header on line %d in '%s'", line, m_ConfigFile[ns].string8());
break;
}
while (pos < filebufend && *pos != '\n')
++pos;
++line;
continue;
case '=':
// Parse parameters (comma separated, possibly quoted)
for (++pos; pos < filebufend && *pos != '\n' && *pos != ';'; ++pos)
{
switch (*pos)
{
case '"':
quoted = true;
// parse until not quoted anymore
for (++pos; pos < filebufend && *pos != '\n' && *pos != '"'; ++pos)
{
if (*pos == '\\' && ++pos == filebufend)
{
LOGERROR("Escape character at end of input (line %d in '%s')", line, m_ConfigFile[ns].string8());
break;
}
value.push_back(*pos);
}
if (pos < filebufend && *pos == '"')
quoted = false;
else
--pos; // We should terminate the outer loop too
break;
case ' ':
case '\r':
case '\t':
break; // ignore
case ',':
if (!value.empty())
values.push_back(value);
value.clear();
break;
default:
value.push_back(*pos);
break;
}
}
if (quoted) // We ignore the invalid parameter
LOGERROR("Unmatched quote while parsing config file '%s' on line %d", m_ConfigFile[ns].string8(), line);
else if (!value.empty())
values.push_back(value);
value.clear();
quoted = false;
break; // We are either at the end of the line, or we still have a comment to parse
default:
name.push_back(*pos);
continue;
}
// Consume the rest of the line
while (pos < filebufend && *pos != '\n')
++pos;
// Store the setting
if (!name.empty() && !values.empty())
{
CStr key(header + name);
newMap[key] = values;
if (key == "lobby.password")
LOGMESSAGE("Loaded config string \"%s\"", key);
else
{
std::string vals;
for (size_t i = 0; i < newMap[key].size() - 1; ++i)
vals += "\"" + EscapeString(newMap[key][i]) + "\", ";
vals += "\"" + EscapeString(newMap[key][values.size()-1]) + "\"";
LOGMESSAGE("Loaded config string \"%s\" = %s", key, vals);
}
}
else if (!name.empty())
LOGERROR("Encountered config setting '%s' without value while parsing '%s' on line %d", name, m_ConfigFile[ns].string8(), line);
name.clear();
values.clear();
++line;
}
if (!name.empty())
LOGERROR("Config file does not have a new line after the last config setting '%s'", name);
m_Map[ns].swap(newMap);
return true;
}
-bool CConfigDB::WriteFile(EConfigNamespace ns)
+bool CConfigDB::WriteFile(EConfigNamespace ns) const
{
CHECK_NS(false);
CScopeLock s(&cfgdb_mutex);
return WriteFile(ns, m_ConfigFile[ns]);
}
-bool CConfigDB::WriteFile(EConfigNamespace ns, const VfsPath& path)
+bool CConfigDB::WriteFile(EConfigNamespace ns, const VfsPath& path) const
{
CHECK_NS(false);
CScopeLock s(&cfgdb_mutex);
shared_ptr buf;
AllocateAligned(buf, 1*MiB, maxSectorSize);
char* pos = (char*)buf.get();
- TConfigMap &map = m_Map[ns];
- for (TConfigMap::const_iterator it = map.begin(); it != map.end(); ++it)
+ for (const std::pair& p : m_Map[ns])
{
size_t i;
- pos += sprintf(pos, "%s = ", it->first.c_str());
- for (i = 0; i < it->second.size() - 1; ++i)
- pos += sprintf(pos, "\"%s\", ", EscapeString(it->second[i]).c_str());
- pos += sprintf(pos, "\"%s\"\n", EscapeString(it->second[i]).c_str());
+ pos += sprintf(pos, "%s = ", p.first.c_str());
+ for (i = 0; i < p.second.size() - 1; ++i)
+ pos += sprintf(pos, "\"%s\", ", EscapeString(p.second[i]).c_str());
+ pos += sprintf(pos, "\"%s\"\n", EscapeString(p.second[i]).c_str());
}
const size_t len = pos - (char*)buf.get();
Status ret = g_VFS->CreateFile(path, buf, len);
if (ret < 0)
{
LOGERROR("CConfigDB::WriteFile(): CreateFile \"%s\" failed (error: %d)", path.string8(), (int)ret);
return false;
}
return true;
}
#undef CHECK_NS
Index: ps/trunk/source/ps/ConfigDB.h
===================================================================
--- ps/trunk/source/ps/ConfigDB.h (revision 16887)
+++ ps/trunk/source/ps/ConfigDB.h (revision 16888)
@@ -1,147 +1,146 @@
-/* Copyright (C) 2014 Wildfire Games.
+/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
CConfigDB - Load, access and store configuration variables
TDD : http://www.wildfiregames.com/forum/index.php?showtopic=1125
OVERVIEW:
JavaScript: Check this documentation: http://trac.wildfiregames.com/wiki/Exposed_ConfigDB_Functions
*/
#ifndef INCLUDED_CONFIGDB
#define INCLUDED_CONFIGDB
-#include "CStr.h"
-#include "Singleton.h"
-
#include "lib/file/vfs/vfs_path.h"
+#include "ps/CStr.h"
+#include "ps/Singleton.h"
// Namespace priorities: User supersedes mod supersedes system.
// Command-line arguments override everything.
enum EConfigNamespace
{
CFG_DEFAULT,
CFG_SYSTEM,
CFG_MOD,
CFG_USER,
CFG_COMMAND,
CFG_LAST
};
typedef std::vector CConfigValueSet;
#define g_ConfigDB CConfigDB::GetSingleton()
class CConfigDB: public Singleton
{
static std::map m_Map[];
static VfsPath m_ConfigFile[];
public:
CConfigDB();
/**
* Attempt to retrieve the value of a config variable with the given name;
* will search CFG_COMMAND first, and then all namespaces from the specified
* namespace down.
*/
void GetValue(EConfigNamespace ns, const CStr& name, bool& value);
///@copydoc CConfigDB::GetValue
void GetValue(EConfigNamespace ns, const CStr& name, int& value);
///@copydoc CConfigDB::GetValue
void GetValue(EConfigNamespace ns, const CStr& name, float& value);
///@copydoc CConfigDB::GetValue
void GetValue(EConfigNamespace ns, const CStr& name, double& value);
///@copydoc CConfigDB::GetValue
void GetValue(EConfigNamespace ns, const CStr& name, std::string& value);
/**
* Attempt to retrieve a vector of values corresponding to the given setting;
* will search CFG_COMMAND first, and then all namespaces from the specified
* namespace down.
*/
- void GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values);
+ void GetValues(EConfigNamespace ns, const CStr& name, CConfigValueSet& values) const;
/**
* Returns the namespace that the value returned by GetValues was defined in,
* or CFG_LAST if it wasn't defined at all.
*/
- EConfigNamespace GetValueNamespace(EConfigNamespace ns, const CStr& name);
+ EConfigNamespace GetValueNamespace(EConfigNamespace ns, const CStr& name) const;
/**
* Retrieve a map of values corresponding to settings whose names begin
* with the given prefix;
* will search all namespaces from default up to the specified namespace.
*/
- std::map GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix);
+ std::map GetValuesWithPrefix(EConfigNamespace ns, const CStr& prefix) const;
/**
* Save a config value in the specified namespace. If the config variable
* existed the value is replaced.
*/
void SetValueString(EConfigNamespace ns, const CStr& name, const CStr& value);
/**
* Set the path to the config file used to populate the specified namespace
* Note that this function does not actually load the config file. Use
* the Reload() method if you want to read the config file at the same time.
*
* 'path': The path to the config file.
*/
void SetConfigFile(EConfigNamespace ns, const VfsPath& path);
/**
* Reload the config file associated with the specified config namespace
* (the last config file path set with SetConfigFile)
*
* Returns:
* true: if the reload succeeded,
* false: if the reload failed
*/
bool Reload(EConfigNamespace);
/**
* Write the current state of the specified config namespace to the file
* specified by 'path'
*
* Returns:
* true: if the config namespace was successfully written to the file
* false: if an error occurred
*/
- bool WriteFile(EConfigNamespace ns, const VfsPath& path);
+ bool WriteFile(EConfigNamespace ns, const VfsPath& path) const;
/**
* Write the current state of the specified config namespace to the file
* it was originally loaded from.
*
* Returns:
* true: if the config namespace was successfully written to the file
* false: if an error occurred
*/
- bool WriteFile(EConfigNamespace ns);
+ bool WriteFile(EConfigNamespace ns) const;
};
// stores the value of the given key into . this quasi-template
// convenience wrapper on top of GetValue simplifies user code
#define CFG_GET_VAL(name, destination)\
g_ConfigDB.GetValue(CFG_USER, name, destination)
-#endif
+#endif // INCLUDED_CONFIGDB
Index: ps/trunk/source/ps/Hotkey.cpp
===================================================================
--- ps/trunk/source/ps/Hotkey.cpp (revision 16887)
+++ ps/trunk/source/ps/Hotkey.cpp (revision 16888)
@@ -1,367 +1,361 @@
-/* Copyright (C) 2014 Wildfire Games.
+/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include "Hotkey.h"
#include
#include "lib/input.h"
-#include "ConfigDB.h"
-#include "CLogger.h"
-#include "CConsole.h"
-#include "CStr.h"
+#include "ps/CConsole.h"
+#include "ps/CLogger.h"
+#include "ps/CStr.h"
+#include "ps/ConfigDB.h"
#include "ps/Globals.h"
-#include "KeyName.h"
+#include "ps/KeyName.h"
static bool unified[UNIFIED_LAST - UNIFIED_SHIFT];
#if SDL_VERSION_ATLEAST(2, 0, 0)
#define SDLKEY SDL_Keycode
#else
#define SDLKEY SDLKey
#endif
struct SKey
{
SDLKEY code; // keycode or MOUSE_ or UNIFIED_ value
bool negated; // whether the key must be pressed (false) or unpressed (true)
};
// Hotkey data associated with an externally-specified 'primary' keycode
struct SHotkeyMapping
{
CStr name; // name of the hotkey
bool negated; // whether the primary key must be pressed (false) or unpressed (true)
std::vector requires; // list of non-primary keys that must also be active
};
typedef std::vector KeyMapping;
// A mapping of keycodes onto the hotkeys that are associated with that key.
// (A hotkey triggered by a combination of multiple keys will be in this map
// multiple times.)
static std::map g_HotkeyMap;
// The current pressed status of hotkeys
std::map g_HotkeyStatus;
// Look up each key binding in the config file and set the mappings for
// all key combinations that trigger it.
static void LoadConfigBindings()
{
- std::map bindings = g_ConfigDB.GetValuesWithPrefix(CFG_COMMAND, "hotkey.");
- for (std::map::iterator bindingsIt = bindings.begin(); bindingsIt != bindings.end(); ++bindingsIt)
+ for (const std::pair& configPair : g_ConfigDB.GetValuesWithPrefix(CFG_COMMAND, "hotkey."))
{
- std::string hotkeyName = bindingsIt->first.substr(7); // strip the "hotkey." prefix
- for (CConfigValueSet::iterator it = bindingsIt->second.begin(); it != bindingsIt->second.end(); ++it)
+ std::string hotkeyName = configPair.first.substr(7); // strip the "hotkey." prefix
+ for (const CStr& hotkey : configPair.second)
{
- const CStr& hotkey = *it;
std::vector keyCombination;
// Iterate through multiple-key bindings (e.g. Ctrl+I)
boost::char_separator sep("+");
typedef boost::tokenizer > tokenizer;
tokenizer tok(hotkey, sep);
for (tokenizer::iterator it = tok.begin(); it != tok.end(); ++it)
{
// Attempt decode as key name
int mapping = FindKeyCode(*it);
if (!mapping)
{
LOGWARNING("Hotkey mapping used invalid key '%s'", hotkey.c_str());
continue;
}
SKey key = { (SDLKEY)mapping, false };
keyCombination.push_back(key);
}
std::vector::iterator itKey, itKey2;
for (itKey = keyCombination.begin(); itKey != keyCombination.end(); ++itKey)
{
SHotkeyMapping bindCode;
bindCode.name = hotkeyName;
bindCode.negated = itKey->negated;
for (itKey2 = keyCombination.begin(); itKey2 != keyCombination.end(); ++itKey2)
if (itKey != itKey2) // Push any auxiliary keys
bindCode.requires.push_back(*itKey2);
g_HotkeyMap[itKey->code].push_back(bindCode);
}
}
}
}
void LoadHotkeys()
{
InitKeyNameMap();
LoadConfigBindings();
// Set up the state of the hotkeys given no key is down.
// i.e. find those hotkeys triggered by all negations.
- for( std::map::iterator mapIt = g_HotkeyMap.begin(); mapIt != g_HotkeyMap.end(); ++mapIt )
- {
- KeyMapping& hotkeyMap = mapIt->second;
-
- for( std::vector::iterator it = hotkeyMap.begin(); it != hotkeyMap.end(); ++it )
+ for (const std::pair& p : g_HotkeyMap)
+ for (const SHotkeyMapping& hotkey : p.second)
{
- if( !it->negated )
+ if (!hotkey.negated)
continue;
bool allNegated = true;
- for( std::vector::iterator j = it->requires.begin(); j != it->requires.end(); ++j )
- if( !j->negated )
+ for (const SKey& k : hotkey.requires)
+ if (!k.negated)
allNegated = false;
- if( allNegated )
- g_HotkeyStatus[it->name] = true;
+ if (allNegated)
+ g_HotkeyStatus[hotkey.name] = true;
}
- }
}
void UnloadHotkeys()
{
g_HotkeyMap.clear();
g_HotkeyStatus.clear();
}
bool isNegated(const SKey& key)
{
// Normal keycodes are below EXTRA_KEYS_BASE
if ((int)key.code < EXTRA_KEYS_BASE && g_keys[key.code] == key.negated)
return false;
// Mouse 'keycodes' are after the modifier keys
else if ((int)key.code > UNIFIED_LAST && g_mouse_buttons[key.code - UNIFIED_LAST] == key.negated)
return false;
// Modifier keycodes are between the normal keys and the mouse 'keys'
else if ((int)key.code < UNIFIED_LAST && (int)key.code > CUSTOM_SDL_KEYCODE && unified[key.code - UNIFIED_SHIFT] == key.negated)
return false;
else
return true;
}
-InReaction HotkeyInputHandler( const SDL_Event_* ev )
+InReaction HotkeyInputHandler(const SDL_Event_* ev)
{
int keycode = 0;
- switch( ev->ev.type )
+ switch(ev->ev.type)
{
case SDL_KEYDOWN:
case SDL_KEYUP:
keycode = (int)ev->ev.key.keysym.sym;
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
#if SDL_VERSION_ATLEAST(2, 0, 0)
// Mousewheel events are no longer buttons, but we want to maintain the order
// expected by g_mouse_buttons for compatibility
if (ev->ev.button.button >= SDL_BUTTON_X1)
keycode = MOUSE_BASE + (int)ev->ev.button.button + 2;
else
#endif
keycode = MOUSE_BASE + (int)ev->ev.button.button;
break;
#if SDL_VERSION_ATLEAST(2, 0, 0)
case SDL_MOUSEWHEEL:
if (ev->ev.wheel.y > 0)
{
keycode = MOUSE_WHEELUP;
break;
}
else if (ev->ev.wheel.y < 0)
{
keycode = MOUSE_WHEELDOWN;
break;
}
else if (ev->ev.wheel.x > 0)
{
keycode = MOUSE_X2;
break;
}
else if (ev->ev.wheel.x < 0)
{
keycode = MOUSE_X1;
break;
}
return IN_PASS;
#endif
case SDL_HOTKEYDOWN:
g_HotkeyStatus[static_cast(ev->ev.user.data1)] = true;
return IN_PASS;
case SDL_HOTKEYUP:
g_HotkeyStatus[static_cast(ev->ev.user.data1)] = false;
return IN_PASS;
default:
return IN_PASS;
}
#if !SDL_VERSION_ATLEAST(2, 0, 0)
// Rather ugly hack to make the '"' key work better on a MacBook Pro on Windows so it doesn't
// always close the console. (Maybe this would be better handled in wsdl or something?)
if (keycode == SDLK_BACKQUOTE && (ev->ev.key.keysym.unicode == '\'' || ev->ev.key.keysym.unicode == '"'))
keycode = ev->ev.key.keysym.unicode;
#endif
// Somewhat hackish:
// Create phantom 'unified-modifier' events when left- or right- modifier keys are pressed
// Just send them to this handler; don't let the imaginary event codes leak back to real SDL.
SDL_Event_ phantom;
- phantom.ev.type = ( ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN ) ) ? SDL_KEYDOWN : SDL_KEYUP;
- if( ( keycode == SDLK_LSHIFT ) || ( keycode == SDLK_RSHIFT ) )
+ phantom.ev.type = ((ev->ev.type == SDL_KEYDOWN) || (ev->ev.type == SDL_MOUSEBUTTONDOWN)) ? SDL_KEYDOWN : SDL_KEYUP;
+ if ((keycode == SDLK_LSHIFT) || (keycode == SDLK_RSHIFT))
{
phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_SHIFT;
- unified[0] = ( phantom.ev.type == SDL_KEYDOWN );
- HotkeyInputHandler( &phantom );
+ unified[0] = (phantom.ev.type == SDL_KEYDOWN);
+ HotkeyInputHandler(&phantom);
}
- else if( ( keycode == SDLK_LCTRL ) || ( keycode == SDLK_RCTRL ) )
+ else if ((keycode == SDLK_LCTRL) || (keycode == SDLK_RCTRL))
{
phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_CTRL;
- unified[1] = ( phantom.ev.type == SDL_KEYDOWN );
- HotkeyInputHandler( &phantom );
+ unified[1] = (phantom.ev.type == SDL_KEYDOWN);
+ HotkeyInputHandler(&phantom);
}
- else if( ( keycode == SDLK_LALT ) || ( keycode == SDLK_RALT ) )
+ else if ((keycode == SDLK_LALT) || (keycode == SDLK_RALT))
{
phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_ALT;
- unified[2] = ( phantom.ev.type == SDL_KEYDOWN );
- HotkeyInputHandler( &phantom );
+ unified[2] = (phantom.ev.type == SDL_KEYDOWN);
+ HotkeyInputHandler(&phantom);
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
- else if( ( keycode == SDLK_LGUI ) || ( keycode == SDLK_RGUI ) )
+ else if ((keycode == SDLK_LGUI) || (keycode == SDLK_RGUI))
#else // SDL 1.2
- else if( ( keycode == SDLK_LSUPER ) || ( keycode == SDLK_RSUPER ) || ( keycode == SDLK_LMETA ) || ( keycode == SDLK_RMETA) )
+ else if ((keycode == SDLK_LSUPER) || (keycode == SDLK_RSUPER) || (keycode == SDLK_LMETA) || (keycode == SDLK_RMETA))
#endif
{
phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_SUPER;
- unified[3] = ( phantom.ev.type == SDL_KEYDOWN );
- HotkeyInputHandler( &phantom );
+ unified[3] = (phantom.ev.type == SDL_KEYDOWN);
+ HotkeyInputHandler(&phantom);
}
// Check whether we have any hotkeys registered for this particular keycode
- if( g_HotkeyMap.find(keycode) == g_HotkeyMap.end() )
- return( IN_PASS );
+ if (g_HotkeyMap.find(keycode) == g_HotkeyMap.end())
+ return (IN_PASS);
// Inhibit the dispatch of hotkey events caused by real keys (not fake mouse button
// events) while the console is up.
bool consoleCapture = false;
- if( g_Console->IsActive() && keycode < CUSTOM_SDL_KEYCODE )
+ if (g_Console->IsActive() && keycode < CUSTOM_SDL_KEYCODE)
consoleCapture = true;
// Here's an interesting bit:
// If you have an event bound to, say, 'F', and another to, say, 'Ctrl+F', pressing
// 'F' while control is down would normally fire off both.
// To avoid this, set the modifier keys for /all/ events this key would trigger
// (Ctrl, for example, is both group-save and bookmark-save)
// but only send a HotkeyDown event for the event with bindings most precisely
// matching the conditions (i.e. the event with the highest number of auxiliary
// keys, providing they're all down)
#if SDL_VERSION_ATLEAST(2, 0, 0)
bool typeKeyDown = ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN ) || (ev->ev.type == SDL_MOUSEWHEEL);
#else
bool typeKeyDown = ( ev->ev.type == SDL_KEYDOWN ) || ( ev->ev.type == SDL_MOUSEBUTTONDOWN );
#endif
// -- KEYDOWN SECTION --
std::vector closestMapNames;
size_t closestMapMatch = 0;
- for (std::vector::iterator it = g_HotkeyMap[keycode].begin(); it < g_HotkeyMap[keycode].end(); ++it)
+ for (const SHotkeyMapping& hotkey : g_HotkeyMap[keycode])
{
// If a key has been pressed, and this event triggers on its release, skip it.
// Similarly, if the key's been released and the event triggers on a keypress, skip it.
- if (it->negated == typeKeyDown)
+ if (hotkey.negated == typeKeyDown)
continue;
// Check for no unpermitted keys
bool accept = true;
- for (std::vector::iterator itKey = it->requires.begin(); itKey != it->requires.end() && accept; ++itKey)
- accept = isNegated(*itKey);
+ for (const SKey& k : hotkey.requires)
+ accept = isNegated(k);
- if (accept && !(consoleCapture && it->name != "console.toggle"))
+ if (accept && !(consoleCapture && hotkey.name != "console.toggle"))
{
// Check if this is an equally precise or more precise match
- if (it->requires.size() + 1 >= closestMapMatch)
+ if (hotkey.requires.size() + 1 >= closestMapMatch)
{
// Check if more precise
- if (it->requires.size() + 1 > closestMapMatch)
+ if (hotkey.requires.size() + 1 > closestMapMatch)
{
// Throw away the old less-precise matches
closestMapNames.clear();
- closestMapMatch = it->requires.size() + 1;
+ closestMapMatch = hotkey.requires.size() + 1;
}
- closestMapNames.push_back(it->name.c_str());
+ closestMapNames.push_back(hotkey.name.c_str());
}
}
}
for (size_t i = 0; i < closestMapNames.size(); ++i)
{
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = SDL_HOTKEYDOWN;
hotkeyNotification.ev.user.data1 = const_cast(closestMapNames[i]);
in_push_priority_event(&hotkeyNotification);
}
// -- KEYUP SECTION --
- for (std::vector::iterator it = g_HotkeyMap[keycode].begin(); it < g_HotkeyMap[keycode].end(); ++it)
+ for (const SHotkeyMapping& hotkey : g_HotkeyMap[keycode])
{
// If it's a keydown event, won't cause HotKeyUps in anything that doesn't
// use this key negated => skip them
// If it's a keyup event, won't cause HotKeyUps in anything that does use
// this key negated => skip them too.
- if (it->negated != typeKeyDown)
+ if (hotkey.negated != typeKeyDown)
continue;
// Check for no unpermitted keys
bool accept = true;
- for (std::vector::iterator itKey = it->requires.begin(); itKey != it->requires.end() && accept; ++itKey)
- accept = isNegated(*itKey);
+ for (const SKey& k : hotkey.requires)
+ accept = isNegated(k);
if (accept)
{
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = SDL_HOTKEYUP;
- hotkeyNotification.ev.user.data1 = const_cast(it->name.c_str());
+ hotkeyNotification.ev.user.data1 = const_cast(hotkey.name.c_str());
in_push_priority_event(&hotkeyNotification);
}
}
return IN_PASS;
}
bool HotkeyIsPressed(const CStr& keyname)
{
return g_HotkeyStatus[keyname];
}
Index: ps/trunk/source/ps/ProfileViewer.cpp
===================================================================
--- ps/trunk/source/ps/ProfileViewer.cpp (revision 16887)
+++ ps/trunk/source/ps/ProfileViewer.cpp (revision 16888)
@@ -1,640 +1,637 @@
-/* Copyright (C) 2012 Wildfire Games.
+/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
/*
* Implementation of profile display (containing only display routines,
* the data model(s) are implemented elsewhere).
*/
#include "precompiled.h"
#include
#include
#include "ProfileViewer.h"
#include "graphics/FontMetrics.h"
#include "gui/GUIutil.h"
#include "graphics/ShaderManager.h"
#include "graphics/TextRenderer.h"
#include "ps/CLogger.h"
#include "ps/Filesystem.h"
#include "ps/Hotkey.h"
#include "ps/Profile.h"
#include "lib/external_libraries/libsdl.h"
#include "renderer/Renderer.h"
#include "scriptinterface/ScriptInterface.h"
extern int g_xres, g_yres;
struct CProfileViewerInternals
{
+ NONCOPYABLE(CProfileViewerInternals); // because of the ofstream
+public:
CProfileViewerInternals() {}
/// Whether the profiling display is currently visible
bool profileVisible;
/// List of root tables
std::vector rootTables;
/// Path from a root table (path[0]) to the currently visible table (path[size-1])
std::vector path;
/// Helper functions
void TableIsDeleted(AbstractProfileTable* table);
void NavigateTree(int id);
/// File for saved profile output (reset when the game is restarted)
std::ofstream outputStream;
-
-private:
- // Cannot be copied/assigned, because of the ofstream
- CProfileViewerInternals(const CProfileViewerInternals& rhs);
- const CProfileViewerInternals& operator=(const CProfileViewerInternals& rhs);
};
///////////////////////////////////////////////////////////////////////////////////////////////
// AbstractProfileTable implementation
AbstractProfileTable::~AbstractProfileTable()
{
if (CProfileViewer::IsInitialised())
{
g_ProfileViewer.m->TableIsDeleted(this);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
// CProfileViewer implementation
// AbstractProfileTable got deleted, make sure we have no dangling pointers
void CProfileViewerInternals::TableIsDeleted(AbstractProfileTable* table)
{
for(int idx = (int)rootTables.size()-1; idx >= 0; --idx)
{
if (rootTables[idx] == table)
rootTables.erase(rootTables.begin() + idx);
}
for(size_t idx = 0; idx < path.size(); ++idx)
{
if (path[idx] != table)
continue;
path.erase(path.begin() + idx, path.end());
if (path.size() == 0)
profileVisible = false;
}
}
// Move into child tables or return to parent tables based on the given number
void CProfileViewerInternals::NavigateTree(int id)
{
if (id == 0)
{
if (path.size() > 1)
path.pop_back();
}
else
{
AbstractProfileTable* table = path[path.size() - 1];
size_t numrows = table->GetNumberRows();
for(size_t row = 0; row < numrows; ++row)
{
AbstractProfileTable* child = table->GetChild(row);
if (!child)
continue;
--id;
if (id == 0)
{
path.push_back(child);
break;
}
}
}
}
// Construction/Destruction
CProfileViewer::CProfileViewer()
{
m = new CProfileViewerInternals;
m->profileVisible = false;
}
CProfileViewer::~CProfileViewer()
{
delete m;
}
// Render
void CProfileViewer::RenderProfile()
{
if (!m->profileVisible)
return;
if (!m->path.size())
{
m->profileVisible = false;
return;
}
PROFILE3_GPU("profile viewer");
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
AbstractProfileTable* table = m->path[m->path.size() - 1];
const std::vector& columns = table->GetColumns();
size_t numrows = table->GetNumberRows();
CStrIntern font_name("mono-stroke-10");
CFontMetrics font(font_name);
int lineSpacing = font.GetLineSpacing();
// Render background
GLint estimate_height;
GLint estimate_width;
estimate_width = 50;
for(size_t i = 0; i < columns.size(); ++i)
estimate_width += (GLint)columns[i].width;
estimate_height = 3 + (GLint)numrows;
if (m->path.size() > 1)
estimate_height += 2;
estimate_height = lineSpacing*estimate_height;
CShaderTechniquePtr solidTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_solid);
solidTech->BeginPass();
CShaderProgramPtr solidShader = solidTech->GetShader();
solidShader->Uniform(str_color, 0.0f, 0.0f, 0.0f, 0.5f);
CMatrix3D transform = GetDefaultGuiMatrix();
solidShader->Uniform(str_transform, transform);
float backgroundVerts[] = {
(float)estimate_width, 0.0f,
0.0f, 0.0f,
0.0f, (float)estimate_height,
0.0f, (float)estimate_height,
(float)estimate_width, (float)estimate_height,
(float)estimate_width, 0.0f
};
solidShader->VertexPointer(2, GL_FLOAT, 0, backgroundVerts);
solidShader->AssertPointersBound();
glDrawArrays(GL_TRIANGLES, 0, 6);
transform.PostTranslate(22.0f, lineSpacing*3.0f, 0.0f);
solidShader->Uniform(str_transform, transform);
// Draw row backgrounds
for (size_t row = 0; row < numrows; ++row)
{
if (row % 2)
solidShader->Uniform(str_color, 1.0f, 1.0f, 1.0f, 0.1f);
else
solidShader->Uniform(str_color, 0.0f, 0.0f, 0.0f, 0.1f);
float rowVerts[] = {
-22.f, 2.f,
estimate_width-22.f, 2.f,
estimate_width-22.f, 2.f-lineSpacing,
estimate_width-22.f, 2.f-lineSpacing,
-22.f, 2.f-lineSpacing,
-22.f, 2.f
};
solidShader->VertexPointer(2, GL_FLOAT, 0, rowVerts);
solidShader->AssertPointersBound();
glDrawArrays(GL_TRIANGLES, 0, 6);
transform.PostTranslate(0.0f, lineSpacing, 0.0f);
solidShader->Uniform(str_transform, transform);
}
solidTech->EndPass();
// Print table and column titles
CShaderTechniquePtr textTech = g_Renderer.GetShaderManager().LoadEffect(str_gui_text);
textTech->BeginPass();
CTextRenderer textRenderer(textTech->GetShader());
textRenderer.Font(font_name);
textRenderer.Color(1.0f, 1.0f, 1.0f);
textRenderer.PrintfAt(2.0f, lineSpacing, L"%hs", table->GetTitle().c_str());
textRenderer.Translate(22.0f, lineSpacing*2.0f, 0.0f);
float colX = 0.0f;
for (size_t col = 0; col < columns.size(); ++col)
{
CStrW text = columns[col].title.FromUTF8();
int w, h;
font.CalculateStringSize(text.c_str(), w, h);
float x = colX;
if (col > 0) // right-align all but the first column
x += columns[col].width - w;
textRenderer.Put(x, 0.0f, text.c_str());
colX += columns[col].width;
}
textRenderer.Translate(0.0f, lineSpacing, 0.0f);
// Print rows
int currentExpandId = 1;
for (size_t row = 0; row < numrows; ++row)
{
if (table->IsHighlightRow(row))
textRenderer.Color(1.0f, 0.5f, 0.5f);
else
textRenderer.Color(1.0f, 1.0f, 1.0f);
if (table->GetChild(row))
{
textRenderer.PrintfAt(-15.0f, 0.0f, L"%d", currentExpandId);
currentExpandId++;
}
float colX = 0.0f;
for (size_t col = 0; col < columns.size(); ++col)
{
CStrW text = table->GetCellText(row, col).FromUTF8();
int w, h;
font.CalculateStringSize(text.c_str(), w, h);
float x = colX;
if (col > 0) // right-align all but the first column
x += columns[col].width - w;
textRenderer.Put(x, 0.0f, text.c_str());
colX += columns[col].width;
}
textRenderer.Translate(0.0f, lineSpacing, 0.0f);
}
textRenderer.Color(1.0f, 1.0f, 1.0f);
if (m->path.size() > 1)
{
textRenderer.Translate(0.0f, lineSpacing, 0.0f);
textRenderer.Put(-15.0f, 0.0f, L"0");
textRenderer.Put(0.0f, 0.0f, L"back to parent");
}
textRenderer.Render();
textTech->EndPass();
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
}
// Handle input
InReaction CProfileViewer::Input(const SDL_Event_* ev)
{
switch(ev->ev.type)
{
case SDL_KEYDOWN:
{
if (!m->profileVisible)
break;
int k = ev->ev.key.keysym.sym;
if (k >= SDLK_0 && k <= SDLK_9)
{
m->NavigateTree(k - SDLK_0);
return IN_HANDLED;
}
break;
}
case SDL_HOTKEYDOWN:
std::string hotkey = static_cast(ev->ev.user.data1);
if( hotkey == "profile.toggle" )
{
if (!m->profileVisible)
{
if (m->rootTables.size())
{
m->profileVisible = true;
m->path.push_back(m->rootTables[0]);
}
}
else
{
size_t i;
for(i = 0; i < m->rootTables.size(); ++i)
{
if (m->rootTables[i] == m->path[0])
break;
}
i++;
m->path.clear();
if (i < m->rootTables.size())
{
m->path.push_back(m->rootTables[i]);
}
else
{
m->profileVisible = false;
}
}
return( IN_HANDLED );
}
else if( hotkey == "profile.save" )
{
SaveToFile();
return( IN_HANDLED );
}
break;
}
return( IN_PASS );
}
InReaction CProfileViewer::InputThunk(const SDL_Event_* ev)
{
if (CProfileViewer::IsInitialised())
return g_ProfileViewer.Input(ev);
return IN_PASS;
}
// Add a table to the list of roots
void CProfileViewer::AddRootTable(AbstractProfileTable* table, bool front)
{
if (front)
m->rootTables.insert(m->rootTables.begin(), table);
else
m->rootTables.push_back(table);
}
namespace
{
struct WriteTable
{
std::ofstream& f;
WriteTable(std::ofstream& f) : f(f) {}
void operator() (AbstractProfileTable* table)
{
std::vector data; // 2d array of (rows+head)*columns elements
const std::vector& columns = table->GetColumns();
// Add column headers to 'data'
for (std::vector::const_iterator col_it = columns.begin();
col_it != columns.end(); ++col_it)
data.push_back(col_it->title);
// Recursively add all profile data to 'data'
WriteRows(1, table, data);
// Calculate the width of each column ( = the maximum width of
// any value in that column)
std::vector columnWidths;
size_t cols = columns.size();
for (size_t c = 0; c < cols; ++c)
{
size_t max = 0;
for (size_t i = c; i < data.size(); i += cols)
max = std::max(max, data[i].length());
columnWidths.push_back(max);
}
// Output data as a formatted table:
f << "\n\n" << table->GetTitle() << "\n";
if (cols == 0) // avoid divide-by-zero
return;
for (size_t r = 0; r < data.size()/cols; ++r)
{
for (size_t c = 0; c < cols; ++c)
f << (c ? " | " : "\n")
<< data[r*cols + c].Pad(PS_TRIM_RIGHT, columnWidths[c]);
// Add dividers under some rows. (Currently only the first, since
// that contains the column headers.)
if (r == 0)
for (size_t c = 0; c < cols; ++c)
f << (c ? "-|-" : "\n")
<< CStr::Repeat("-", columnWidths[c]);
}
}
void WriteRows(int indent, AbstractProfileTable* table, std::vector& data)
{
const std::vector& columns = table->GetColumns();
for (size_t r = 0; r < table->GetNumberRows(); ++r)
{
// Do pretty tree-structure indenting
CStr indentation = CStr::Repeat("| ", indent-1);
if (r+1 == table->GetNumberRows())
indentation += "'-";
else
indentation += "|-";
for (size_t c = 0; c < columns.size(); ++c)
if (c == 0)
data.push_back(indentation + table->GetCellText(r, c));
else
data.push_back(table->GetCellText(r, c));
if (table->GetChild(r))
WriteRows(indent+1, table->GetChild(r), data);
}
}
private:
const WriteTable& operator=(const WriteTable&);
};
struct DumpTable
{
ScriptInterface& m_ScriptInterface;
JS::PersistentRooted m_Root;
DumpTable(ScriptInterface& scriptInterface, JS::HandleValue root) :
m_ScriptInterface(scriptInterface), m_Root(scriptInterface.GetJSRuntime(), root)
{
}
// std::for_each requires a move constructor and the use of JS::PersistentRooted apparently breaks a requirement for an
// automatic move constructor
DumpTable(DumpTable && original) :
m_ScriptInterface(original.m_ScriptInterface),
m_Root(original.m_ScriptInterface.GetJSRuntime(), original.m_Root.get())
{
}
void operator() (AbstractProfileTable* table)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue t(cx);
JS::RootedValue rows(cx, DumpRows(table));
m_ScriptInterface.Eval(L"({})", &t);
m_ScriptInterface.SetProperty(t, "cols", DumpCols(table));
m_ScriptInterface.SetProperty(t, "data", rows);
m_ScriptInterface.SetProperty(m_Root, table->GetTitle().c_str(), t);
}
std::vector DumpCols(AbstractProfileTable* table)
{
std::vector titles;
const std::vector& columns = table->GetColumns();
for (size_t c = 0; c < columns.size(); ++c)
titles.push_back(columns[c].title);
return titles;
}
JS::Value DumpRows(AbstractProfileTable* table)
{
JSContext* cx = m_ScriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue data(cx);
m_ScriptInterface.Eval("({})", &data);
const std::vector& columns = table->GetColumns();
for (size_t r = 0; r < table->GetNumberRows(); ++r)
{
JS::RootedValue row(cx);
m_ScriptInterface.Eval("([])", &row);
m_ScriptInterface.SetProperty(data, table->GetCellText(r, 0).c_str(), row);
if (table->GetChild(r))
{
JS::RootedValue childRows(cx, DumpRows(table->GetChild(r)));
m_ScriptInterface.SetPropertyInt(row, 0, childRows);
}
for (size_t c = 1; c < columns.size(); ++c)
m_ScriptInterface.SetPropertyInt(row, c, table->GetCellText(r, c));
}
return data;
}
private:
const DumpTable& operator=(const DumpTable&);
};
bool SortByName(AbstractProfileTable* a, AbstractProfileTable* b)
{
return (a->GetName() < b->GetName());
}
}
void CProfileViewer::SaveToFile()
{
// Open the file, if necessary. If this method is called several times,
// the profile results will be appended to the previous ones from the same
// run.
if (! m->outputStream.is_open())
{
// Open the file. (It will be closed when the CProfileViewer
// destructor is called.)
OsPath path = psLogDir()/"profile.txt";
m->outputStream.open(OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
if (m->outputStream.fail())
{
LOGERROR("Failed to open profile log file");
return;
}
else
{
LOGMESSAGERENDER("Profiler snapshot saved to '%s'", path.string8());
}
}
time_t t;
time(&t);
m->outputStream << "================================================================\n\n";
m->outputStream << "PS profiler snapshot - " << asctime(localtime(&t));
std::vector tables = m->rootTables;
sort(tables.begin(), tables.end(), SortByName);
for_each(tables.begin(), tables.end(), WriteTable(m->outputStream));
m->outputStream << "\n\n================================================================\n";
m->outputStream.flush();
}
JS::Value CProfileViewer::SaveToJS(ScriptInterface& scriptInterface)
{
JSContext* cx = scriptInterface.GetContext();
JSAutoRequest rq(cx);
JS::RootedValue root(cx);
scriptInterface.Eval("({})", &root);
std::vector tables = m->rootTables;
sort(tables.begin(), tables.end(), SortByName);
for_each(tables.begin(), tables.end(), DumpTable(scriptInterface, root));
return root;
}
void CProfileViewer::ShowTable(const CStr& table)
{
m->path.clear();
if (table.length() > 0)
{
for (size_t i = 0; i < m->rootTables.size(); ++i)
{
if (m->rootTables[i]->GetName() == table)
{
m->path.push_back(m->rootTables[i]);
m->profileVisible = true;
return;
}
}
}
// No matching table found, so don't display anything
m->profileVisible = false;
}
Index: ps/trunk/source/ps/XML/Xeromyces.cpp
===================================================================
--- ps/trunk/source/ps/XML/Xeromyces.cpp (revision 16887)
+++ ps/trunk/source/ps/XML/Xeromyces.cpp (revision 16888)
@@ -1,421 +1,421 @@
/* Copyright (C) 2015 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. 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. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see .
*/
#include "precompiled.h"
#include
#include
#include