Index: ps/trunk/source/ps/ConfigDB.cpp
===================================================================
--- ps/trunk/source/ps/ConfigDB.cpp (revision 15979)
+++ ps/trunk/source/ps/ConfigDB.cpp (revision 15980)
@@ -1,322 +1,375 @@
/* Copyright (C) 2014 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 "CLogger.h"
#include "ConfigDB.h"
#include "Filesystem.h"
-#include "Parser.h"
#include "ThreadUtil.h"
#include "lib/allocators/shared_ptr.h"
-typedef std::map TConfigMap;
+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 GETVAL(T, type) \
- void CConfigDB::GetValue##T(EConfigNamespace ns, const CStr& name, type& value) \
- { \
- if (ns < 0 || ns >= CFG_LAST) \
- { \
- debug_warn(L"CConfigDB: Invalid ns value"); \
- return; \
- } \
- CScopeLock s(&cfgdb_mutex); \
- TConfigMap::iterator it = m_Map[CFG_COMMAND].find(name); \
- if (it != m_Map[CFG_COMMAND].end()) \
- { \
- it->second[0].Get##T(value); \
- return; \
- } \
- \
- for (int search_ns = ns; search_ns >= 0; search_ns--) \
- { \
- it = m_Map[search_ns].find(name); \
- if (it != m_Map[search_ns].end()) \
- { \
- it->second[0].Get##T(value); \
- return; \
- } \
- } \
- }
-
-GETVAL(Bool, bool)
-GETVAL(Int, int)
-GETVAL(Float, float)
-GETVAL(Double, double)
-GETVAL(String, std::string)
+#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--)\
+ {\
+ 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)
{
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return;
- }
+ 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--)
{
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)
{
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return CFG_LAST;
- }
+ 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--)
{
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)
{
CScopeLock s(&cfgdb_mutex);
std::map ret;
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return 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;
}
return ret;
}
void CConfigDB::SetValueString(EConfigNamespace ns, const CStr& name, const CStr& value)
{
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return;
- }
+ 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].m_String = value;
+ it->second[0] = value;
}
void CConfigDB::SetConfigFile(EConfigNamespace ns, const VfsPath& path)
{
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return;
- }
+ CHECK_NS();
CScopeLock s(&cfgdb_mutex);
- m_ConfigFile[ns]=path;
+ m_ConfigFile[ns] = path;
}
bool CConfigDB::Reload(EConfigNamespace ns)
{
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return false;
- }
+ CHECK_NS(false);
CScopeLock s(&cfgdb_mutex);
- // Set up CParser
- CParser parser;
- CParserLine parserLine;
- parser.InputTaskType("Assignment", "_$ident_=<_[-$arg(_minus)]_$value_,>_[-$arg(_minus)]_$value[[;]$rest]");
- parser.InputTaskType("CommentOrBlank", "_[;[$rest]]");
-
- // Open file with VFS
- shared_ptr buffer; size_t buflen;
+ shared_ptr buffer;
+ size_t buflen;
{
// Handle missing files quietly
if (g_VFS->GetFileInfo(m_ConfigFile[ns], NULL) < 0)
{
LOGMESSAGE(L"Cannot find config file \"%ls\" - ignoring", m_ConfigFile[ns].string().c_str());
return false;
}
- else
+
+ LOGMESSAGE(L"Loading config file \"%ls\"", m_ConfigFile[ns].string().c_str());
+ Status ret = g_VFS->LoadFile(m_ConfigFile[ns], buffer, buflen);
+ if (ret != INFO::OK)
{
- LOGMESSAGE(L"Loading config file \"%ls\"", m_ConfigFile[ns].string().c_str());
- Status ret = g_VFS->LoadFile(m_ConfigFile[ns], buffer, buflen);
- if (ret != INFO::OK)
- {
- LOGERROR(L"CConfigDB::Reload(): vfs_load for \"%ls\" failed: return was %lld", m_ConfigFile[ns].string().c_str(), (long long)ret);
- return false;
- }
+ LOGERROR(L"CConfigDB::Reload(): vfs_load for \"%ls\" failed: return was %lld", m_ConfigFile[ns].string().c_str(), (long long)ret);
+ return false;
}
}
TConfigMap newMap;
+ char *filebuf = (char*)buffer.get();
+ char *filebufend = filebuf+buflen;
- char *filebuf=(char *)buffer.get();
- char *filebufend=filebuf+buflen;
-
- // Read file line by line
- char *next=filebuf-1;
- do
+ bool quoted = false;
+ CStr name;
+ CStr value;
+ int line = 1;
+ std::vector values;
+ for (char* pos = filebuf; pos < filebufend; ++pos)
{
- char *pos=next+1;
- next=(char *)memchr(pos, '\n', filebufend-pos);
- if (!next) next=filebufend;
-
- char *lend=next;
- if (lend > filebuf && *(lend-1) == '\r') lend--;
-
- // Send line to parser
- bool parseOk=parserLine.ParseString(parser, std::string(pos, lend));
- // Get name and value from parser
- std::string name;
- std::string value;
+ switch (*pos)
+ {
+ case '\n':
+ case ';':
+ break; // We finished parsing this line
+
+ case ' ':
+ case '\r':
+ continue; // ignore
+
+ 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(L"Escape character at end of input (line %d in '%ls')", line, m_ConfigFile[ns].string().c_str());
+ break;
+ }
+
+ value.push_back(*pos);
+ }
+ if (pos < filebufend && *pos == '"')
+ quoted = false;
+ else
+ --pos; // We should terminate the outer loop too
+ break;
+
+ case '\r':
+ case ' ':
+ 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(L"Unmatched quote while parsing config file '%ls' on line %d", m_ConfigFile[ns].string().c_str(), 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;
+ }
- if (parseOk &&
- parserLine.GetArgCount()>=2 &&
- parserLine.GetArgString(0, name) &&
- parserLine.GetArgString(1, value))
+ // Consume the rest of the line
+ while (pos < filebufend && *pos != '\n')
+ ++pos;
+ // Store the setting
+ if (!name.empty() && !values.empty())
{
- // Add name and value to the map
- size_t argCount = parserLine.GetArgCount();
-
- newMap[name].clear();
-
- for( size_t t = 0; t < argCount; t++ )
+ newMap[name] = values;
+ if (name == "lobby.password")
+ LOGMESSAGE(L"Loaded config string \"%hs\"", name.c_str());
+ else
{
- if( !parserLine.GetArgString( (int)t + 1, value ) )
- continue;
- CConfigValue argument;
- argument.m_String = value;
- newMap[name].push_back( argument );
- if (name == "lobby.password")
- value = "*******";
- LOGMESSAGE(L"Loaded config string \"%hs\" = \"%hs\"", name.c_str(), value.c_str());
+ std::string vals;
+ for (size_t i = 0; i < newMap[name].size() - 1; ++i)
+ vals += "\"" + EscapeString(newMap[name][i]) + "\", ";
+ vals += "\"" + EscapeString(newMap[name][values.size()-1]) + "\"";
+ LOGMESSAGE(L"Loaded config string \"%hs\" = %hs", name.c_str(), vals.c_str());
}
}
+ else if (!name.empty())
+ LOGERROR(L"Encountered config setting '%hs' without value while parsing '%ls' on line %d", name.c_str(), m_ConfigFile[ns].string().c_str(), line);
+
+ name.clear();
+ values.clear();
+ ++line;
}
- while (next < filebufend);
-
+
+ if (!name.empty())
+ LOGERROR(L"Config file does not have a new line after the last config setting '%hs'", name.c_str());
+
m_Map[ns].swap(newMap);
return true;
}
bool CConfigDB::WriteFile(EConfigNamespace ns)
{
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return false;
- }
+ CHECK_NS(false);
CScopeLock s(&cfgdb_mutex);
return WriteFile(ns, m_ConfigFile[ns]);
}
bool CConfigDB::WriteFile(EConfigNamespace ns, const VfsPath& path)
{
- if (ns < 0 || ns >= CFG_LAST)
- {
- debug_warn(L"CConfigDB: Invalid ns value");
- return false;
- }
+ 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)
+ TConfigMap &map = m_Map[ns];
+ for (TConfigMap::const_iterator it = map.begin(); it != map.end(); ++it)
{
- pos += sprintf(pos, "%s = \"%s\"\n", it->first.c_str(), it->second[0].m_String.c_str());
+ 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());
}
const size_t len = pos - (char*)buf.get();
Status ret = g_VFS->CreateFile(path, buf, len);
- if(ret < 0)
+ if (ret < 0)
{
LOGERROR(L"CConfigDB::WriteFile(): CreateFile \"%ls\" failed (error: %d)", path.string().c_str(), (int)ret);
return false;
}
return true;
}
+
+#undef CHECK_NS
Index: ps/trunk/source/ps/ConfigDB.h
===================================================================
--- ps/trunk/source/ps/ConfigDB.h (revision 15979)
+++ ps/trunk/source/ps/ConfigDB.h (revision 15980)
@@ -1,150 +1,148 @@
-/* Copyright (C) 2013 Wildfire Games.
+/* Copyright (C) 2014 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 "Parser.h"
#include "CStr.h"
#include "Singleton.h"
#include "lib/file/vfs/vfs_path.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 CParserValue CConfigValue;
-typedef std::vector CConfigValueSet;
+typedef std::vector CConfigValueSet;
#define g_ConfigDB CConfigDB::GetSingleton()
class CConfigDB: public Singleton
{
- static std::map m_Map[];
+ 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 GetValueBool(EConfigNamespace ns, const CStr& name, bool& value);
- ///@copydoc CConfigDB::GetValueBool
- void GetValueInt(EConfigNamespace ns, const CStr& name, int& value);
- ///@copydoc CConfigDB::GetValueBool
- void GetValueFloat(EConfigNamespace ns, const CStr& name, float& value);
- ///@copydoc CConfigDB::GetValueBool
- void GetValueDouble(EConfigNamespace ns, const CStr& name, double& value);
- ///@copydoc CConfigDB::GetValueBool
- void GetValueString(EConfigNamespace ns, const CStr& name, std::string& value);
+ 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);
/**
* 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);
/**
* 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);
/**
* 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);
/**
* 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);
};
// stores the value of the given key into . this quasi-template
// convenience wrapper on top of CConfigValue::Get* simplifies user code and
// avoids "assignment within condition expression" warnings.
#define CFG_GET_VAL(name, type, destination)\
- g_ConfigDB.GetValue##type(CFG_USER, name, destination)
+ g_ConfigDB.GetValue(CFG_USER, name, destination)
#endif
Index: ps/trunk/source/ps/Hotkey.cpp
===================================================================
--- ps/trunk/source/ps/Hotkey.cpp (revision 15979)
+++ ps/trunk/source/ps/Hotkey.cpp (revision 15980)
@@ -1,400 +1,367 @@
/* Copyright (C) 2014 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/Globals.h"
#include "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." );
-
- CParser multikeyParser;
- multikeyParser.InputTaskType( "multikey", "<[~$arg(_negate)]$value_+_>_[~$arg(_negate)]$value" );
-
- for( std::map::iterator bindingsIt = bindings.begin(); bindingsIt != bindings.end(); ++bindingsIt )
+ std::map bindings = g_ConfigDB.GetValuesWithPrefix(CFG_COMMAND, "hotkey.");
+ for (std::map::iterator bindingsIt = bindings.begin(); bindingsIt != bindings.end(); ++bindingsIt)
{
std::string hotkeyName = bindingsIt->first.substr(7); // strip the "hotkey." prefix
-
- for( CConfigValueSet::iterator it = bindingsIt->second.begin(); it != bindingsIt->second.end(); ++it )
+ for (CConfigValueSet::iterator it = bindingsIt->second.begin(); it != bindingsIt->second.end(); ++it)
{
- std::string hotkey;
- if( it->GetString( hotkey ) )
- {
- std::vector keyCombination;
+ const CStr& hotkey = *it;
+ std::vector keyCombination;
- CParserLine multikeyIdentifier;
- multikeyIdentifier.ParseString( multikeyParser, hotkey );
-
- // Iterate through multiple-key bindings (e.g. Ctrl+I)
-
- bool negateNext = false;
-
- for( size_t t = 0; t < multikeyIdentifier.GetArgCount(); t++ )
+ // 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)
{
-
- if( multikeyIdentifier.GetArgString( (int)t, hotkey ) )
- {
- if( hotkey == "_negate" )
- {
- negateNext = true;
- continue;
- }
-
- // Attempt decode as key name
- int mapping = FindKeyCode( hotkey );
-
- // Attempt to decode as a negation of a keyname
- // Yes, it's going a bit far, perhaps.
- // Too powerful for most uses, probably.
- // However, it got some hardcoding out of the engine.
- // Thus it makes me happy.
-
- if( !mapping )
- {
- LOGWARNING(L"Hotkey mapping used invalid key '%hs'", hotkey.c_str() );
- continue;
- }
-
- SKey key = { (SDLKEY)mapping, negateNext };
- keyCombination.push_back(key);
-
- negateNext = false;
-
- }
+ LOGWARNING(L"Hotkey mapping used invalid key '%hs'", hotkey.c_str());
+ continue;
}
- std::vector::iterator itKey, itKey2;
+ SKey key = { (SDLKEY)mapping, false };
+ keyCombination.push_back(key);
+ }
- for( itKey = keyCombination.begin(); itKey != keyCombination.end(); ++itKey )
- {
- SHotkeyMapping bindCode;
+ std::vector::iterator itKey, itKey2;
+ for (itKey = keyCombination.begin(); itKey != keyCombination.end(); ++itKey)
+ {
+ SHotkeyMapping bindCode;
- bindCode.name = hotkeyName;
- bindCode.negated = itKey->negated;
+ bindCode.name = hotkeyName;
+ bindCode.negated = itKey->negated;
- for( itKey2 = keyCombination.begin(); itKey2 != keyCombination.end(); ++itKey2 )
- {
- // Push any auxiliary keys.
- if( itKey != itKey2 )
- bindCode.requires.push_back( *itKey2 );
- }
+ 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 );
- }
+ 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 )
{
if( !it->negated )
continue;
bool allNegated = true;
for( std::vector::iterator j = it->requires.begin(); j != it->requires.end(); ++j )
if( !j->negated )
allNegated = false;
if( allNegated )
g_HotkeyStatus[it->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 )
{
int keycode = 0;
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.key.keysym.sym = (SDLKEY)UNIFIED_SHIFT;
unified[0] = ( phantom.ev.type == SDL_KEYDOWN );
HotkeyInputHandler( &phantom );
}
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 );
}
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 );
}
#if SDL_VERSION_ATLEAST(2, 0, 0)
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) )
#endif
{
phantom.ev.key.keysym.sym = (SDLKEY)UNIFIED_SUPER;
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 );
// 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 )
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)
{
// 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)
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);
if (accept && !(consoleCapture && it->name != "console.toggle"))
{
// Check if this is an equally precise or more precise match
if (it->requires.size() + 1 >= closestMapMatch)
{
// Check if more precise
if (it->requires.size() + 1 > closestMapMatch)
{
// Throw away the old less-precise matches
closestMapNames.clear();
closestMapMatch = it->requires.size() + 1;
}
closestMapNames.push_back(it->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)
{
// 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)
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);
if (accept)
{
SDL_Event_ hotkeyNotification;
hotkeyNotification.ev.type = SDL_HOTKEYUP;
hotkeyNotification.ev.user.data1 = const_cast(it->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/scripting/JSInterface_ConfigDB.cpp
===================================================================
--- ps/trunk/source/ps/scripting/JSInterface_ConfigDB.cpp (revision 15979)
+++ ps/trunk/source/ps/scripting/JSInterface_ConfigDB.cpp (revision 15980)
@@ -1,104 +1,104 @@
-/* Copyright (C) 2013 Wildfire Games.
+/* Copyright (C) 2014 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 "JSInterface_ConfigDB.h"
#include "ps/ConfigDB.h"
#include "ps/CLogger.h"
#include "scriptinterface/ScriptInterface.h"
bool JSI_ConfigDB::GetConfigNamespace(std::wstring cfgNsString, EConfigNamespace& cfgNs)
{
if (cfgNsString == L"default")
cfgNs = CFG_DEFAULT;
else if (cfgNsString == L"system")
cfgNs = CFG_SYSTEM;
else if (cfgNsString == L"user")
cfgNs = CFG_USER;
else if (cfgNsString == L"mod")
cfgNs = CFG_MOD;
else
{
LOGERROR(L"Invalid namespace name passed to the ConfigDB!");
cfgNs = CFG_DEFAULT;
return false;
}
return true;
}
std::string JSI_ConfigDB::GetValue(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring cfgNsString, std::string name)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return std::string();
std::string value;
- g_ConfigDB.GetValueString(cfgNs, name, value);
+ g_ConfigDB.GetValue(cfgNs, name, value);
return value;
}
bool JSI_ConfigDB::CreateValue(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring cfgNsString, std::string name, std::string value)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return false;
g_ConfigDB.SetValueString(cfgNs, name, value);
return true;
}
bool JSI_ConfigDB::WriteFile(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring cfgNsString, Path path)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return false;
bool ret = g_ConfigDB.WriteFile(cfgNs, path);
return ret;
}
bool JSI_ConfigDB::Reload(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring cfgNsString)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return false;
bool ret = g_ConfigDB.Reload(cfgNs);
return ret;
}
bool JSI_ConfigDB::SetFile(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), std::wstring cfgNsString, Path path)
{
EConfigNamespace cfgNs;
if (!GetConfigNamespace(cfgNsString, cfgNs))
return false;
g_ConfigDB.SetConfigFile(cfgNs, path);
return true;
}
void JSI_ConfigDB::RegisterScriptFunctions(ScriptInterface& scriptInterface)
{
scriptInterface.RegisterFunction("ConfigDB_GetValue");
scriptInterface.RegisterFunction("ConfigDB_CreateValue");
scriptInterface.RegisterFunction("ConfigDB_WriteFile");
scriptInterface.RegisterFunction("ConfigDB_SetFile");
scriptInterface.RegisterFunction("ConfigDB_Reload");
}