barrier/src/lib/server/Config.cpp

2328 lines
52 KiB
C++
Raw Normal View History

2012-06-10 16:50:54 +00:00
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012 Bolton Software Ltd.
* Copyright (C) 2002 Chris Schoeneman
2012-06-10 16:50:54 +00:00
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file COPYING that should have accompanied this file.
*
* This package 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "server/Config.h"
#include "server/Server.h"
#include "synergy/KeyMap.h"
#include "synergy/key_types.h"
#include "net/XSocket.h"
#include "base/IEventQueue.h"
#include "common/stdistream.h"
#include "common/stdostream.h"
2012-06-10 16:50:54 +00:00
#include <cstdlib>
using namespace synergy::string;
2012-06-10 16:50:54 +00:00
//
2014-11-11 13:51:47 +00:00
// Config
2012-06-10 16:50:54 +00:00
//
2014-11-11 13:51:47 +00:00
Config::Config(IEventQueue* events) :
2014-02-25 15:03:43 +00:00
m_inputFilter(events),
m_hasLockToScreenAction(false),
2014-02-25 15:03:43 +00:00
m_events(events)
2012-06-10 16:50:54 +00:00
{
// do nothing
}
2014-11-11 13:51:47 +00:00
Config::~Config()
2012-06-10 16:50:54 +00:00
{
// do nothing
}
bool
2014-11-11 13:51:47 +00:00
Config::addScreen(const String& name)
2012-06-10 16:50:54 +00:00
{
// alias name must not exist
if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) {
return false;
}
// add cell
2014-11-11 13:51:47 +00:00
m_map.insert(std::make_pair(name, Cell()));
2012-06-10 16:50:54 +00:00
// add name
m_nameToCanonicalName.insert(std::make_pair(name, name));
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::renameScreen(const String& oldName,
const String& newName)
2012-06-10 16:50:54 +00:00
{
// get canonical name and find cell
2014-11-11 13:51:47 +00:00
String oldCanonical = getCanonicalName(oldName);
CellMap::iterator index = m_map.find(oldCanonical);
2012-06-10 16:50:54 +00:00
if (index == m_map.end()) {
return false;
}
// accept if names are equal but replace with new name to maintain
// case. otherwise, the new name must not exist.
if (!CaselessCmp::equal(oldName, newName) &&
2012-06-10 16:50:54 +00:00
m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) {
return false;
}
// update cell
2014-11-11 13:51:47 +00:00
Cell tmpCell = index->second;
2012-06-10 16:50:54 +00:00
m_map.erase(index);
m_map.insert(std::make_pair(newName, tmpCell));
// update name
m_nameToCanonicalName.erase(oldCanonical);
m_nameToCanonicalName.insert(std::make_pair(newName, newName));
// update connections
2014-11-11 13:51:47 +00:00
Name oldNameObj(this, oldName);
2012-06-10 16:50:54 +00:00
for (index = m_map.begin(); index != m_map.end(); ++index) {
index->second.rename(oldNameObj, newName);
}
// update alias targets
if (CaselessCmp::equal(oldName, oldCanonical)) {
2014-11-11 13:51:47 +00:00
for (NameMap::iterator iter = m_nameToCanonicalName.begin();
2012-06-10 16:50:54 +00:00
iter != m_nameToCanonicalName.end(); ++iter) {
if (CaselessCmp::equal(
2012-06-10 16:50:54 +00:00
iter->second, oldCanonical)) {
iter->second = newName;
}
}
}
return true;
}
void
2014-11-11 13:51:47 +00:00
Config::removeScreen(const String& name)
2012-06-10 16:50:54 +00:00
{
// get canonical name and find cell
2014-11-11 13:51:47 +00:00
String canonical = getCanonicalName(name);
CellMap::iterator index = m_map.find(canonical);
2012-06-10 16:50:54 +00:00
if (index == m_map.end()) {
return;
}
// remove from map
m_map.erase(index);
// disconnect
2014-11-11 13:51:47 +00:00
Name nameObj(this, name);
2012-06-10 16:50:54 +00:00
for (index = m_map.begin(); index != m_map.end(); ++index) {
index->second.remove(nameObj);
}
// remove aliases (and canonical name)
2014-11-11 13:51:47 +00:00
for (NameMap::iterator iter = m_nameToCanonicalName.begin();
2012-06-10 16:50:54 +00:00
iter != m_nameToCanonicalName.end(); ) {
if (iter->second == canonical) {
m_nameToCanonicalName.erase(iter++);
}
else {
++index;
}
}
}
void
2014-11-11 13:51:47 +00:00
Config::removeAllScreens()
2012-06-10 16:50:54 +00:00
{
m_map.clear();
m_nameToCanonicalName.clear();
}
bool
2014-11-11 13:51:47 +00:00
Config::addAlias(const String& canonical, const String& alias)
2012-06-10 16:50:54 +00:00
{
// alias name must not exist
if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) {
return false;
}
// canonical name must be known
if (m_map.find(canonical) == m_map.end()) {
return false;
}
// insert alias
m_nameToCanonicalName.insert(std::make_pair(alias, canonical));
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::removeAlias(const String& alias)
2012-06-10 16:50:54 +00:00
{
// must not be a canonical name
if (m_map.find(alias) != m_map.end()) {
return false;
}
// find alias
2014-11-11 13:51:47 +00:00
NameMap::iterator index = m_nameToCanonicalName.find(alias);
2012-06-10 16:50:54 +00:00
if (index == m_nameToCanonicalName.end()) {
return false;
}
// remove alias
m_nameToCanonicalName.erase(index);
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::removeAliases(const String& canonical)
2012-06-10 16:50:54 +00:00
{
// must be a canonical name
if (m_map.find(canonical) == m_map.end()) {
return false;
}
// find and removing matching aliases
2014-11-11 13:51:47 +00:00
for (NameMap::iterator index = m_nameToCanonicalName.begin();
2012-06-10 16:50:54 +00:00
index != m_nameToCanonicalName.end(); ) {
if (index->second == canonical && index->first != canonical) {
m_nameToCanonicalName.erase(index++);
}
else {
++index;
}
}
return true;
}
void
2014-11-11 13:51:47 +00:00
Config::removeAllAliases()
2012-06-10 16:50:54 +00:00
{
// remove all names
m_nameToCanonicalName.clear();
// put the canonical names back in
2014-11-11 13:51:47 +00:00
for (CellMap::iterator index = m_map.begin();
2012-06-10 16:50:54 +00:00
index != m_map.end(); ++index) {
m_nameToCanonicalName.insert(
std::make_pair(index->first, index->first));
}
}
bool
2014-11-11 13:51:47 +00:00
Config::connect(const String& srcName,
2012-06-10 16:50:54 +00:00
EDirection srcSide,
float srcStart, float srcEnd,
2014-11-11 13:51:47 +00:00
const String& dstName,
2012-06-10 16:50:54 +00:00
float dstStart, float dstEnd)
{
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
// find source cell
2014-11-11 13:51:47 +00:00
CellMap::iterator index = m_map.find(getCanonicalName(srcName));
2012-06-10 16:50:54 +00:00
if (index == m_map.end()) {
return false;
}
// add link
2014-11-11 13:51:47 +00:00
CellEdge srcEdge(srcSide, Interval(srcStart, srcEnd));
CellEdge dstEdge(dstName, srcSide, Interval(dstStart, dstEnd));
2012-06-10 16:50:54 +00:00
return index->second.add(srcEdge, dstEdge);
}
bool
2014-11-11 13:51:47 +00:00
Config::disconnect(const String& srcName, EDirection srcSide)
2012-06-10 16:50:54 +00:00
{
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
// find source cell
2014-11-11 13:51:47 +00:00
CellMap::iterator index = m_map.find(srcName);
2012-06-10 16:50:54 +00:00
if (index == m_map.end()) {
return false;
}
// disconnect side
index->second.remove(srcSide);
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::disconnect(const String& srcName, EDirection srcSide, float position)
2012-06-10 16:50:54 +00:00
{
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
// find source cell
2014-11-11 13:51:47 +00:00
CellMap::iterator index = m_map.find(srcName);
2012-06-10 16:50:54 +00:00
if (index == m_map.end()) {
return false;
}
// disconnect side
index->second.remove(srcSide, position);
return true;
}
void
2014-11-11 13:51:47 +00:00
Config::setSynergyAddress(const NetworkAddress& addr)
2012-06-10 16:50:54 +00:00
{
m_synergyAddress = addr;
}
bool
2014-11-11 13:51:47 +00:00
Config::addOption(const String& name, OptionID option, OptionValue value)
2012-06-10 16:50:54 +00:00
{
// find options
2014-11-11 13:51:47 +00:00
ScreenOptions* options = NULL;
2012-06-10 16:50:54 +00:00
if (name.empty()) {
options = &m_globalOptions;
}
else {
2014-11-11 13:51:47 +00:00
CellMap::iterator index = m_map.find(name);
2012-06-10 16:50:54 +00:00
if (index != m_map.end()) {
options = &index->second.m_options;
}
}
if (options == NULL) {
return false;
}
// add option
options->insert(std::make_pair(option, value));
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::removeOption(const String& name, OptionID option)
2012-06-10 16:50:54 +00:00
{
// find options
2014-11-11 13:51:47 +00:00
ScreenOptions* options = NULL;
2012-06-10 16:50:54 +00:00
if (name.empty()) {
options = &m_globalOptions;
}
else {
2014-11-11 13:51:47 +00:00
CellMap::iterator index = m_map.find(name);
2012-06-10 16:50:54 +00:00
if (index != m_map.end()) {
options = &index->second.m_options;
}
}
if (options == NULL) {
return false;
}
// remove option
options->erase(option);
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::removeOptions(const String& name)
2012-06-10 16:50:54 +00:00
{
// find options
2014-11-11 13:51:47 +00:00
ScreenOptions* options = NULL;
2012-06-10 16:50:54 +00:00
if (name.empty()) {
options = &m_globalOptions;
}
else {
2014-11-11 13:51:47 +00:00
CellMap::iterator index = m_map.find(name);
2012-06-10 16:50:54 +00:00
if (index != m_map.end()) {
options = &index->second.m_options;
}
}
if (options == NULL) {
return false;
}
// remove options
options->clear();
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::isValidScreenName(const String& name) const
2012-06-10 16:50:54 +00:00
{
// name is valid if matches validname
// name ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9]
// domain ::= . name
// validname ::= name domain*
// we also accept names ending in . because many OS X users have
// so misconfigured their systems.
// empty name is invalid
if (name.empty()) {
return false;
}
// check each dot separated part
2014-11-11 13:51:47 +00:00
String::size_type b = 0;
2012-06-10 16:50:54 +00:00
for (;;) {
// accept trailing .
if (b == name.size()) {
break;
}
// find end of part
2014-11-11 13:51:47 +00:00
String::size_type e = name.find('.', b);
if (e == String::npos) {
2012-06-10 16:50:54 +00:00
e = name.size();
}
// part may not be empty
if (e - b < 1) {
return false;
}
// check first and last characters
if (!(isalnum(name[b]) || name[b] == '_') ||
!(isalnum(name[e - 1]) || name[e - 1] == '_')) {
return false;
}
// check interior characters
2014-11-11 13:51:47 +00:00
for (String::size_type i = b; i < e; ++i) {
2012-06-10 16:50:54 +00:00
if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') {
return false;
}
}
// next part
if (e == name.size()) {
// no more parts
break;
}
b = e + 1;
}
return true;
}
2014-11-11 13:51:47 +00:00
Config::const_iterator
Config::begin() const
2012-06-10 16:50:54 +00:00
{
return const_iterator(m_map.begin());
}
2014-11-11 13:51:47 +00:00
Config::const_iterator
Config::end() const
2012-06-10 16:50:54 +00:00
{
return const_iterator(m_map.end());
}
2014-11-11 13:51:47 +00:00
Config::all_const_iterator
Config::beginAll() const
2012-06-10 16:50:54 +00:00
{
return m_nameToCanonicalName.begin();
}
2014-11-11 13:51:47 +00:00
Config::all_const_iterator
Config::endAll() const
2012-06-10 16:50:54 +00:00
{
return m_nameToCanonicalName.end();
}
bool
2014-11-11 13:51:47 +00:00
Config::isScreen(const String& name) const
2012-06-10 16:50:54 +00:00
{
return (m_nameToCanonicalName.count(name) > 0);
}
bool
2014-11-11 13:51:47 +00:00
Config::isCanonicalName(const String& name) const
2012-06-10 16:50:54 +00:00
{
return (!name.empty() &&
CaselessCmp::equal(getCanonicalName(name), name));
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
String
Config::getCanonicalName(const String& name) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
NameMap::const_iterator index = m_nameToCanonicalName.find(name);
2012-06-10 16:50:54 +00:00
if (index == m_nameToCanonicalName.end()) {
2014-11-11 13:51:47 +00:00
return String();
2012-06-10 16:50:54 +00:00
}
else {
return index->second;
}
}
2014-11-11 13:51:47 +00:00
String
Config::getNeighbor(const String& srcName, EDirection srcSide,
2012-06-10 16:50:54 +00:00
float position, float* positionOut) const
{
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
// find source cell
2014-11-11 13:51:47 +00:00
CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
2012-06-10 16:50:54 +00:00
if (index == m_map.end()) {
2014-11-11 13:51:47 +00:00
return String();
2012-06-10 16:50:54 +00:00
}
// find edge
2014-11-11 13:51:47 +00:00
const CellEdge* srcEdge, *dstEdge;
2012-06-10 16:50:54 +00:00
if (!index->second.getLink(srcSide, position, srcEdge, dstEdge)) {
// no neighbor
return "";
}
else {
// compute position on neighbor
if (positionOut != NULL) {
*positionOut =
dstEdge->inverseTransform(srcEdge->transform(position));
}
// return neighbor's name
return getCanonicalName(dstEdge->getName());
}
}
bool
2014-11-11 13:51:47 +00:00
Config::hasNeighbor(const String& srcName, EDirection srcSide) const
2012-06-10 16:50:54 +00:00
{
return hasNeighbor(srcName, srcSide, 0.0f, 1.0f);
}
bool
2014-11-11 13:51:47 +00:00
Config::hasNeighbor(const String& srcName, EDirection srcSide,
2012-06-10 16:50:54 +00:00
float start, float end) const
{
assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
// find source cell
2014-11-11 13:51:47 +00:00
CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
2012-06-10 16:50:54 +00:00
if (index == m_map.end()) {
return false;
}
2014-11-11 13:51:47 +00:00
return index->second.overlaps(CellEdge(srcSide, Interval(start, end)));
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
Config::link_const_iterator
Config::beginNeighbor(const String& srcName) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
2012-06-10 16:50:54 +00:00
assert(index != m_map.end());
return index->second.begin();
}
2014-11-11 13:51:47 +00:00
Config::link_const_iterator
Config::endNeighbor(const String& srcName) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
CellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
2012-06-10 16:50:54 +00:00
assert(index != m_map.end());
return index->second.end();
}
2014-11-11 13:51:47 +00:00
const NetworkAddress&
Config::getSynergyAddress() const
2012-06-10 16:50:54 +00:00
{
return m_synergyAddress;
}
2014-11-11 13:51:47 +00:00
const Config::ScreenOptions*
Config::getOptions(const String& name) const
2012-06-10 16:50:54 +00:00
{
// find options
2014-11-11 13:51:47 +00:00
const ScreenOptions* options = NULL;
2012-06-10 16:50:54 +00:00
if (name.empty()) {
options = &m_globalOptions;
}
else {
2014-11-11 13:51:47 +00:00
CellMap::const_iterator index = m_map.find(name);
2012-06-10 16:50:54 +00:00
if (index != m_map.end()) {
options = &index->second.m_options;
}
}
// return options
return options;
}
bool
2014-11-11 13:51:47 +00:00
Config::hasLockToScreenAction() const
2012-06-10 16:50:54 +00:00
{
return m_hasLockToScreenAction;
}
bool
2014-11-11 13:51:47 +00:00
Config::operator==(const Config& x) const
2012-06-10 16:50:54 +00:00
{
if (m_synergyAddress != x.m_synergyAddress) {
return false;
}
if (m_map.size() != x.m_map.size()) {
return false;
}
if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) {
return false;
}
// compare global options
if (m_globalOptions != x.m_globalOptions) {
return false;
}
2014-11-11 13:51:47 +00:00
for (CellMap::const_iterator index1 = m_map.begin(),
2012-06-10 16:50:54 +00:00
index2 = x.m_map.begin();
index1 != m_map.end(); ++index1, ++index2) {
// compare names
if (!CaselessCmp::equal(index1->first, index2->first)) {
2012-06-10 16:50:54 +00:00
return false;
}
// compare cells
if (index1->second != index2->second) {
return false;
}
}
2014-11-11 13:51:47 +00:00
for (NameMap::const_iterator index1 = m_nameToCanonicalName.begin(),
2012-06-10 16:50:54 +00:00
index2 = x.m_nameToCanonicalName.begin();
index1 != m_nameToCanonicalName.end();
++index1, ++index2) {
if (!CaselessCmp::equal(index1->first, index2->first) ||
!CaselessCmp::equal(index1->second, index2->second)) {
2012-06-10 16:50:54 +00:00
return false;
}
}
// compare input filters
if (m_inputFilter != x.m_inputFilter) {
return false;
}
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::operator!=(const Config& x) const
2012-06-10 16:50:54 +00:00
{
return !operator==(x);
}
void
2014-11-11 13:51:47 +00:00
Config::read(ConfigReadContext& context)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
Config tmp(m_events);
2014-01-24 17:53:03 +00:00
while (context.getStream()) {
2012-06-10 16:50:54 +00:00
tmp.readSection(context);
}
*this = tmp;
}
const char*
2014-11-11 13:51:47 +00:00
Config::dirName(EDirection dir)
2012-06-10 16:50:54 +00:00
{
static const char* s_name[] = { "left", "right", "up", "down" };
assert(dir >= kFirstDirection && dir <= kLastDirection);
return s_name[dir - kFirstDirection];
}
2014-11-11 13:51:47 +00:00
InputFilter*
Config::getInputFilter()
2012-06-10 16:50:54 +00:00
{
return &m_inputFilter;
}
2014-11-11 13:51:47 +00:00
String
Config::formatInterval(const Interval& x)
2012-06-10 16:50:54 +00:00
{
if (x.first == 0.0f && x.second == 1.0f) {
return "";
}
return synergy::string::sprintf("(%d,%d)", (int)(x.first * 100.0f + 0.5f),
2012-06-10 16:50:54 +00:00
(int)(x.second * 100.0f + 0.5f));
}
void
2014-11-11 13:51:47 +00:00
Config::readSection(ConfigReadContext& s)
2012-06-10 16:50:54 +00:00
{
static const char s_section[] = "section:";
static const char s_options[] = "options";
static const char s_screens[] = "screens";
static const char s_links[] = "links";
static const char s_aliases[] = "aliases";
2014-11-11 13:51:47 +00:00
String line;
2012-06-10 16:50:54 +00:00
if (!s.readLine(line)) {
// no more sections
return;
}
// should be a section header
if (line.find(s_section) != 0) {
throw XConfigRead(s, "found data outside section");
}
// get section name
2014-11-11 13:51:47 +00:00
String::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1);
if (i == String::npos) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(s, "section name is missing");
}
2014-11-11 13:51:47 +00:00
String name = line.substr(i);
2012-06-10 16:50:54 +00:00
i = name.find_first_of(" \t");
2014-11-11 13:51:47 +00:00
if (i != String::npos) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(s, "unexpected data after section name");
}
// read section
if (name == s_options) {
readSectionOptions(s);
}
else if (name == s_screens) {
readSectionScreens(s);
}
else if (name == s_links) {
readSectionLinks(s);
}
else if (name == s_aliases) {
readSectionAliases(s);
}
else {
throw XConfigRead(s, "unknown section name \"%{1}\"", name);
}
}
void
2014-11-11 13:51:47 +00:00
Config::readSectionOptions(ConfigReadContext& s)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String line;
2012-06-10 16:50:54 +00:00
while (s.readLine(line)) {
// check for end of section
if (line == "end") {
return;
}
// parse argument: `nameAndArgs = [values][;[values]]'
// nameAndArgs := <name>[(arg[,...])]
// values := valueAndArgs[,valueAndArgs]...
// valueAndArgs := <value>[(arg[,...])]
2014-11-11 13:51:47 +00:00
String::size_type i = 0;
String name, value;
ConfigReadContext::ArgList nameArgs, valueArgs;
2012-06-10 16:50:54 +00:00
s.parseNameWithArgs("name", line, "=", i, name, nameArgs);
++i;
s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
bool handled = true;
if (name == "address") {
try {
2014-11-11 13:51:47 +00:00
m_synergyAddress = NetworkAddress(value, kDefaultPort);
2012-06-10 16:50:54 +00:00
m_synergyAddress.resolve();
}
catch (XSocketAddress& e) {
throw XConfigRead(s,
2014-11-11 13:51:47 +00:00
String("invalid address argument ") + e.what());
2012-06-10 16:50:54 +00:00
}
}
else if (name == "heartbeat") {
addOption("", kOptionHeartbeat, s.parseInt(value));
}
else if (name == "switchCorners") {
addOption("", kOptionScreenSwitchCorners, s.parseCorners(value));
}
else if (name == "switchCornerSize") {
addOption("", kOptionScreenSwitchCornerSize, s.parseInt(value));
}
else if (name == "switchDelay") {
addOption("", kOptionScreenSwitchDelay, s.parseInt(value));
}
else if (name == "switchDoubleTap") {
addOption("", kOptionScreenSwitchTwoTap, s.parseInt(value));
}
else if (name == "switchNeedsShift") {
addOption("", kOptionScreenSwitchNeedsShift, s.parseBoolean(value));
}
else if (name == "switchNeedsControl") {
addOption("", kOptionScreenSwitchNeedsControl, s.parseBoolean(value));
}
else if (name == "switchNeedsAlt") {
addOption("", kOptionScreenSwitchNeedsAlt, s.parseBoolean(value));
}
else if (name == "screenSaverSync") {
addOption("", kOptionScreenSaverSync, s.parseBoolean(value));
}
else if (name == "relativeMouseMoves") {
addOption("", kOptionRelativeMouseMoves, s.parseBoolean(value));
}
else if (name == "win32KeepForeground") {
addOption("", kOptionWin32KeepForeground, s.parseBoolean(value));
}
else {
handled = false;
}
if (handled) {
// make sure handled options aren't followed by more values
if (i < line.size() && (line[i] == ',' || line[i] == ';')) {
throw XConfigRead(s, "to many arguments to %s", name.c_str());
}
}
else {
// make filter rule
2014-11-11 13:51:47 +00:00
InputFilter::Rule rule(parseCondition(s, name, nameArgs));
2012-06-10 16:50:54 +00:00
// save first action (if any)
if (!value.empty() || line[i] != ';') {
parseAction(s, value, valueArgs, rule, true);
}
// get remaining activate actions
while (i < line.length() && line[i] != ';') {
++i;
s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
parseAction(s, value, valueArgs, rule, true);
}
// get deactivate actions
if (i < line.length() && line[i] == ';') {
// allow trailing ';'
i = line.find_first_not_of(" \t", i + 1);
2014-11-11 13:51:47 +00:00
if (i == String::npos) {
2012-06-10 16:50:54 +00:00
i = line.length();
}
else {
--i;
}
// get actions
while (i < line.length()) {
++i;
s.parseNameWithArgs("value", line, ",\n",
i, value, valueArgs);
parseAction(s, value, valueArgs, rule, false);
}
}
// add rule
m_inputFilter.addFilterRule(rule);
}
}
throw XConfigRead(s, "unexpected end of options section");
}
void
2014-11-11 13:51:47 +00:00
Config::readSectionScreens(ConfigReadContext& s)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String line;
String screen;
2012-06-10 16:50:54 +00:00
while (s.readLine(line)) {
// check for end of section
if (line == "end") {
return;
}
// see if it's the next screen
if (line[line.size() - 1] == ':') {
// strip :
screen = line.substr(0, line.size() - 1);
// verify validity of screen name
if (!isValidScreenName(screen)) {
throw XConfigRead(s, "invalid screen name \"%{1}\"", screen);
}
// add the screen to the configuration
if (!addScreen(screen)) {
throw XConfigRead(s, "duplicate screen name \"%{1}\"", screen);
}
}
else if (screen.empty()) {
throw XConfigRead(s, "argument before first screen");
}
else {
// parse argument: `<name>=<value>'
2014-11-11 13:51:47 +00:00
String::size_type i = line.find_first_of(" \t=");
2012-06-10 16:50:54 +00:00
if (i == 0) {
throw XConfigRead(s, "missing argument name");
}
2014-11-11 13:51:47 +00:00
if (i == String::npos) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(s, "missing =");
}
2014-11-11 13:51:47 +00:00
String name = line.substr(0, i);
2012-06-10 16:50:54 +00:00
i = line.find_first_not_of(" \t", i);
2014-11-11 13:51:47 +00:00
if (i == String::npos || line[i] != '=') {
2012-06-10 16:50:54 +00:00
throw XConfigRead(s, "missing =");
}
i = line.find_first_not_of(" \t", i + 1);
2014-11-11 13:51:47 +00:00
String value;
if (i != String::npos) {
2012-06-10 16:50:54 +00:00
value = line.substr(i);
}
// handle argument
if (name == "halfDuplexCapsLock") {
addOption(screen, kOptionHalfDuplexCapsLock,
s.parseBoolean(value));
}
else if (name == "halfDuplexNumLock") {
addOption(screen, kOptionHalfDuplexNumLock,
s.parseBoolean(value));
}
else if (name == "halfDuplexScrollLock") {
addOption(screen, kOptionHalfDuplexScrollLock,
s.parseBoolean(value));
}
else if (name == "shift") {
addOption(screen, kOptionModifierMapForShift,
s.parseModifierKey(value));
}
else if (name == "ctrl") {
addOption(screen, kOptionModifierMapForControl,
s.parseModifierKey(value));
}
else if (name == "alt") {
addOption(screen, kOptionModifierMapForAlt,
s.parseModifierKey(value));
}
else if (name == "altgr") {
addOption(screen, kOptionModifierMapForAltGr,
s.parseModifierKey(value));
}
else if (name == "meta") {
addOption(screen, kOptionModifierMapForMeta,
s.parseModifierKey(value));
}
else if (name == "super") {
addOption(screen, kOptionModifierMapForSuper,
s.parseModifierKey(value));
}
else if (name == "xtestIsXineramaUnaware") {
addOption(screen, kOptionXTestXineramaUnaware,
s.parseBoolean(value));
}
else if (name == "switchCorners") {
addOption(screen, kOptionScreenSwitchCorners,
s.parseCorners(value));
}
else if (name == "switchCornerSize") {
addOption(screen, kOptionScreenSwitchCornerSize,
s.parseInt(value));
}
else if (name == "preserveFocus") {
addOption(screen, kOptionScreenPreserveFocus,
s.parseBoolean(value));
}
else {
// unknown argument
throw XConfigRead(s, "unknown argument \"%{1}\"", name);
}
}
}
throw XConfigRead(s, "unexpected end of screens section");
}
void
2014-11-11 13:51:47 +00:00
Config::readSectionLinks(ConfigReadContext& s)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String line;
String screen;
2012-06-10 16:50:54 +00:00
while (s.readLine(line)) {
// check for end of section
if (line == "end") {
return;
}
// see if it's the next screen
if (line[line.size() - 1] == ':') {
// strip :
screen = line.substr(0, line.size() - 1);
// verify we know about the screen
if (!isScreen(screen)) {
throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
}
if (!isCanonicalName(screen)) {
throw XConfigRead(s, "cannot use screen name alias here");
}
}
else if (screen.empty()) {
throw XConfigRead(s, "argument before first screen");
}
else {
// parse argument: `<name>[(<s0>,<e0>)]=<value>[(<s1>,<e1>)]'
// the stuff in brackets is optional. interval values must be
// in the range [0,100] and start < end. if not given the
// interval is taken to be (0,100).
2014-11-11 13:51:47 +00:00
String::size_type i = 0;
String side, dstScreen, srcArgString, dstArgString;
ConfigReadContext::ArgList srcArgs, dstArgs;
2012-06-10 16:50:54 +00:00
s.parseNameWithArgs("link", line, "=", i, side, srcArgs);
++i;
s.parseNameWithArgs("screen", line, "", i, dstScreen, dstArgs);
2014-11-11 13:51:47 +00:00
Interval srcInterval(s.parseInterval(srcArgs));
Interval dstInterval(s.parseInterval(dstArgs));
2012-06-10 16:50:54 +00:00
// handle argument
EDirection dir;
if (side == "left") {
dir = kLeft;
}
else if (side == "right") {
dir = kRight;
}
else if (side == "up") {
dir = kTop;
}
else if (side == "down") {
dir = kBottom;
}
else {
// unknown argument
throw XConfigRead(s, "unknown side \"%{1}\" in link", side);
}
if (!isScreen(dstScreen)) {
throw XConfigRead(s, "unknown screen name \"%{1}\"", dstScreen);
}
if (!connect(screen, dir,
srcInterval.first, srcInterval.second,
dstScreen,
dstInterval.first, dstInterval.second)) {
throw XConfigRead(s, "overlapping range");
}
}
}
throw XConfigRead(s, "unexpected end of links section");
}
void
2014-11-11 13:51:47 +00:00
Config::readSectionAliases(ConfigReadContext& s)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String line;
String screen;
2012-06-10 16:50:54 +00:00
while (s.readLine(line)) {
// check for end of section
if (line == "end") {
return;
}
// see if it's the next screen
if (line[line.size() - 1] == ':') {
// strip :
screen = line.substr(0, line.size() - 1);
// verify we know about the screen
if (!isScreen(screen)) {
throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
}
if (!isCanonicalName(screen)) {
throw XConfigRead(s, "cannot use screen name alias here");
}
}
else if (screen.empty()) {
throw XConfigRead(s, "argument before first screen");
}
else {
// verify validity of screen name
if (!isValidScreenName(line)) {
throw XConfigRead(s, "invalid screen alias \"%{1}\"", line);
}
// add alias
if (!addAlias(screen, line)) {
throw XConfigRead(s, "alias \"%{1}\" is already used", line);
}
}
}
throw XConfigRead(s, "unexpected end of aliases section");
}
2014-11-11 13:51:47 +00:00
InputFilter::Condition*
Config::parseCondition(ConfigReadContext& s,
const String& name, const std::vector<String>& args)
2012-06-10 16:50:54 +00:00
{
if (name == "keystroke") {
if (args.size() != 1) {
throw XConfigRead(s, "syntax for condition: keystroke(modifiers+key)");
}
2014-11-11 13:51:47 +00:00
IPlatformScreen::KeyInfo* keyInfo = s.parseKeystroke(args[0]);
2012-06-10 16:50:54 +00:00
2014-11-11 13:51:47 +00:00
return new InputFilter::KeystrokeCondition(m_events, keyInfo);
2012-06-10 16:50:54 +00:00
}
if (name == "mousebutton") {
if (args.size() != 1) {
throw XConfigRead(s, "syntax for condition: mousebutton(modifiers+button)");
}
2014-11-11 13:51:47 +00:00
IPlatformScreen::ButtonInfo* mouseInfo = s.parseMouse(args[0]);
2012-06-10 16:50:54 +00:00
2014-11-11 13:51:47 +00:00
return new InputFilter::MouseButtonCondition(m_events, mouseInfo);
2012-06-10 16:50:54 +00:00
}
if (name == "connect") {
if (args.size() != 1) {
throw XConfigRead(s, "syntax for condition: connect([screen])");
}
2014-11-11 13:51:47 +00:00
String screen = args[0];
2012-06-10 16:50:54 +00:00
if (isScreen(screen)) {
screen = getCanonicalName(screen);
}
else if (!screen.empty()) {
throw XConfigRead(s, "unknown screen name \"%{1}\" in connect", screen);
}
2014-11-11 13:51:47 +00:00
return new InputFilter::ScreenConnectedCondition(m_events, screen);
2012-06-10 16:50:54 +00:00
}
throw XConfigRead(s, "unknown argument \"%{1}\"", name);
}
void
2014-11-11 13:51:47 +00:00
Config::parseAction(ConfigReadContext& s,
const String& name, const std::vector<String>& args,
InputFilter::Rule& rule, bool activate)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
InputFilter::Action* action;
2012-06-10 16:50:54 +00:00
if (name == "keystroke" || name == "keyDown" || name == "keyUp") {
if (args.size() < 1 || args.size() > 2) {
throw XConfigRead(s, "syntax for action: keystroke(modifiers+key[,screens])");
}
2014-11-11 13:51:47 +00:00
IPlatformScreen::KeyInfo* keyInfo;
2012-06-10 16:50:54 +00:00
if (args.size() == 1) {
keyInfo = s.parseKeystroke(args[0]);
}
else {
2014-11-11 13:51:47 +00:00
std::set<String> screens;
2012-06-10 16:50:54 +00:00
parseScreens(s, args[1], screens);
keyInfo = s.parseKeystroke(args[0], screens);
}
if (name == "keystroke") {
2014-11-11 13:51:47 +00:00
IPlatformScreen::KeyInfo* keyInfo2 =
IKeyState::KeyInfo::alloc(*keyInfo);
action = new InputFilter::KeystrokeAction(m_events, keyInfo2, true);
2012-06-10 16:50:54 +00:00
rule.adoptAction(action, true);
2014-11-11 13:51:47 +00:00
action = new InputFilter::KeystrokeAction(m_events, keyInfo, false);
2012-06-10 16:50:54 +00:00
activate = false;
}
else if (name == "keyDown") {
2014-11-11 13:51:47 +00:00
action = new InputFilter::KeystrokeAction(m_events, keyInfo, true);
2012-06-10 16:50:54 +00:00
}
else {
2014-11-11 13:51:47 +00:00
action = new InputFilter::KeystrokeAction(m_events, keyInfo, false);
2012-06-10 16:50:54 +00:00
}
}
else if (name == "mousebutton" ||
name == "mouseDown" || name == "mouseUp") {
if (args.size() != 1) {
throw XConfigRead(s, "syntax for action: mousebutton(modifiers+button)");
}
2014-11-11 13:51:47 +00:00
IPlatformScreen::ButtonInfo* mouseInfo = s.parseMouse(args[0]);
2012-06-10 16:50:54 +00:00
if (name == "mousebutton") {
2014-11-11 13:51:47 +00:00
IPlatformScreen::ButtonInfo* mouseInfo2 =
IPlatformScreen::ButtonInfo::alloc(*mouseInfo);
action = new InputFilter::MouseButtonAction(m_events, mouseInfo2, true);
2012-06-10 16:50:54 +00:00
rule.adoptAction(action, true);
2014-11-11 13:51:47 +00:00
action = new InputFilter::MouseButtonAction(m_events, mouseInfo, false);
2012-06-10 16:50:54 +00:00
activate = false;
}
else if (name == "mouseDown") {
2014-11-11 13:51:47 +00:00
action = new InputFilter::MouseButtonAction(m_events, mouseInfo, true);
2012-06-10 16:50:54 +00:00
}
else {
2014-11-11 13:51:47 +00:00
action = new InputFilter::MouseButtonAction(m_events, mouseInfo, false);
2012-06-10 16:50:54 +00:00
}
}
/* XXX -- not supported
else if (name == "modifier") {
if (args.size() != 1) {
throw XConfigRead(s, "syntax for action: modifier(modifiers)");
}
KeyModifierMask mask = s.parseModifier(args[0]);
2014-11-11 13:51:47 +00:00
action = new InputFilter::ModifierAction(mask, ~mask);
2012-06-10 16:50:54 +00:00
}
*/
else if (name == "switchToScreen") {
if (args.size() != 1) {
throw XConfigRead(s, "syntax for action: switchToScreen(name)");
}
2014-11-11 13:51:47 +00:00
String screen = args[0];
2012-06-10 16:50:54 +00:00
if (isScreen(screen)) {
screen = getCanonicalName(screen);
}
else if (!screen.empty()) {
throw XConfigRead(s, "unknown screen name in switchToScreen");
}
2014-11-11 13:51:47 +00:00
action = new InputFilter::SwitchToScreenAction(m_events, screen);
2012-06-10 16:50:54 +00:00
}
else if (name == "switchInDirection") {
if (args.size() != 1) {
throw XConfigRead(s, "syntax for action: switchInDirection(<left|right|up|down>)");
}
EDirection direction;
if (args[0] == "left") {
direction = kLeft;
}
else if (args[0] == "right") {
direction = kRight;
}
else if (args[0] == "up") {
direction = kTop;
}
else if (args[0] == "down") {
direction = kBottom;
}
else {
throw XConfigRead(s, "unknown direction \"%{1}\" in switchToScreen", args[0]);
}
2014-11-11 13:51:47 +00:00
action = new InputFilter::SwitchInDirectionAction(m_events, direction);
2012-06-10 16:50:54 +00:00
}
else if (name == "lockCursorToScreen") {
if (args.size() > 1) {
throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
}
2014-11-11 13:51:47 +00:00
InputFilter::LockCursorToScreenAction::Mode mode =
InputFilter::LockCursorToScreenAction::kToggle;
2012-06-10 16:50:54 +00:00
if (args.size() == 1) {
if (args[0] == "off") {
2014-11-11 13:51:47 +00:00
mode = InputFilter::LockCursorToScreenAction::kOff;
2012-06-10 16:50:54 +00:00
}
else if (args[0] == "on") {
2014-11-11 13:51:47 +00:00
mode = InputFilter::LockCursorToScreenAction::kOn;
2012-06-10 16:50:54 +00:00
}
else if (args[0] == "toggle") {
2014-11-11 13:51:47 +00:00
mode = InputFilter::LockCursorToScreenAction::kToggle;
2012-06-10 16:50:54 +00:00
}
else {
throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
}
}
2014-11-11 13:51:47 +00:00
if (mode != InputFilter::LockCursorToScreenAction::kOff) {
2012-06-10 16:50:54 +00:00
m_hasLockToScreenAction = true;
}
2014-11-11 13:51:47 +00:00
action = new InputFilter::LockCursorToScreenAction(m_events, mode);
2012-06-10 16:50:54 +00:00
}
else if (name == "keyboardBroadcast") {
if (args.size() > 2) {
throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
}
2014-11-11 13:51:47 +00:00
InputFilter::KeyboardBroadcastAction::Mode mode =
InputFilter::KeyboardBroadcastAction::kToggle;
2012-06-10 16:50:54 +00:00
if (args.size() >= 1) {
if (args[0] == "off") {
2014-11-11 13:51:47 +00:00
mode = InputFilter::KeyboardBroadcastAction::kOff;
2012-06-10 16:50:54 +00:00
}
else if (args[0] == "on") {
2014-11-11 13:51:47 +00:00
mode = InputFilter::KeyboardBroadcastAction::kOn;
2012-06-10 16:50:54 +00:00
}
else if (args[0] == "toggle") {
2014-11-11 13:51:47 +00:00
mode = InputFilter::KeyboardBroadcastAction::kToggle;
2012-06-10 16:50:54 +00:00
}
else {
throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
}
}
2014-11-11 13:51:47 +00:00
std::set<String> screens;
2012-06-10 16:50:54 +00:00
if (args.size() >= 2) {
parseScreens(s, args[1], screens);
}
2014-11-11 13:51:47 +00:00
action = new InputFilter::KeyboardBroadcastAction(m_events, mode, screens);
2012-06-10 16:50:54 +00:00
}
else {
throw XConfigRead(s, "unknown action argument \"%{1}\"", name);
}
rule.adoptAction(action, activate);
}
void
2014-11-11 13:51:47 +00:00
Config::parseScreens(ConfigReadContext& c,
const String& s, std::set<String>& screens) const
2012-06-10 16:50:54 +00:00
{
screens.clear();
2014-11-11 13:51:47 +00:00
String::size_type i = 0;
2012-06-10 16:50:54 +00:00
while (i < s.size()) {
// find end of next screen name
2014-11-11 13:51:47 +00:00
String::size_type j = s.find(':', i);
if (j == String::npos) {
2012-06-10 16:50:54 +00:00
j = s.size();
}
// extract name
2014-11-11 13:51:47 +00:00
String rawName;
2012-06-10 16:50:54 +00:00
i = s.find_first_not_of(" \t", i);
if (i < j) {
rawName = s.substr(i, s.find_last_not_of(" \t", j - 1) - i + 1);
}
// add name
if (rawName == "*") {
screens.insert("*");
}
else if (!rawName.empty()) {
2014-11-11 13:51:47 +00:00
String name = getCanonicalName(rawName);
2012-06-10 16:50:54 +00:00
if (name.empty()) {
throw XConfigRead(c, "unknown screen name \"%{1}\"", rawName);
}
screens.insert(name);
}
// next
i = j + 1;
}
}
const char*
2014-11-11 13:51:47 +00:00
Config::getOptionName(OptionID id)
2012-06-10 16:50:54 +00:00
{
if (id == kOptionHalfDuplexCapsLock) {
return "halfDuplexCapsLock";
}
if (id == kOptionHalfDuplexNumLock) {
return "halfDuplexNumLock";
}
if (id == kOptionHalfDuplexScrollLock) {
return "halfDuplexScrollLock";
}
if (id == kOptionModifierMapForShift) {
return "shift";
}
if (id == kOptionModifierMapForControl) {
return "ctrl";
}
if (id == kOptionModifierMapForAlt) {
return "alt";
}
if (id == kOptionModifierMapForAltGr) {
return "altgr";
}
if (id == kOptionModifierMapForMeta) {
return "meta";
}
if (id == kOptionModifierMapForSuper) {
return "super";
}
if (id == kOptionHeartbeat) {
return "heartbeat";
}
if (id == kOptionScreenSwitchCorners) {
return "switchCorners";
}
if (id == kOptionScreenSwitchCornerSize) {
return "switchCornerSize";
}
if (id == kOptionScreenSwitchDelay) {
return "switchDelay";
}
if (id == kOptionScreenSwitchTwoTap) {
return "switchDoubleTap";
}
if (id == kOptionScreenSwitchNeedsShift) {
return "switchNeedsShift";
}
if (id == kOptionScreenSwitchNeedsControl) {
return "switchNeedsControl";
}
if (id == kOptionScreenSwitchNeedsAlt) {
return "switchNeedsAlt";
}
if (id == kOptionScreenSaverSync) {
return "screenSaverSync";
}
if (id == kOptionXTestXineramaUnaware) {
return "xtestIsXineramaUnaware";
}
if (id == kOptionRelativeMouseMoves) {
return "relativeMouseMoves";
}
if (id == kOptionWin32KeepForeground) {
return "win32KeepForeground";
}
if (id == kOptionScreenPreserveFocus) {
return "preserveFocus";
}
return NULL;
}
2014-11-11 13:51:47 +00:00
String
Config::getOptionValue(OptionID id, OptionValue value)
2012-06-10 16:50:54 +00:00
{
if (id == kOptionHalfDuplexCapsLock ||
id == kOptionHalfDuplexNumLock ||
id == kOptionHalfDuplexScrollLock ||
id == kOptionScreenSwitchNeedsShift ||
id == kOptionScreenSwitchNeedsControl ||
id == kOptionScreenSwitchNeedsAlt ||
id == kOptionScreenSaverSync ||
id == kOptionXTestXineramaUnaware ||
id == kOptionRelativeMouseMoves ||
id == kOptionWin32KeepForeground ||
id == kOptionScreenPreserveFocus) {
return (value != 0) ? "true" : "false";
}
if (id == kOptionModifierMapForShift ||
id == kOptionModifierMapForControl ||
id == kOptionModifierMapForAlt ||
id == kOptionModifierMapForAltGr ||
id == kOptionModifierMapForMeta ||
id == kOptionModifierMapForSuper) {
switch (value) {
case kKeyModifierIDShift:
return "shift";
case kKeyModifierIDControl:
return "ctrl";
case kKeyModifierIDAlt:
return "alt";
case kKeyModifierIDAltGr:
return "altgr";
case kKeyModifierIDMeta:
return "meta";
case kKeyModifierIDSuper:
return "super";
default:
return "none";
}
}
if (id == kOptionHeartbeat ||
id == kOptionScreenSwitchCornerSize ||
id == kOptionScreenSwitchDelay ||
id == kOptionScreenSwitchTwoTap) {
return synergy::string::sprintf("%d", value);
2012-06-10 16:50:54 +00:00
}
if (id == kOptionScreenSwitchCorners) {
std::string result("none");
if ((value & kTopLeftMask) != 0) {
result += " +top-left";
}
if ((value & kTopRightMask) != 0) {
result += " +top-right";
}
if ((value & kBottomLeftMask) != 0) {
result += " +bottom-left";
}
if ((value & kBottomRightMask) != 0) {
result += " +bottom-right";
}
return result;
}
return "";
}
//
2014-11-11 13:51:47 +00:00
// Config::Name
2012-06-10 16:50:54 +00:00
//
2014-11-11 13:51:47 +00:00
Config::Name::Name(Config* config, const String& name) :
2012-06-10 16:50:54 +00:00
m_config(config),
m_name(config->getCanonicalName(name))
{
// do nothing
}
bool
2014-11-11 13:51:47 +00:00
Config::Name::operator==(const String& name) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String canonical = m_config->getCanonicalName(name);
return CaselessCmp::equal(canonical, m_name);
2012-06-10 16:50:54 +00:00
}
//
2014-11-11 13:51:47 +00:00
// Config::CellEdge
2012-06-10 16:50:54 +00:00
//
2014-11-11 13:51:47 +00:00
Config::CellEdge::CellEdge(EDirection side, float position)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
init("", side, Interval(position, position));
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
Config::CellEdge::CellEdge(EDirection side, const Interval& interval)
2012-06-10 16:50:54 +00:00
{
assert(interval.first >= 0.0f);
assert(interval.second <= 1.0f);
assert(interval.first < interval.second);
init("", side, interval);
}
2014-11-11 13:51:47 +00:00
Config::CellEdge::CellEdge(const String& name,
EDirection side, const Interval& interval)
2012-06-10 16:50:54 +00:00
{
assert(interval.first >= 0.0f);
assert(interval.second <= 1.0f);
assert(interval.first < interval.second);
init(name, side, interval);
}
2014-11-11 13:51:47 +00:00
Config::CellEdge::~CellEdge()
2012-06-10 16:50:54 +00:00
{
// do nothing
}
void
2014-11-11 13:51:47 +00:00
Config::CellEdge::init(const String& name, EDirection side,
const Interval& interval)
2012-06-10 16:50:54 +00:00
{
assert(side != kNoDirection);
m_name = name;
m_side = side;
m_interval = interval;
}
2014-11-11 13:51:47 +00:00
Config::Interval
Config::CellEdge::getInterval() const
2012-06-10 16:50:54 +00:00
{
return m_interval;
}
void
2014-11-11 13:51:47 +00:00
Config::CellEdge::setName(const String& newName)
2012-06-10 16:50:54 +00:00
{
m_name = newName;
}
2014-11-11 13:51:47 +00:00
String
Config::CellEdge::getName() const
2012-06-10 16:50:54 +00:00
{
return m_name;
}
EDirection
2014-11-11 13:51:47 +00:00
Config::CellEdge::getSide() const
2012-06-10 16:50:54 +00:00
{
return m_side;
}
bool
2014-11-11 13:51:47 +00:00
Config::CellEdge::overlaps(const CellEdge& edge) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
const Interval& x = m_interval;
const Interval& y = edge.m_interval;
2012-06-10 16:50:54 +00:00
if (m_side != edge.m_side) {
return false;
}
return (x.first >= y.first && x.first < y.second) ||
(x.second > y.first && x.second <= y.second) ||
(y.first >= x.first && y.first < x.second) ||
(y.second > x.first && y.second <= x.second);
}
bool
2014-11-11 13:51:47 +00:00
Config::CellEdge::isInside(float x) const
2012-06-10 16:50:54 +00:00
{
return (x >= m_interval.first && x < m_interval.second);
}
float
2014-11-11 13:51:47 +00:00
Config::CellEdge::transform(float x) const
2012-06-10 16:50:54 +00:00
{
return (x - m_interval.first) / (m_interval.second - m_interval.first);
}
float
2014-11-11 13:51:47 +00:00
Config::CellEdge::inverseTransform(float x) const
2012-06-10 16:50:54 +00:00
{
return x * (m_interval.second - m_interval.first) + m_interval.first;
}
bool
2014-11-11 13:51:47 +00:00
Config::CellEdge::operator<(const CellEdge& o) const
2012-06-10 16:50:54 +00:00
{
if (static_cast<int>(m_side) < static_cast<int>(o.m_side)) {
return true;
}
else if (static_cast<int>(m_side) > static_cast<int>(o.m_side)) {
return false;
}
return (m_interval.first < o.m_interval.first);
}
bool
2014-11-11 13:51:47 +00:00
Config::CellEdge::operator==(const CellEdge& x) const
2012-06-10 16:50:54 +00:00
{
return (m_side == x.m_side && m_interval == x.m_interval);
}
bool
2014-11-11 13:51:47 +00:00
Config::CellEdge::operator!=(const CellEdge& x) const
2012-06-10 16:50:54 +00:00
{
return !operator==(x);
}
//
2014-11-11 13:51:47 +00:00
// Config::Cell
2012-06-10 16:50:54 +00:00
//
bool
2014-11-11 13:51:47 +00:00
Config::Cell::add(const CellEdge& src, const CellEdge& dst)
2012-06-10 16:50:54 +00:00
{
// cannot add an edge that overlaps other existing edges but we
// can exactly replace an edge.
if (!hasEdge(src) && overlaps(src)) {
return false;
}
m_neighbors.erase(src);
m_neighbors.insert(std::make_pair(src, dst));
return true;
}
void
2014-11-11 13:51:47 +00:00
Config::Cell::remove(EDirection side)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
for (EdgeLinks::iterator j = m_neighbors.begin();
2012-06-10 16:50:54 +00:00
j != m_neighbors.end(); ) {
if (j->first.getSide() == side) {
m_neighbors.erase(j++);
}
else {
++j;
}
}
}
void
2014-11-11 13:51:47 +00:00
Config::Cell::remove(EDirection side, float position)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
for (EdgeLinks::iterator j = m_neighbors.begin();
2012-06-10 16:50:54 +00:00
j != m_neighbors.end(); ++j) {
if (j->first.getSide() == side && j->first.isInside(position)) {
m_neighbors.erase(j);
break;
}
}
}
void
2014-11-11 13:51:47 +00:00
Config::Cell::remove(const Name& name)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
for (EdgeLinks::iterator j = m_neighbors.begin();
2012-06-10 16:50:54 +00:00
j != m_neighbors.end(); ) {
if (name == j->second.getName()) {
m_neighbors.erase(j++);
}
else {
++j;
}
}
}
void
2014-11-11 13:51:47 +00:00
Config::Cell::rename(const Name& oldName, const String& newName)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
for (EdgeLinks::iterator j = m_neighbors.begin();
2012-06-10 16:50:54 +00:00
j != m_neighbors.end(); ++j) {
if (oldName == j->second.getName()) {
j->second.setName(newName);
}
}
}
bool
2014-11-11 13:51:47 +00:00
Config::Cell::hasEdge(const CellEdge& edge) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
EdgeLinks::const_iterator i = m_neighbors.find(edge);
2012-06-10 16:50:54 +00:00
return (i != m_neighbors.end() && i->first == edge);
}
bool
2014-11-11 13:51:47 +00:00
Config::Cell::overlaps(const CellEdge& edge) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
EdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
2012-06-10 16:50:54 +00:00
if (i != m_neighbors.end() && i->first.overlaps(edge)) {
return true;
}
if (i != m_neighbors.begin() && (--i)->first.overlaps(edge)) {
return true;
}
return false;
}
bool
2014-11-11 13:51:47 +00:00
Config::Cell::getLink(EDirection side, float position,
const CellEdge*& src, const CellEdge*& dst) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
CellEdge edge(side, position);
EdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
2012-06-10 16:50:54 +00:00
if (i == m_neighbors.begin()) {
return false;
}
--i;
if (i->first.getSide() == side && i->first.isInside(position)) {
src = &i->first;
dst = &i->second;
return true;
}
return false;
}
bool
2014-11-11 13:51:47 +00:00
Config::Cell::operator==(const Cell& x) const
2012-06-10 16:50:54 +00:00
{
// compare options
if (m_options != x.m_options) {
return false;
}
// compare links
if (m_neighbors.size() != x.m_neighbors.size()) {
return false;
}
2014-11-11 13:51:47 +00:00
for (EdgeLinks::const_iterator index1 = m_neighbors.begin(),
2012-06-10 16:50:54 +00:00
index2 = x.m_neighbors.begin();
index1 != m_neighbors.end();
++index1, ++index2) {
if (index1->first != index2->first) {
return false;
}
if (index1->second != index2->second) {
return false;
}
// operator== doesn't compare names. only compare destination
// names.
if (!CaselessCmp::equal(index1->second.getName(),
2012-06-10 16:50:54 +00:00
index2->second.getName())) {
return false;
}
}
return true;
}
bool
2014-11-11 13:51:47 +00:00
Config::Cell::operator!=(const Cell& x) const
2012-06-10 16:50:54 +00:00
{
return !operator==(x);
}
2014-11-11 13:51:47 +00:00
Config::Cell::const_iterator
Config::Cell::begin() const
2012-06-10 16:50:54 +00:00
{
return m_neighbors.begin();
}
2014-11-11 13:51:47 +00:00
Config::Cell::const_iterator
Config::Cell::end() const
2012-06-10 16:50:54 +00:00
{
return m_neighbors.end();
}
//
2014-11-11 13:51:47 +00:00
// Config I/O
2012-06-10 16:50:54 +00:00
//
std::istream&
2014-11-11 13:51:47 +00:00
operator>>(std::istream& s, Config& config)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
ConfigReadContext context(s);
2012-06-10 16:50:54 +00:00
config.read(context);
return s;
}
std::ostream&
2014-11-11 13:51:47 +00:00
operator<<(std::ostream& s, const Config& config)
2012-06-10 16:50:54 +00:00
{
// screens section
s << "section: screens" << std::endl;
2014-11-11 13:51:47 +00:00
for (Config::const_iterator screen = config.begin();
2012-06-10 16:50:54 +00:00
screen != config.end(); ++screen) {
s << "\t" << screen->c_str() << ":" << std::endl;
2014-11-11 13:51:47 +00:00
const Config::ScreenOptions* options = config.getOptions(*screen);
2012-06-10 16:50:54 +00:00
if (options != NULL && options->size() > 0) {
2014-11-11 13:51:47 +00:00
for (Config::ScreenOptions::const_iterator
2012-06-10 16:50:54 +00:00
option = options->begin();
option != options->end(); ++option) {
2014-11-11 13:51:47 +00:00
const char* name = Config::getOptionName(option->first);
String value = Config::getOptionValue(option->first,
2012-06-10 16:50:54 +00:00
option->second);
if (name != NULL && !value.empty()) {
s << "\t\t" << name << " = " << value << std::endl;
}
}
}
}
s << "end" << std::endl;
// links section
2014-11-11 13:51:47 +00:00
String neighbor;
2012-06-10 16:50:54 +00:00
s << "section: links" << std::endl;
2014-11-11 13:51:47 +00:00
for (Config::const_iterator screen = config.begin();
2012-06-10 16:50:54 +00:00
screen != config.end(); ++screen) {
s << "\t" << screen->c_str() << ":" << std::endl;
2014-11-11 13:51:47 +00:00
for (Config::link_const_iterator
2012-06-10 16:50:54 +00:00
link = config.beginNeighbor(*screen),
nend = config.endNeighbor(*screen); link != nend; ++link) {
2014-11-11 13:51:47 +00:00
s << "\t\t" << Config::dirName(link->first.getSide()) <<
Config::formatInterval(link->first.getInterval()) <<
2012-06-10 16:50:54 +00:00
" = " << link->second.getName().c_str() <<
2014-11-11 13:51:47 +00:00
Config::formatInterval(link->second.getInterval()) <<
2012-06-10 16:50:54 +00:00
std::endl;
}
}
s << "end" << std::endl;
// aliases section (if there are any)
if (config.m_map.size() != config.m_nameToCanonicalName.size()) {
// map canonical to alias
2014-11-11 13:51:47 +00:00
typedef std::multimap<String, String,
CaselessCmp> CMNameMap;
2012-06-10 16:50:54 +00:00
CMNameMap aliases;
2014-11-11 13:51:47 +00:00
for (Config::NameMap::const_iterator
2012-06-10 16:50:54 +00:00
index = config.m_nameToCanonicalName.begin();
index != config.m_nameToCanonicalName.end();
++index) {
if (index->first != index->second) {
aliases.insert(std::make_pair(index->second, index->first));
}
}
// dump it
2014-11-11 13:51:47 +00:00
String screen;
2012-06-10 16:50:54 +00:00
s << "section: aliases" << std::endl;
for (CMNameMap::const_iterator index = aliases.begin();
index != aliases.end(); ++index) {
if (index->first != screen) {
screen = index->first;
s << "\t" << screen.c_str() << ":" << std::endl;
}
s << "\t\t" << index->second.c_str() << std::endl;
}
s << "end" << std::endl;
}
// options section
s << "section: options" << std::endl;
2014-11-11 13:51:47 +00:00
const Config::ScreenOptions* options = config.getOptions("");
2012-06-10 16:50:54 +00:00
if (options != NULL && options->size() > 0) {
2014-11-11 13:51:47 +00:00
for (Config::ScreenOptions::const_iterator
2012-06-10 16:50:54 +00:00
option = options->begin();
option != options->end(); ++option) {
2014-11-11 13:51:47 +00:00
const char* name = Config::getOptionName(option->first);
String value = Config::getOptionValue(option->first,
2012-06-10 16:50:54 +00:00
option->second);
if (name != NULL && !value.empty()) {
s << "\t" << name << " = " << value << std::endl;
}
}
}
if (config.m_synergyAddress.isValid()) {
s << "\taddress = " <<
config.m_synergyAddress.getHostname().c_str() << std::endl;
}
s << config.m_inputFilter.format("\t");
s << "end" << std::endl;
return s;
}
//
2014-11-11 13:51:47 +00:00
// ConfigReadContext
2012-06-10 16:50:54 +00:00
//
2014-11-11 13:51:47 +00:00
ConfigReadContext::ConfigReadContext(std::istream& s, SInt32 firstLine) :
2012-06-10 16:50:54 +00:00
m_stream(s),
m_line(firstLine - 1)
{
// do nothing
}
2014-11-11 13:51:47 +00:00
ConfigReadContext::~ConfigReadContext()
2012-06-10 16:50:54 +00:00
{
// do nothing
}
bool
2014-11-11 13:51:47 +00:00
ConfigReadContext::readLine(String& line)
2012-06-10 16:50:54 +00:00
{
++m_line;
while (std::getline(m_stream, line)) {
// strip leading whitespace
2014-11-11 13:51:47 +00:00
String::size_type i = line.find_first_not_of(" \t");
if (i != String::npos) {
2012-06-10 16:50:54 +00:00
line.erase(0, i);
}
// strip comments and then trailing whitespace
i = line.find('#');
2014-11-11 13:51:47 +00:00
if (i != String::npos) {
2012-06-10 16:50:54 +00:00
line.erase(i);
}
i = line.find_last_not_of(" \r\t");
2014-11-11 13:51:47 +00:00
if (i != String::npos) {
2012-06-10 16:50:54 +00:00
line.erase(i + 1);
}
// return non empty line
if (!line.empty()) {
// make sure there are no invalid characters
for (i = 0; i < line.length(); ++i) {
if (!isgraph(line[i]) && line[i] != ' ' && line[i] != '\t') {
throw XConfigRead(*this,
"invalid character %{1}",
synergy::string::sprintf("%#2x", line[i]));
2012-06-10 16:50:54 +00:00
}
}
return true;
}
// next line
++m_line;
}
return false;
}
UInt32
2014-11-11 13:51:47 +00:00
ConfigReadContext::getLineNumber() const
2012-06-10 16:50:54 +00:00
{
return m_line;
}
bool
2014-11-11 13:51:47 +00:00
ConfigReadContext::operator!() const
2012-06-10 16:50:54 +00:00
{
return !m_stream;
}
OptionValue
2014-11-11 13:51:47 +00:00
ConfigReadContext::parseBoolean(const String& arg) const
2012-06-10 16:50:54 +00:00
{
if (CaselessCmp::equal(arg, "true")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(true);
}
if (CaselessCmp::equal(arg, "false")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(false);
}
throw XConfigRead(*this, "invalid boolean argument \"%{1}\"", arg);
}
OptionValue
2014-11-11 13:51:47 +00:00
ConfigReadContext::parseInt(const String& arg) const
2012-06-10 16:50:54 +00:00
{
const char* s = arg.c_str();
char* end;
long tmp = strtol(s, &end, 10);
if (*end != '\0') {
// invalid characters
throw XConfigRead(*this, "invalid integer argument \"%{1}\"", arg);
}
OptionValue value = static_cast<OptionValue>(tmp);
if (value != tmp) {
// out of range
throw XConfigRead(*this, "integer argument \"%{1}\" out of range", arg);
}
return value;
}
OptionValue
2014-11-11 13:51:47 +00:00
ConfigReadContext::parseModifierKey(const String& arg) const
2012-06-10 16:50:54 +00:00
{
if (CaselessCmp::equal(arg, "shift")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(kKeyModifierIDShift);
}
if (CaselessCmp::equal(arg, "ctrl")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(kKeyModifierIDControl);
}
if (CaselessCmp::equal(arg, "alt")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(kKeyModifierIDAlt);
}
if (CaselessCmp::equal(arg, "altgr")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(kKeyModifierIDAltGr);
}
if (CaselessCmp::equal(arg, "meta")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(kKeyModifierIDMeta);
}
if (CaselessCmp::equal(arg, "super")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(kKeyModifierIDSuper);
}
if (CaselessCmp::equal(arg, "none")) {
2012-06-10 16:50:54 +00:00
return static_cast<OptionValue>(kKeyModifierIDNull);
}
throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
}
OptionValue
2014-11-11 13:51:47 +00:00
ConfigReadContext::parseCorner(const String& arg) const
2012-06-10 16:50:54 +00:00
{
if (CaselessCmp::equal(arg, "left")) {
2012-06-10 16:50:54 +00:00
return kTopLeftMask | kBottomLeftMask;
}
else if (CaselessCmp::equal(arg, "right")) {
2012-06-10 16:50:54 +00:00
return kTopRightMask | kBottomRightMask;
}
else if (CaselessCmp::equal(arg, "top")) {
2012-06-10 16:50:54 +00:00
return kTopLeftMask | kTopRightMask;
}
else if (CaselessCmp::equal(arg, "bottom")) {
2012-06-10 16:50:54 +00:00
return kBottomLeftMask | kBottomRightMask;
}
else if (CaselessCmp::equal(arg, "top-left")) {
2012-06-10 16:50:54 +00:00
return kTopLeftMask;
}
else if (CaselessCmp::equal(arg, "top-right")) {
2012-06-10 16:50:54 +00:00
return kTopRightMask;
}
else if (CaselessCmp::equal(arg, "bottom-left")) {
2012-06-10 16:50:54 +00:00
return kBottomLeftMask;
}
else if (CaselessCmp::equal(arg, "bottom-right")) {
2012-06-10 16:50:54 +00:00
return kBottomRightMask;
}
else if (CaselessCmp::equal(arg, "none")) {
2012-06-10 16:50:54 +00:00
return kNoCornerMask;
}
else if (CaselessCmp::equal(arg, "all")) {
2012-06-10 16:50:54 +00:00
return kAllCornersMask;
}
throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
}
OptionValue
2014-11-11 13:51:47 +00:00
ConfigReadContext::parseCorners(const String& args) const
2012-06-10 16:50:54 +00:00
{
// find first token
2014-11-11 13:51:47 +00:00
String::size_type i = args.find_first_not_of(" \t", 0);
if (i == String::npos) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(*this, "missing corner argument");
}
2014-11-11 13:51:47 +00:00
String::size_type j = args.find_first_of(" \t", i);
2012-06-10 16:50:54 +00:00
// parse first corner token
OptionValue corners = parseCorner(args.substr(i, j - i));
// get +/-
i = args.find_first_not_of(" \t", j);
2014-11-11 13:51:47 +00:00
while (i != String::npos) {
2012-06-10 16:50:54 +00:00
// parse +/-
bool add;
if (args[i] == '-') {
add = false;
}
else if (args[i] == '+') {
add = true;
}
else {
throw XConfigRead(*this,
"invalid corner operator \"%{1}\"",
2014-11-11 13:51:47 +00:00
String(args.c_str() + i, 1));
2012-06-10 16:50:54 +00:00
}
// get next corner token
i = args.find_first_not_of(" \t", i + 1);
j = args.find_first_of(" \t", i);
2014-11-11 13:51:47 +00:00
if (i == String::npos) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(*this, "missing corner argument");
}
// parse next corner token
if (add) {
corners |= parseCorner(args.substr(i, j - i));
}
else {
corners &= ~parseCorner(args.substr(i, j - i));
}
i = args.find_first_not_of(" \t", j);
}
return corners;
}
2014-11-11 13:51:47 +00:00
Config::Interval
ConfigReadContext::parseInterval(const ArgList& args) const
2012-06-10 16:50:54 +00:00
{
if (args.size() == 0) {
2014-11-11 13:51:47 +00:00
return Config::Interval(0.0f, 1.0f);
2012-06-10 16:50:54 +00:00
}
if (args.size() != 2 || args[0].empty() || args[1].empty()) {
throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
}
char* end;
long startValue = strtol(args[0].c_str(), &end, 10);
if (end[0] != '\0') {
throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
}
long endValue = strtol(args[1].c_str(), &end, 10);
if (end[0] != '\0') {
throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
}
if (startValue < 0 || startValue > 100 ||
endValue < 0 || endValue > 100 ||
startValue >= endValue) {
throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
}
2014-11-11 13:51:47 +00:00
return Config::Interval(startValue / 100.0f, endValue / 100.0f);
2012-06-10 16:50:54 +00:00
}
void
2014-11-11 13:51:47 +00:00
ConfigReadContext::parseNameWithArgs(
const String& type, const String& line,
const String& delim, String::size_type& index,
String& name, ArgList& args) const
2012-06-10 16:50:54 +00:00
{
// skip leading whitespace
2014-11-11 13:51:47 +00:00
String::size_type i = line.find_first_not_of(" \t", index);
if (i == String::npos) {
throw XConfigRead(*this, String("missing ") + type);
2012-06-10 16:50:54 +00:00
}
// find end of name
2014-11-11 13:51:47 +00:00
String::size_type j = line.find_first_of(" \t(" + delim, i);
if (j == String::npos) {
2012-06-10 16:50:54 +00:00
j = line.length();
}
// save name
name = line.substr(i, j - i);
args.clear();
// is it okay to not find a delimiter?
2014-11-11 13:51:47 +00:00
bool needDelim = (!delim.empty() && delim.find('\n') == String::npos);
2012-06-10 16:50:54 +00:00
// skip whitespace
i = line.find_first_not_of(" \t", j);
2014-11-11 13:51:47 +00:00
if (i == String::npos && needDelim) {
2012-06-10 16:50:54 +00:00
// expected delimiter but didn't find it
2014-11-11 13:51:47 +00:00
throw XConfigRead(*this, String("missing ") + delim[0]);
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
if (i == String::npos) {
2012-06-10 16:50:54 +00:00
// no arguments
index = line.length();
return;
}
if (line[i] != '(') {
// no arguments
index = i;
return;
}
// eat '('
++i;
// parse arguments
j = line.find_first_of(",)", i);
2014-11-11 13:51:47 +00:00
while (j != String::npos) {
2012-06-10 16:50:54 +00:00
// extract arg
2014-11-11 13:51:47 +00:00
String arg(line.substr(i, j - i));
2012-06-10 16:50:54 +00:00
i = j;
// trim whitespace
j = arg.find_first_not_of(" \t");
2014-11-11 13:51:47 +00:00
if (j != String::npos) {
2012-06-10 16:50:54 +00:00
arg.erase(0, j);
}
j = arg.find_last_not_of(" \t");
2014-11-11 13:51:47 +00:00
if (j != String::npos) {
2012-06-10 16:50:54 +00:00
arg.erase(j + 1);
}
// save arg
args.push_back(arg);
// exit loop at end of arguments
if (line[i] == ')') {
break;
}
// eat ','
++i;
// next
j = line.find_first_of(",)", i);
}
// verify ')'
2014-11-11 13:51:47 +00:00
if (j == String::npos) {
2012-06-10 16:50:54 +00:00
// expected )
throw XConfigRead(*this, "missing )");
}
// eat ')'
++i;
// skip whitespace
j = line.find_first_not_of(" \t", i);
2014-11-11 13:51:47 +00:00
if (j == String::npos && needDelim) {
2012-06-10 16:50:54 +00:00
// expected delimiter but didn't find it
2014-11-11 13:51:47 +00:00
throw XConfigRead(*this, String("missing ") + delim[0]);
2012-06-10 16:50:54 +00:00
}
// verify delimiter
2014-11-11 13:51:47 +00:00
if (needDelim && delim.find(line[j]) == String::npos) {
throw XConfigRead(*this, String("expected ") + delim[0]);
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
if (j == String::npos) {
2012-06-10 16:50:54 +00:00
j = line.length();
}
index = j;
return;
}
2014-11-11 13:51:47 +00:00
IPlatformScreen::KeyInfo*
ConfigReadContext::parseKeystroke(const String& keystroke) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
return parseKeystroke(keystroke, std::set<String>());
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
IPlatformScreen::KeyInfo*
ConfigReadContext::parseKeystroke(const String& keystroke,
const std::set<String>& screens) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String s = keystroke;
2012-06-10 16:50:54 +00:00
KeyModifierMask mask;
2014-11-11 13:51:47 +00:00
if (!synergy::KeyMap::parseModifiers(s, mask)) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(*this, "unable to parse key modifiers");
}
KeyID key;
2014-11-11 13:51:47 +00:00
if (!synergy::KeyMap::parseKey(s, key)) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(*this, "unable to parse key");
}
if (key == kKeyNone && mask == 0) {
throw XConfigRead(*this, "missing key and/or modifiers in keystroke");
}
2014-11-11 13:51:47 +00:00
return IPlatformScreen::KeyInfo::alloc(key, mask, 0, 0, screens);
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
IPlatformScreen::ButtonInfo*
ConfigReadContext::parseMouse(const String& mouse) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String s = mouse;
2012-06-10 16:50:54 +00:00
KeyModifierMask mask;
2014-11-11 13:51:47 +00:00
if (!synergy::KeyMap::parseModifiers(s, mask)) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(*this, "unable to parse button modifiers");
}
char* end;
ButtonID button = (ButtonID)strtol(s.c_str(), &end, 10);
if (*end != '\0') {
throw XConfigRead(*this, "unable to parse button");
}
if (s.empty() || button <= 0) {
throw XConfigRead(*this, "invalid button");
}
2014-11-11 13:51:47 +00:00
return IPlatformScreen::ButtonInfo::alloc(button, mask);
2012-06-10 16:50:54 +00:00
}
KeyModifierMask
2014-11-11 13:51:47 +00:00
ConfigReadContext::parseModifier(const String& modifiers) const
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String s = modifiers;
2012-06-10 16:50:54 +00:00
KeyModifierMask mask;
2014-11-11 13:51:47 +00:00
if (!synergy::KeyMap::parseModifiers(s, mask)) {
2012-06-10 16:50:54 +00:00
throw XConfigRead(*this, "unable to parse modifiers");
}
if (mask == 0) {
throw XConfigRead(*this, "no modifiers specified");
}
return mask;
}
2014-11-11 13:51:47 +00:00
String
ConfigReadContext::concatArgs(const ArgList& args)
2012-06-10 16:50:54 +00:00
{
2014-11-11 13:51:47 +00:00
String s("(");
2012-06-10 16:50:54 +00:00
for (size_t i = 0; i < args.size(); ++i) {
if (i != 0) {
s += ",";
}
s += args[i];
}
s += ")";
return s;
}
//
2014-11-11 13:51:47 +00:00
// Config I/O exceptions
2012-06-10 16:50:54 +00:00
//
2014-11-11 13:51:47 +00:00
XConfigRead::XConfigRead(const ConfigReadContext& context,
const String& error) :
m_error(synergy::string::sprintf("line %d: %s",
2012-06-10 16:50:54 +00:00
context.getLineNumber(), error.c_str()))
{
// do nothing
}
2014-11-11 13:51:47 +00:00
XConfigRead::XConfigRead(const ConfigReadContext& context,
const char* errorFmt, const String& arg) :
m_error(synergy::string::sprintf("line %d: ", context.getLineNumber()) +
synergy::string::format(errorFmt, arg.c_str()))
2012-06-10 16:50:54 +00:00
{
// do nothing
}
XConfigRead::~XConfigRead() _NOEXCEPT
2012-06-10 16:50:54 +00:00
{
// do nothing
}
2014-11-11 13:51:47 +00:00
String
2012-06-10 16:50:54 +00:00
XConfigRead::getWhat() const throw()
{
return format("XConfigRead", "read error: %{1}", m_error.c_str());
}