Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

CConfig.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2002 Chris Schoeneman
00004  * 
00005  * This package is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * found in the file COPYING that should have accompanied this file.
00008  * 
00009  * This package is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "CConfig.h"
00016 #include "CServer.h"
00017 #include "CKeyMap.h"
00018 #include "KeyTypes.h"
00019 #include "XSocket.h"
00020 #include "stdistream.h"
00021 #include "stdostream.h"
00022 #include <cstdlib>
00023 
00024 //
00025 // CConfig
00026 //
00027 
00028 CConfig::CConfig() : m_hasLockToScreenAction(false)
00029 {
00030     // do nothing
00031 }
00032 
00033 CConfig::~CConfig()
00034 {
00035     // do nothing
00036 }
00037 
00038 bool
00039 CConfig::addScreen(const CString& name)
00040 {
00041     // alias name must not exist
00042     if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) {
00043         return false;
00044     }
00045 
00046     // add cell
00047     m_map.insert(std::make_pair(name, CCell()));
00048 
00049     // add name
00050     m_nameToCanonicalName.insert(std::make_pair(name, name));
00051 
00052     return true;
00053 }
00054 
00055 bool
00056 CConfig::renameScreen(const CString& oldName,
00057                             const CString& newName)
00058 {
00059     // get canonical name and find cell
00060     CString oldCanonical = getCanonicalName(oldName);
00061     CCellMap::iterator index = m_map.find(oldCanonical);
00062     if (index == m_map.end()) {
00063         return false;
00064     }
00065 
00066     // accept if names are equal but replace with new name to maintain
00067     // case.  otherwise, the new name must not exist.
00068     if (!CStringUtil::CaselessCmp::equal(oldName, newName) &&
00069         m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) {
00070         return false;
00071     }
00072 
00073     // update cell
00074     CCell tmpCell = index->second;
00075     m_map.erase(index);
00076     m_map.insert(std::make_pair(newName, tmpCell));
00077 
00078     // update name
00079     m_nameToCanonicalName.erase(oldCanonical);
00080     m_nameToCanonicalName.insert(std::make_pair(newName, newName));
00081 
00082     // update connections
00083     CName oldNameObj(this, oldName);
00084     for (index = m_map.begin(); index != m_map.end(); ++index) {
00085         index->second.rename(oldNameObj, newName);
00086     }
00087 
00088     // update alias targets
00089     if (CStringUtil::CaselessCmp::equal(oldName, oldCanonical)) {
00090         for (CNameMap::iterator index = m_nameToCanonicalName.begin();
00091                             index != m_nameToCanonicalName.end(); ++index) {
00092             if (CStringUtil::CaselessCmp::equal(
00093                             index->second, oldCanonical)) {
00094                 index->second = newName;
00095             }
00096         }
00097     }
00098 
00099     return true;
00100 }
00101 
00102 void
00103 CConfig::removeScreen(const CString& name)
00104 {
00105     // get canonical name and find cell
00106     CString canonical = getCanonicalName(name);
00107     CCellMap::iterator index = m_map.find(canonical);
00108     if (index == m_map.end()) {
00109         return;
00110     }
00111 
00112     // remove from map
00113     m_map.erase(index);
00114 
00115     // disconnect
00116     CName nameObj(this, name);
00117     for (index = m_map.begin(); index != m_map.end(); ++index) {
00118         index->second.remove(nameObj);
00119     }
00120 
00121     // remove aliases (and canonical name)
00122     for (CNameMap::iterator index = m_nameToCanonicalName.begin();
00123                                 index != m_nameToCanonicalName.end(); ) {
00124         if (index->second == canonical) {
00125             m_nameToCanonicalName.erase(index++);
00126         }
00127         else {
00128             ++index;
00129         }
00130     }
00131 }
00132 
00133 void
00134 CConfig::removeAllScreens()
00135 {
00136     m_map.clear();
00137     m_nameToCanonicalName.clear();
00138 }
00139 
00140 bool
00141 CConfig::addAlias(const CString& canonical, const CString& alias)
00142 {
00143     // alias name must not exist
00144     if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) {
00145         return false;
00146     }
00147 
00148     // canonical name must be known
00149     if (m_map.find(canonical) == m_map.end()) {
00150         return false;
00151     }
00152 
00153     // insert alias
00154     m_nameToCanonicalName.insert(std::make_pair(alias, canonical));
00155 
00156     return true;
00157 }
00158 
00159 bool
00160 CConfig::removeAlias(const CString& alias)
00161 {
00162     // must not be a canonical name
00163     if (m_map.find(alias) != m_map.end()) {
00164         return false;
00165     }
00166 
00167     // find alias
00168     CNameMap::iterator index = m_nameToCanonicalName.find(alias);
00169     if (index == m_nameToCanonicalName.end()) {
00170         return false;
00171     }
00172 
00173     // remove alias
00174     m_nameToCanonicalName.erase(index);
00175 
00176     return true;
00177 }
00178 
00179 bool
00180 CConfig::removeAliases(const CString& canonical)
00181 {
00182     // must be a canonical name
00183     if (m_map.find(canonical) == m_map.end()) {
00184         return false;
00185     }
00186 
00187     // find and removing matching aliases
00188     for (CNameMap::iterator index = m_nameToCanonicalName.begin();
00189                             index != m_nameToCanonicalName.end(); ) {
00190         if (index->second == canonical && index->first != canonical) {
00191             m_nameToCanonicalName.erase(index++);
00192         }
00193         else {
00194             ++index;
00195         }
00196     }
00197 
00198     return true;
00199 }
00200 
00201 void
00202 CConfig::removeAllAliases()
00203 {
00204     // remove all names
00205     m_nameToCanonicalName.clear();
00206 
00207     // put the canonical names back in
00208     for (CCellMap::iterator index = m_map.begin();
00209                                 index != m_map.end(); ++index) {
00210         m_nameToCanonicalName.insert(
00211                                 std::make_pair(index->first, index->first));
00212     }
00213 }
00214 
00215 bool
00216 CConfig::connect(const CString& srcName,
00217                 EDirection srcSide,
00218                 float srcStart, float srcEnd,
00219                 const CString& dstName,
00220                 float dstStart, float dstEnd)
00221 {
00222     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00223 
00224     // find source cell
00225     CCellMap::iterator index = m_map.find(getCanonicalName(srcName));
00226     if (index == m_map.end()) {
00227         return false;
00228     }
00229 
00230     // add link
00231     CCellEdge srcEdge(srcSide, CInterval(srcStart, srcEnd));
00232     CCellEdge dstEdge(dstName, srcSide, CInterval(dstStart, dstEnd));
00233     return index->second.add(srcEdge, dstEdge);
00234 }
00235 
00236 bool
00237 CConfig::disconnect(const CString& srcName, EDirection srcSide)
00238 {
00239     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00240 
00241     // find source cell
00242     CCellMap::iterator index = m_map.find(srcName);
00243     if (index == m_map.end()) {
00244         return false;
00245     }
00246 
00247     // disconnect side
00248     index->second.remove(srcSide);
00249 
00250     return true;
00251 }
00252 
00253 bool
00254 CConfig::disconnect(const CString& srcName, EDirection srcSide, float position)
00255 {
00256     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00257 
00258     // find source cell
00259     CCellMap::iterator index = m_map.find(srcName);
00260     if (index == m_map.end()) {
00261         return false;
00262     }
00263 
00264     // disconnect side
00265     index->second.remove(srcSide, position);
00266 
00267     return true;
00268 }
00269 
00270 void
00271 CConfig::setSynergyAddress(const CNetworkAddress& addr)
00272 {
00273     m_synergyAddress = addr;
00274 }
00275 
00276 bool
00277 CConfig::addOption(const CString& name, OptionID option, OptionValue value)
00278 {
00279     // find options
00280     CScreenOptions* options = NULL;
00281     if (name.empty()) {
00282         options = &m_globalOptions;
00283     }
00284     else {
00285         CCellMap::iterator index = m_map.find(name);
00286         if (index != m_map.end()) {
00287             options = &index->second.m_options;
00288         }
00289     }
00290     if (options == NULL) {
00291         return false;
00292     }
00293 
00294     // add option
00295     options->insert(std::make_pair(option, value));
00296     return true;
00297 }
00298 
00299 bool
00300 CConfig::removeOption(const CString& name, OptionID option)
00301 {
00302     // find options
00303     CScreenOptions* options = NULL;
00304     if (name.empty()) {
00305         options = &m_globalOptions;
00306     }
00307     else {
00308         CCellMap::iterator index = m_map.find(name);
00309         if (index != m_map.end()) {
00310             options = &index->second.m_options;
00311         }
00312     }
00313     if (options == NULL) {
00314         return false;
00315     }
00316 
00317     // remove option
00318     options->erase(option);
00319     return true;
00320 }
00321 
00322 bool
00323 CConfig::removeOptions(const CString& name)
00324 {
00325     // find options
00326     CScreenOptions* options = NULL;
00327     if (name.empty()) {
00328         options = &m_globalOptions;
00329     }
00330     else {
00331         CCellMap::iterator index = m_map.find(name);
00332         if (index != m_map.end()) {
00333             options = &index->second.m_options;
00334         }
00335     }
00336     if (options == NULL) {
00337         return false;
00338     }
00339 
00340     // remove options
00341     options->clear();
00342     return true;
00343 }
00344 
00345 bool
00346 CConfig::isValidScreenName(const CString& name) const
00347 {
00348     // name is valid if matches validname
00349     //  name      ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9]
00350     //  domain    ::= . name
00351     //  validname ::= name domain*
00352     // we also accept names ending in . because many OS X users have
00353     // so misconfigured their systems.
00354 
00355     // empty name is invalid
00356     if (name.empty()) {
00357         return false;
00358     }
00359 
00360     // check each dot separated part
00361     CString::size_type b = 0;
00362     for (;;) {
00363         // accept trailing .
00364         if (b == name.size()) {
00365             break;
00366         }
00367 
00368         // find end of part
00369         CString::size_type e = name.find('.', b);
00370         if (e == CString::npos) {
00371             e = name.size();
00372         }
00373 
00374         // part may not be empty
00375         if (e - b < 1) {
00376             return false;
00377         }
00378 
00379         // check first and last characters
00380         if (!(isalnum(name[b]) || name[b] == '_') ||
00381             !(isalnum(name[e - 1]) || name[e - 1] == '_')) {
00382             return false;
00383         }
00384 
00385         // check interior characters
00386         for (CString::size_type i = b; i < e; ++i) {
00387             if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') {
00388                 return false;
00389             }
00390         }
00391 
00392         // next part
00393         if (e == name.size()) {
00394             // no more parts
00395             break;
00396         }
00397         b = e + 1;
00398     }
00399 
00400     return true;
00401 }
00402 
00403 CConfig::const_iterator
00404 CConfig::begin() const
00405 {
00406     return const_iterator(m_map.begin());
00407 }
00408 
00409 CConfig::const_iterator
00410 CConfig::end() const
00411 {
00412     return const_iterator(m_map.end());
00413 }
00414 
00415 CConfig::all_const_iterator
00416 CConfig::beginAll() const
00417 {
00418     return m_nameToCanonicalName.begin();
00419 }
00420 
00421 CConfig::all_const_iterator
00422 CConfig::endAll() const
00423 {
00424     return m_nameToCanonicalName.end();
00425 }
00426 
00427 bool
00428 CConfig::isScreen(const CString& name) const
00429 {
00430     return (m_nameToCanonicalName.count(name) > 0);
00431 }
00432 
00433 bool
00434 CConfig::isCanonicalName(const CString& name) const
00435 {
00436     return (!name.empty() &&
00437             CStringUtil::CaselessCmp::equal(getCanonicalName(name), name));
00438 }
00439 
00440 CString
00441 CConfig::getCanonicalName(const CString& name) const
00442 {
00443     CNameMap::const_iterator index = m_nameToCanonicalName.find(name);
00444     if (index == m_nameToCanonicalName.end()) {
00445         return CString();
00446     }
00447     else {
00448         return index->second;
00449     }
00450 }
00451 
00452 CString
00453 CConfig::getNeighbor(const CString& srcName, EDirection srcSide,
00454                 float position, float* positionOut) const
00455 {
00456     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00457 
00458     // find source cell
00459     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00460     if (index == m_map.end()) {
00461         return CString();
00462     }
00463 
00464     // find edge
00465     const CCellEdge* srcEdge, *dstEdge;
00466     if (!index->second.getLink(srcSide, position, srcEdge, dstEdge)) {
00467         // no neighbor
00468         return "";
00469     }
00470     else {
00471         // compute position on neighbor
00472         if (positionOut != NULL) {
00473             *positionOut =
00474                 dstEdge->inverseTransform(srcEdge->transform(position));
00475         }
00476 
00477         // return neighbor's name
00478         return getCanonicalName(dstEdge->getName());
00479     }
00480 }
00481 
00482 bool
00483 CConfig::hasNeighbor(const CString& srcName, EDirection srcSide) const
00484 {
00485     return hasNeighbor(srcName, srcSide, 0.0f, 1.0f);
00486 }
00487 
00488 bool
00489 CConfig::hasNeighbor(const CString& srcName, EDirection srcSide,
00490                             float start, float end) const
00491 {
00492     assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
00493 
00494     // find source cell
00495     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00496     if (index == m_map.end()) {
00497         return false;
00498     }
00499 
00500     return index->second.overlaps(CCellEdge(srcSide, CInterval(start, end)));
00501 }
00502 
00503 CConfig::link_const_iterator
00504 CConfig::beginNeighbor(const CString& srcName) const
00505 {
00506     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00507     assert(index != m_map.end());
00508     return index->second.begin();
00509 }
00510 
00511 CConfig::link_const_iterator
00512 CConfig::endNeighbor(const CString& srcName) const
00513 {
00514     CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
00515     assert(index != m_map.end());
00516     return index->second.end();
00517 }
00518 
00519 const CNetworkAddress&
00520 CConfig::getSynergyAddress() const
00521 {
00522     return m_synergyAddress;
00523 }
00524 
00525 const CConfig::CScreenOptions*
00526 CConfig::getOptions(const CString& name) const
00527 {
00528     // find options
00529     const CScreenOptions* options = NULL;
00530     if (name.empty()) {
00531         options = &m_globalOptions;
00532     }
00533     else {
00534         CCellMap::const_iterator index = m_map.find(name);
00535         if (index != m_map.end()) {
00536             options = &index->second.m_options;
00537         }
00538     }
00539 
00540     // return options
00541     return options;
00542 }
00543 
00544 bool
00545 CConfig::hasLockToScreenAction() const
00546 {
00547     return m_hasLockToScreenAction;
00548 }
00549 
00550 bool
00551 CConfig::operator==(const CConfig& x) const
00552 {
00553     if (m_synergyAddress != x.m_synergyAddress) {
00554         return false;
00555     }
00556     if (m_map.size() != x.m_map.size()) {
00557         return false;
00558     }
00559     if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) {
00560         return false;
00561     }
00562 
00563     // compare global options
00564     if (m_globalOptions != x.m_globalOptions) {
00565         return false;
00566     }
00567 
00568     for (CCellMap::const_iterator index1 = m_map.begin(),
00569                                 index2 = x.m_map.begin();
00570                                 index1 != m_map.end(); ++index1, ++index2) {
00571         // compare names
00572         if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first)) {
00573             return false;
00574         }
00575 
00576         // compare cells
00577         if (index1->second != index2->second) {
00578             return false;
00579         }
00580     }
00581 
00582     for (CNameMap::const_iterator index1 = m_nameToCanonicalName.begin(),
00583                                 index2 = x.m_nameToCanonicalName.begin();
00584                                 index1 != m_nameToCanonicalName.end();
00585                                 ++index1, ++index2) {
00586         if (!CStringUtil::CaselessCmp::equal(index1->first,  index2->first) ||
00587             !CStringUtil::CaselessCmp::equal(index1->second, index2->second)) {
00588             return false;
00589         }
00590     }
00591 
00592     // compare input filters
00593     if (m_inputFilter != x.m_inputFilter) {
00594         return false;
00595     }
00596 
00597     return true;
00598 }
00599 
00600 bool
00601 CConfig::operator!=(const CConfig& x) const
00602 {
00603     return !operator==(x);
00604 }
00605 
00606 void
00607 CConfig::read(CConfigReadContext& context)
00608 {
00609     CConfig tmp;
00610     while (context) {
00611         tmp.readSection(context);
00612     }
00613     *this = tmp;
00614 }
00615 
00616 const char*
00617 CConfig::dirName(EDirection dir)
00618 {
00619     static const char* s_name[] = { "left", "right", "up", "down" };
00620 
00621     assert(dir >= kFirstDirection && dir <= kLastDirection);
00622 
00623     return s_name[dir - kFirstDirection];
00624 }
00625 
00626 CInputFilter*
00627 CConfig::getInputFilter()
00628 {
00629     return &m_inputFilter;
00630 }
00631 
00632 CString
00633 CConfig::formatInterval(const CInterval& x)
00634 {
00635     if (x.first == 0.0f && x.second == 1.0f) {
00636         return "";
00637     }
00638     return CStringUtil::print("(%d,%d)", (int)(x.first * 100.0f + 0.5f),
00639                                         (int)(x.second * 100.0f + 0.5f));
00640 }
00641 
00642 void
00643 CConfig::readSection(CConfigReadContext& s)
00644 {
00645     static const char s_section[] = "section:";
00646     static const char s_options[] = "options";
00647     static const char s_screens[] = "screens";
00648     static const char s_links[]   = "links";
00649     static const char s_aliases[] = "aliases";
00650 
00651     CString line;
00652     if (!s.readLine(line)) {
00653         // no more sections
00654         return;
00655     }
00656 
00657     // should be a section header
00658     if (line.find(s_section) != 0) {
00659         throw XConfigRead(s, "found data outside section");
00660     }
00661 
00662     // get section name
00663     CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1);
00664     if (i == CString::npos) {
00665         throw XConfigRead(s, "section name is missing");
00666     }
00667     CString name = line.substr(i);
00668     i = name.find_first_of(" \t");
00669     if (i != CString::npos) {
00670         throw XConfigRead(s, "unexpected data after section name");
00671     }
00672 
00673     // read section
00674     if (name == s_options) {
00675         readSectionOptions(s);
00676     }
00677     else if (name == s_screens) {
00678         readSectionScreens(s);
00679     }
00680     else if (name == s_links) {
00681         readSectionLinks(s);
00682     }
00683     else if (name == s_aliases) {
00684         readSectionAliases(s);
00685     }
00686     else {
00687         throw XConfigRead(s, "unknown section name \"%{1}\"", name);
00688     }
00689 }
00690 
00691 void
00692 CConfig::readSectionOptions(CConfigReadContext& s)
00693 {
00694     CString line;
00695     while (s.readLine(line)) {
00696         // check for end of section
00697         if (line == "end") {
00698             return;
00699         }
00700 
00701         // parse argument:  `nameAndArgs = [values][;[values]]'
00702         //   nameAndArgs  := <name>[(arg[,...])]
00703         //   values       := valueAndArgs[,valueAndArgs]...
00704         //   valueAndArgs := <value>[(arg[,...])]
00705         CString::size_type i = 0;
00706         CString name, value;
00707         CConfigReadContext::ArgList nameArgs, valueArgs;
00708         s.parseNameWithArgs("name", line, "=", i, name, nameArgs);
00709         ++i;
00710         s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
00711 
00712         bool handled = true;
00713         if (name == "address") {
00714             try {
00715                 m_synergyAddress = CNetworkAddress(value, kDefaultPort);
00716                 m_synergyAddress.resolve();
00717             }
00718             catch (XSocketAddress& e) {
00719                 throw XConfigRead(s,
00720                             CString("invalid address argument ") + e.what());
00721             }
00722         }
00723         else if (name == "heartbeat") {
00724             addOption("", kOptionHeartbeat, s.parseInt(value));
00725         }
00726         else if (name == "switchCorners") {
00727             addOption("", kOptionScreenSwitchCorners, s.parseCorners(value));
00728         }
00729         else if (name == "switchCornerSize") {
00730             addOption("", kOptionScreenSwitchCornerSize, s.parseInt(value));
00731         }
00732         else if (name == "switchDelay") {
00733             addOption("", kOptionScreenSwitchDelay, s.parseInt(value));
00734         }
00735         else if (name == "switchDoubleTap") {
00736             addOption("", kOptionScreenSwitchTwoTap, s.parseInt(value));
00737         }
00738         else if (name == "screenSaverSync") {
00739             addOption("", kOptionScreenSaverSync, s.parseBoolean(value));
00740         }
00741         else if (name == "relativeMouseMoves") {
00742             addOption("", kOptionRelativeMouseMoves, s.parseBoolean(value));
00743         }
00744         else if (name == "win32KeepForeground") {
00745             addOption("", kOptionWin32KeepForeground, s.parseBoolean(value));
00746         }
00747         else {
00748             handled = false;
00749         }
00750 
00751         if (handled) {
00752             // make sure handled options aren't followed by more values
00753             if (i < line.size() && (line[i] == ',' || line[i] == ';')) {
00754                 throw XConfigRead(s, "to many arguments to %s", name.c_str());
00755             }
00756         }
00757         else {
00758             // make filter rule
00759             CInputFilter::CRule rule(parseCondition(s, name, nameArgs));
00760 
00761             // save first action (if any)
00762             if (!value.empty() || line[i] != ';') {
00763                 parseAction(s, value, valueArgs, rule, true);
00764             }
00765 
00766             // get remaining activate actions
00767             while (i < line.length() && line[i] != ';') {
00768                 ++i;
00769                 s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
00770                 parseAction(s, value, valueArgs, rule, true);
00771             }
00772 
00773             // get deactivate actions
00774             if (i < line.length() && line[i] == ';') {
00775                 // allow trailing ';'
00776                 i = line.find_first_not_of(" \t", i + 1);
00777                 if (i == CString::npos) {
00778                     i = line.length();
00779                 }
00780                 else {
00781                     --i;
00782                 }
00783 
00784                 // get actions
00785                 while (i < line.length()) {
00786                     ++i;
00787                     s.parseNameWithArgs("value", line, ",\n",
00788                                 i, value, valueArgs);
00789                     parseAction(s, value, valueArgs, rule, false);
00790                 }
00791             }
00792 
00793             // add rule
00794             m_inputFilter.addFilterRule(rule);
00795         }
00796     }
00797     throw XConfigRead(s, "unexpected end of options section");
00798 }
00799 
00800 void
00801 CConfig::readSectionScreens(CConfigReadContext& s)
00802 {
00803     CString line;
00804     CString screen;
00805     while (s.readLine(line)) {
00806         // check for end of section
00807         if (line == "end") {
00808             return;
00809         }
00810 
00811         // see if it's the next screen
00812         if (line[line.size() - 1] == ':') {
00813             // strip :
00814             screen = line.substr(0, line.size() - 1);
00815 
00816             // verify validity of screen name
00817             if (!isValidScreenName(screen)) {
00818                 throw XConfigRead(s, "invalid screen name \"%{1}\"", screen);
00819             }
00820 
00821             // add the screen to the configuration
00822             if (!addScreen(screen)) {
00823                 throw XConfigRead(s, "duplicate screen name \"%{1}\"", screen);
00824             }
00825         }
00826         else if (screen.empty()) {
00827             throw XConfigRead(s, "argument before first screen");
00828         }
00829         else {
00830             // parse argument:  `<name>=<value>'
00831             CString::size_type i = line.find_first_of(" \t=");
00832             if (i == 0) {
00833                 throw XConfigRead(s, "missing argument name");
00834             }
00835             if (i == CString::npos) {
00836                 throw XConfigRead(s, "missing =");
00837             }
00838             CString name = line.substr(0, i);
00839             i = line.find_first_not_of(" \t", i);
00840             if (i == CString::npos || line[i] != '=') {
00841                 throw XConfigRead(s, "missing =");
00842             }
00843             i = line.find_first_not_of(" \t", i + 1);
00844             CString value;
00845             if (i != CString::npos) {
00846                 value = line.substr(i);
00847             }
00848 
00849             // handle argument
00850             if (name == "halfDuplexCapsLock") {
00851                 addOption(screen, kOptionHalfDuplexCapsLock,
00852                     s.parseBoolean(value));
00853             }
00854             else if (name == "halfDuplexNumLock") {
00855                 addOption(screen, kOptionHalfDuplexNumLock,
00856                     s.parseBoolean(value));
00857             }
00858             else if (name == "halfDuplexScrollLock") {
00859                 addOption(screen, kOptionHalfDuplexScrollLock,
00860                     s.parseBoolean(value));
00861             }
00862             else if (name == "shift") {
00863                 addOption(screen, kOptionModifierMapForShift,
00864                     s.parseModifierKey(value));
00865             }
00866             else if (name == "ctrl") {
00867                 addOption(screen, kOptionModifierMapForControl,
00868                     s.parseModifierKey(value));
00869             }
00870             else if (name == "alt") {
00871                 addOption(screen, kOptionModifierMapForAlt,
00872                     s.parseModifierKey(value));
00873             }
00874             else if (name == "meta") {
00875                 addOption(screen, kOptionModifierMapForMeta,
00876                     s.parseModifierKey(value));
00877             }
00878             else if (name == "super") {
00879                 addOption(screen, kOptionModifierMapForSuper,
00880                     s.parseModifierKey(value));
00881             }
00882             else if (name == "xtestIsXineramaUnaware") {
00883                 addOption(screen, kOptionXTestXineramaUnaware,
00884                     s.parseBoolean(value));
00885             }
00886             else if (name == "switchCorners") {
00887                 addOption(screen, kOptionScreenSwitchCorners,
00888                     s.parseCorners(value));
00889             }
00890             else if (name == "switchCornerSize") {
00891                 addOption(screen, kOptionScreenSwitchCornerSize,
00892                     s.parseInt(value));
00893             }
00894             else if (name == "preserveFocus") {
00895                 addOption(screen, kOptionScreenPreserveFocus,
00896                     s.parseBoolean(value));
00897             }
00898             else {
00899                 // unknown argument
00900                 throw XConfigRead(s, "unknown argument \"%{1}\"", name);
00901             }
00902         }
00903     }
00904     throw XConfigRead(s, "unexpected end of screens section");
00905 }
00906 
00907 void
00908 CConfig::readSectionLinks(CConfigReadContext& s)
00909 {
00910     CString line;
00911     CString screen;
00912     while (s.readLine(line)) {
00913         // check for end of section
00914         if (line == "end") {
00915             return;
00916         }
00917 
00918         // see if it's the next screen
00919         if (line[line.size() - 1] == ':') {
00920             // strip :
00921             screen = line.substr(0, line.size() - 1);
00922 
00923             // verify we know about the screen
00924             if (!isScreen(screen)) {
00925                 throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
00926             }
00927             if (!isCanonicalName(screen)) {
00928                 throw XConfigRead(s, "cannot use screen name alias here");
00929             }
00930         }
00931         else if (screen.empty()) {
00932             throw XConfigRead(s, "argument before first screen");
00933         }
00934         else {
00935             // parse argument:  `<name>[(<s0>,<e0>)]=<value>[(<s1>,<e1>)]'
00936             // the stuff in brackets is optional.  interval values must be
00937             // in the range [0,100] and start < end.  if not given the
00938             // interval is taken to be (0,100).
00939             CString::size_type i = 0;
00940             CString side, dstScreen, srcArgString, dstArgString;
00941             CConfigReadContext::ArgList srcArgs, dstArgs;
00942             s.parseNameWithArgs("link", line, "=", i, side, srcArgs);
00943             ++i;
00944             s.parseNameWithArgs("screen", line, "", i, dstScreen, dstArgs);
00945             CInterval srcInterval(s.parseInterval(srcArgs));
00946             CInterval dstInterval(s.parseInterval(dstArgs));
00947 
00948             // handle argument
00949             EDirection dir;
00950             if (side == "left") {
00951                 dir = kLeft;
00952             }
00953             else if (side == "right") {
00954                 dir = kRight;
00955             }
00956             else if (side == "up") {
00957                 dir = kTop;
00958             }
00959             else if (side == "down") {
00960                 dir = kBottom;
00961             }
00962             else {
00963                 // unknown argument
00964                 throw XConfigRead(s, "unknown side \"%{1}\" in link", side);
00965             }
00966             if (!isScreen(dstScreen)) {
00967                 throw XConfigRead(s, "unknown screen name \"%{1}\"", dstScreen);
00968             }
00969             if (!connect(screen, dir,
00970                         srcInterval.first, srcInterval.second,
00971                         dstScreen,
00972                         dstInterval.first, dstInterval.second)) {
00973                 throw XConfigRead(s, "overlapping range");
00974             }
00975         }
00976     }
00977     throw XConfigRead(s, "unexpected end of links section");
00978 }
00979 
00980 void
00981 CConfig::readSectionAliases(CConfigReadContext& s)
00982 {
00983     CString line;
00984     CString screen;
00985     while (s.readLine(line)) {
00986         // check for end of section
00987         if (line == "end") {
00988             return;
00989         }
00990 
00991         // see if it's the next screen
00992         if (line[line.size() - 1] == ':') {
00993             // strip :
00994             screen = line.substr(0, line.size() - 1);
00995 
00996             // verify we know about the screen
00997             if (!isScreen(screen)) {
00998                 throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
00999             }
01000             if (!isCanonicalName(screen)) {
01001                 throw XConfigRead(s, "cannot use screen name alias here");
01002             }
01003         }
01004         else if (screen.empty()) {
01005             throw XConfigRead(s, "argument before first screen");
01006         }
01007         else {
01008             // verify validity of screen name
01009             if (!isValidScreenName(line)) {
01010                 throw XConfigRead(s, "invalid screen alias \"%{1}\"", line);
01011             }
01012 
01013             // add alias
01014             if (!addAlias(screen, line)) {
01015                 throw XConfigRead(s, "alias \"%{1}\" is already used", line);
01016             }
01017         }
01018     }
01019     throw XConfigRead(s, "unexpected end of aliases section");
01020 }
01021 
01022 
01023 CInputFilter::CCondition*
01024 CConfig::parseCondition(CConfigReadContext& s,
01025                 const CString& name, const std::vector<CString>& args)
01026 {
01027     if (name == "keystroke") {
01028         if (args.size() != 1) {
01029             throw XConfigRead(s, "syntax for condition: keystroke(modifiers+key)");
01030         }
01031 
01032         IPlatformScreen::CKeyInfo* keyInfo = s.parseKeystroke(args[0]);
01033 
01034         return new CInputFilter::CKeystrokeCondition(keyInfo);
01035     }
01036 
01037     if (name == "mousebutton") {
01038         if (args.size() != 1) {
01039             throw XConfigRead(s, "syntax for condition: mousebutton(modifiers+button)");
01040         }
01041 
01042         IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]);
01043 
01044         return new CInputFilter::CMouseButtonCondition(mouseInfo);
01045     }
01046 
01047     if (name == "connect") {
01048         if (args.size() != 1) {
01049             throw XConfigRead(s, "syntax for condition: connect([screen])");
01050         }
01051 
01052         CString screen = args[0];
01053         if (isScreen(screen)) {
01054             screen = getCanonicalName(screen);
01055         }
01056         else if (!screen.empty()) {
01057             throw XConfigRead(s, "unknown screen name \"%{1}\" in connect", screen);
01058         }
01059 
01060         return new CInputFilter::CScreenConnectedCondition(screen);
01061     }
01062 
01063     throw XConfigRead(s, "unknown argument \"%{1}\"", name);
01064 }
01065 
01066 void
01067 CConfig::parseAction(CConfigReadContext& s,
01068                 const CString& name, const std::vector<CString>& args,
01069                 CInputFilter::CRule& rule, bool activate)
01070 {
01071     CInputFilter::CAction* action;
01072 
01073     if (name == "keystroke" || name == "keyDown" || name == "keyUp") {
01074         if (args.size() < 1 || args.size() > 2) {
01075             throw XConfigRead(s, "syntax for action: keystroke(modifiers+key[,screens])");
01076         }
01077 
01078         IPlatformScreen::CKeyInfo* keyInfo;
01079         if (args.size() == 1) {
01080             keyInfo = s.parseKeystroke(args[0]);
01081         }
01082         else {
01083             std::set<CString> screens;
01084             parseScreens(s, args[1], screens);
01085             keyInfo = s.parseKeystroke(args[0], screens);
01086         }
01087 
01088         if (name == "keystroke") {
01089             IPlatformScreen::CKeyInfo* keyInfo2 =
01090                 IKeyState::CKeyInfo::alloc(*keyInfo);
01091             action = new CInputFilter::CKeystrokeAction(keyInfo2, true);
01092             rule.adoptAction(action, true);
01093             action   = new CInputFilter::CKeystrokeAction(keyInfo, false);
01094             activate = false;
01095         }
01096         else if (name == "keyDown") {
01097             action = new CInputFilter::CKeystrokeAction(keyInfo, true);
01098         }
01099         else {
01100             action = new CInputFilter::CKeystrokeAction(keyInfo, false);
01101         }
01102     }
01103 
01104     else if (name == "mousebutton" ||
01105                 name == "mouseDown" || name == "mouseUp") {
01106         if (args.size() != 1) {
01107             throw XConfigRead(s, "syntax for action: mousebutton(modifiers+button)");
01108         }
01109 
01110         IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]);
01111 
01112         if (name == "mousebutton") {
01113             IPlatformScreen::CButtonInfo* mouseInfo2 =
01114                 IPlatformScreen::CButtonInfo::alloc(*mouseInfo);
01115             action = new CInputFilter::CMouseButtonAction(mouseInfo2, true);
01116             rule.adoptAction(action, true);
01117             action   = new CInputFilter::CMouseButtonAction(mouseInfo, false);
01118             activate = false;
01119         }
01120         else if (name == "mouseDown") {
01121             action = new CInputFilter::CMouseButtonAction(mouseInfo, true);
01122         }
01123         else {
01124             action = new CInputFilter::CMouseButtonAction(mouseInfo, false);
01125         }
01126     }
01127 
01128 /* XXX -- not supported
01129     else if (name == "modifier") {
01130         if (args.size() != 1) {
01131             throw XConfigRead(s, "syntax for action: modifier(modifiers)");
01132         }
01133 
01134         KeyModifierMask mask = s.parseModifier(args[0]);
01135 
01136         action = new CInputFilter::CModifierAction(mask, ~mask);
01137     }
01138 */
01139 
01140     else if (name == "switchToScreen") {
01141         if (args.size() != 1) {
01142             throw XConfigRead(s, "syntax for action: switchToScreen(name)");
01143         }
01144 
01145         CString screen = args[0];
01146         if (isScreen(screen)) {
01147             screen = getCanonicalName(screen);
01148         }
01149         else if (!screen.empty()) {
01150             throw XConfigRead(s, "unknown screen name in switchToScreen");
01151         }
01152 
01153         action = new CInputFilter::CSwitchToScreenAction(screen);
01154     }
01155 
01156     else if (name == "switchInDirection") {
01157         if (args.size() != 1) {
01158             throw XConfigRead(s, "syntax for action: switchInDirection(<left|right|up|down>)");
01159         }
01160 
01161         EDirection direction;
01162         if (args[0] == "left") {
01163             direction = kLeft;
01164         }
01165         else if (args[0] == "right") {
01166             direction = kRight;
01167         }
01168         else if (args[0] == "up") {
01169             direction = kTop;
01170         }
01171         else if (args[0] == "down") {
01172             direction = kBottom;
01173         }
01174         else {
01175             throw XConfigRead(s, "unknown direction \"%{1}\" in switchToScreen", args[0]);
01176         }
01177 
01178         action = new CInputFilter::CSwitchInDirectionAction(direction);
01179     }
01180 
01181     else if (name == "lockCursorToScreen") {
01182         if (args.size() > 1) {
01183             throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
01184         }
01185 
01186         CInputFilter::CLockCursorToScreenAction::Mode mode =
01187             CInputFilter::CLockCursorToScreenAction::kToggle;
01188         if (args.size() == 1) {
01189             if (args[0] == "off") {
01190                 mode = CInputFilter::CLockCursorToScreenAction::kOff;
01191             }
01192             else if (args[0] == "on") {
01193                 mode = CInputFilter::CLockCursorToScreenAction::kOn;
01194             }
01195             else if (args[0] == "toggle") {
01196                 mode = CInputFilter::CLockCursorToScreenAction::kToggle;
01197             }
01198             else {
01199                 throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
01200             }
01201         }
01202 
01203         if (mode != CInputFilter::CLockCursorToScreenAction::kOff) {
01204             m_hasLockToScreenAction = true;
01205         }
01206 
01207         action = new CInputFilter::CLockCursorToScreenAction(mode);
01208     }
01209 
01210     else if (name == "keyboardBroadcast") {
01211         if (args.size() > 2) {
01212             throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
01213         }
01214 
01215         CInputFilter::CKeyboardBroadcastAction::Mode mode =
01216             CInputFilter::CKeyboardBroadcastAction::kToggle;
01217         if (args.size() >= 1) {
01218             if (args[0] == "off") {
01219                 mode = CInputFilter::CKeyboardBroadcastAction::kOff;
01220             }
01221             else if (args[0] == "on") {
01222                 mode = CInputFilter::CKeyboardBroadcastAction::kOn;
01223             }
01224             else if (args[0] == "toggle") {
01225                 mode = CInputFilter::CKeyboardBroadcastAction::kToggle;
01226             }
01227             else {
01228                 throw XConfigRead(s, "syntax for action: keyboardBroadcast([{off|on|toggle}[,screens]])");
01229             }
01230         }
01231 
01232         std::set<CString> screens;
01233         if (args.size() >= 2) {
01234             parseScreens(s, args[1], screens);
01235         }
01236 
01237         action = new CInputFilter::CKeyboardBroadcastAction(mode, screens);
01238     }
01239 
01240     else {
01241         throw XConfigRead(s, "unknown action argument \"%{1}\"", name);
01242     }
01243 
01244     rule.adoptAction(action, activate);
01245 }
01246 
01247 void
01248 CConfig::parseScreens(CConfigReadContext& c,
01249                 const CString& s, std::set<CString>& screens) const
01250 {
01251     screens.clear();
01252 
01253     CString::size_type i = 0;
01254     while (i < s.size()) {
01255         // find end of next screen name
01256         CString::size_type j = s.find(':', i);
01257         if (j == CString::npos) {
01258             j = s.size();
01259         }
01260 
01261         // extract name
01262         CString rawName;
01263         i = s.find_first_not_of(" \t", i);
01264         if (i < j) {
01265             rawName = s.substr(i, s.find_last_not_of(" \t", j - 1) - i + 1);
01266         }
01267 
01268         // add name
01269         if (rawName == "*") {
01270             screens.insert("*");
01271         }
01272         else if (!rawName.empty()) {
01273             CString name = getCanonicalName(rawName);
01274             if (name.empty()) {
01275                 throw XConfigRead(c, "unknown screen name \"%{1}\"", rawName);
01276             }
01277             screens.insert(name);
01278         }
01279 
01280         // next
01281         i = j + 1;
01282     }
01283 }
01284 
01285 const char*
01286 CConfig::getOptionName(OptionID id)
01287 {
01288     if (id == kOptionHalfDuplexCapsLock) {
01289         return "halfDuplexCapsLock";
01290     }
01291     if (id == kOptionHalfDuplexNumLock) {
01292         return "halfDuplexNumLock";
01293     }
01294     if (id == kOptionHalfDuplexScrollLock) {
01295         return "halfDuplexScrollLock";
01296     }
01297     if (id == kOptionModifierMapForShift) {
01298         return "shift";
01299     }
01300     if (id == kOptionModifierMapForControl) {
01301         return "ctrl";
01302     }
01303     if (id == kOptionModifierMapForAlt) {
01304         return "alt";
01305     }
01306     if (id == kOptionModifierMapForMeta) {
01307         return "meta";
01308     }
01309     if (id == kOptionModifierMapForSuper) {
01310         return "super";
01311     }
01312     if (id == kOptionHeartbeat) {
01313         return "heartbeat";
01314     }
01315     if (id == kOptionScreenSwitchCorners) {
01316         return "switchCorners";
01317     }
01318     if (id == kOptionScreenSwitchCornerSize) {
01319         return "switchCornerSize";
01320     }
01321     if (id == kOptionScreenSwitchDelay) {
01322         return "switchDelay";
01323     }
01324     if (id == kOptionScreenSwitchTwoTap) {
01325         return "switchDoubleTap";
01326     }
01327     if (id == kOptionScreenSaverSync) {
01328         return "screenSaverSync";
01329     }
01330     if (id == kOptionXTestXineramaUnaware) {
01331         return "xtestIsXineramaUnaware";
01332     }
01333     if (id == kOptionRelativeMouseMoves) {
01334         return "relativeMouseMoves";
01335     }
01336     if (id == kOptionWin32KeepForeground) {
01337         return "win32KeepForeground";
01338     }
01339     if (id == kOptionScreenPreserveFocus) {
01340         return "preserveFocus";
01341     }
01342     return NULL;
01343 }
01344 
01345 CString
01346 CConfig::getOptionValue(OptionID id, OptionValue value)
01347 {
01348     if (id == kOptionHalfDuplexCapsLock ||
01349         id == kOptionHalfDuplexNumLock ||
01350         id == kOptionHalfDuplexScrollLock ||
01351         id == kOptionScreenSaverSync ||
01352         id == kOptionXTestXineramaUnaware ||
01353         id == kOptionRelativeMouseMoves ||
01354         id == kOptionWin32KeepForeground ||
01355         id == kOptionScreenPreserveFocus) {
01356         return (value != 0) ? "true" : "false";
01357     }
01358     if (id == kOptionModifierMapForShift ||
01359         id == kOptionModifierMapForControl ||
01360         id == kOptionModifierMapForAlt ||
01361         id == kOptionModifierMapForMeta ||
01362         id == kOptionModifierMapForSuper) {
01363         switch (value) {
01364         case kKeyModifierIDShift:
01365             return "shift";
01366 
01367         case kKeyModifierIDControl:
01368             return "ctrl";
01369 
01370         case kKeyModifierIDAlt:
01371             return "alt";
01372 
01373         case kKeyModifierIDMeta:
01374             return "meta";
01375 
01376         case kKeyModifierIDSuper:
01377             return "super";
01378 
01379         default:
01380             return "none";
01381         }
01382     }
01383     if (id == kOptionHeartbeat ||
01384         id == kOptionScreenSwitchCornerSize ||
01385         id == kOptionScreenSwitchDelay ||
01386         id == kOptionScreenSwitchTwoTap) {
01387         return CStringUtil::print("%d", value);
01388     }
01389     if (id == kOptionScreenSwitchCorners) {
01390         std::string result("none");
01391         if ((value & kTopLeftMask) != 0) {
01392             result += " +top-left";
01393         }
01394         if ((value & kTopRightMask) != 0) {
01395             result += " +top-right";
01396         }
01397         if ((value & kBottomLeftMask) != 0) {
01398             result += " +bottom-left";
01399         }
01400         if ((value & kBottomRightMask) != 0) {
01401             result += " +bottom-right";
01402         }
01403         return result;
01404     }
01405 
01406     return "";
01407 }
01408 
01409 
01410 //
01411 // CConfig::CName
01412 //
01413 
01414 CConfig::CName::CName(CConfig* config, const CString& name) :
01415     m_config(config),
01416     m_name(config->getCanonicalName(name))
01417 {
01418     // do nothing
01419 }
01420 
01421 bool
01422 CConfig::CName::operator==(const CString& name) const
01423 {
01424     CString canonical = m_config->getCanonicalName(name);
01425     return CStringUtil::CaselessCmp::equal(canonical, m_name);
01426 }
01427 
01428 
01429 //
01430 // CConfig::CCellEdge
01431 //
01432 
01433 CConfig::CCellEdge::CCellEdge(EDirection side, float position)
01434 {
01435     init("", side, CInterval(position, position));
01436 }
01437 
01438 CConfig::CCellEdge::CCellEdge(EDirection side, const CInterval& interval)
01439 {
01440     assert(interval.first >= 0.0f);
01441     assert(interval.second <= 1.0f);
01442     assert(interval.first < interval.second);
01443 
01444     init("", side, interval);
01445 }
01446 
01447 CConfig::CCellEdge::CCellEdge(const CString& name,
01448                 EDirection side, const CInterval& interval)
01449 {
01450     assert(interval.first >= 0.0f);
01451     assert(interval.second <= 1.0f);
01452     assert(interval.first < interval.second);
01453 
01454     init(name, side, interval);
01455 }
01456 
01457 CConfig::CCellEdge::~CCellEdge()
01458 {
01459     // do nothing
01460 }
01461 
01462 void
01463 CConfig::CCellEdge::init(const CString& name, EDirection side,
01464                 const CInterval& interval)
01465 {
01466     assert(side != kNoDirection);
01467 
01468     m_name     = name;
01469     m_side     = side;
01470     m_interval = interval;
01471 }
01472 
01473 CConfig::CInterval
01474 CConfig::CCellEdge::getInterval() const
01475 {
01476     return m_interval;
01477 }
01478 
01479 void
01480 CConfig::CCellEdge::setName(const CString& newName)
01481 {
01482     m_name = newName;
01483 }
01484 
01485 CString
01486 CConfig::CCellEdge::getName() const
01487 {
01488     return m_name;
01489 }
01490 
01491 EDirection
01492 CConfig::CCellEdge::getSide() const
01493 {
01494     return m_side;
01495 }
01496 
01497 bool
01498 CConfig::CCellEdge::overlaps(const CCellEdge& edge) const
01499 {
01500     const CInterval& x = m_interval;
01501     const CInterval& y = edge.m_interval;
01502     if (m_side != edge.m_side) {
01503         return false;
01504     }
01505     return  (x.first  >= y.first && x.first  <  y.second) ||
01506             (x.second >  y.first && x.second <= y.second) ||
01507             (y.first  >= x.first && y.first  <  x.second) ||
01508             (y.second >  x.first && y.second <= x.second);
01509 }
01510 
01511 bool
01512 CConfig::CCellEdge::isInside(float x) const
01513 {
01514     return (x >= m_interval.first && x < m_interval.second);
01515 }
01516 
01517 float
01518 CConfig::CCellEdge::transform(float x) const
01519 {
01520     return (x - m_interval.first) / (m_interval.second - m_interval.first);
01521 }
01522 
01523 
01524 float
01525 CConfig::CCellEdge::inverseTransform(float x) const
01526 {
01527     return x * (m_interval.second - m_interval.first) + m_interval.first;
01528 }
01529 
01530 bool
01531 CConfig::CCellEdge::operator<(const CCellEdge& o) const
01532 {
01533     if (static_cast<int>(m_side) < static_cast<int>(o.m_side)) {
01534         return true;
01535     }
01536     else if (static_cast<int>(m_side) > static_cast<int>(o.m_side)) {
01537         return false;
01538     }
01539 
01540     return (m_interval.first < o.m_interval.first);
01541 }
01542 
01543 bool
01544 CConfig::CCellEdge::operator==(const CCellEdge& x) const
01545 {
01546     return (m_side == x.m_side && m_interval == x.m_interval);
01547 }
01548 
01549 bool
01550 CConfig::CCellEdge::operator!=(const CCellEdge& x) const
01551 {
01552     return !operator==(x);
01553 }
01554 
01555 
01556 //
01557 // CConfig::CCell
01558 //
01559 
01560 bool
01561 CConfig::CCell::add(const CCellEdge& src, const CCellEdge& dst)
01562 {
01563     // cannot add an edge that overlaps other existing edges but we
01564     // can exactly replace an edge.
01565     if (!hasEdge(src) && overlaps(src)) {
01566         return false;
01567     }
01568 
01569     m_neighbors.erase(src);
01570     m_neighbors.insert(std::make_pair(src, dst));
01571     return true;
01572 }
01573 
01574 void
01575 CConfig::CCell::remove(EDirection side)
01576 {
01577     for (CEdgeLinks::iterator j = m_neighbors.begin();
01578                             j != m_neighbors.end(); ) {
01579         if (j->first.getSide() == side) {
01580             m_neighbors.erase(j++);
01581         }
01582         else {
01583             ++j;
01584         }
01585     }
01586 }
01587 
01588 void
01589 CConfig::CCell::remove(EDirection side, float position)
01590 {
01591     for (CEdgeLinks::iterator j = m_neighbors.begin();
01592                             j != m_neighbors.end(); ++j) {
01593         if (j->first.getSide() == side && j->first.isInside(position)) {
01594             m_neighbors.erase(j);
01595             break;
01596         }
01597     }
01598 }
01599 void
01600 CConfig::CCell::remove(const CName& name)
01601 {
01602     for (CEdgeLinks::iterator j = m_neighbors.begin();
01603                             j != m_neighbors.end(); ) {
01604         if (name == j->second.getName()) {
01605             m_neighbors.erase(j++);
01606         }
01607         else {
01608             ++j;
01609         }
01610     }
01611 }
01612 
01613 void
01614 CConfig::CCell::rename(const CName& oldName, const CString& newName)
01615 {
01616     for (CEdgeLinks::iterator j = m_neighbors.begin();
01617                             j != m_neighbors.end(); ++j) {
01618         if (oldName == j->second.getName()) {
01619             j->second.setName(newName);
01620         }
01621     }
01622 }
01623 
01624 bool
01625 CConfig::CCell::hasEdge(const CCellEdge& edge) const
01626 {
01627     CEdgeLinks::const_iterator i = m_neighbors.find(edge);
01628     return (i != m_neighbors.end() && i->first == edge);
01629 }
01630 
01631 bool
01632 CConfig::CCell::overlaps(const CCellEdge& edge) const
01633 {
01634     CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
01635     if (i != m_neighbors.end() && i->first.overlaps(edge)) {
01636         return true;
01637     }
01638     if (i != m_neighbors.begin() && (--i)->first.overlaps(edge)) {
01639         return true;
01640     }
01641     return false;
01642 }
01643 
01644 bool
01645 CConfig::CCell::getLink(EDirection side, float position,
01646                 const CCellEdge*& src, const CCellEdge*& dst) const
01647 {
01648     CCellEdge edge(side, position);
01649     CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
01650     if (i == m_neighbors.begin()) {
01651         return false;
01652     }
01653     --i;
01654     if (i->first.getSide() == side && i->first.isInside(position)) {
01655         src = &i->first;
01656         dst = &i->second;
01657         return true;
01658     }
01659     return false;
01660 }
01661 
01662 bool
01663 CConfig::CCell::operator==(const CCell& x) const
01664 {
01665     // compare options
01666     if (m_options != x.m_options) {
01667         return false;
01668     }
01669 
01670     // compare links
01671     if (m_neighbors.size() != x.m_neighbors.size()) {
01672         return false;
01673     }
01674     for (CEdgeLinks::const_iterator index1 = m_neighbors.begin(),
01675                                 index2 = x.m_neighbors.begin();
01676                                 index1 != m_neighbors.end();
01677                                 ++index1, ++index2) {
01678         if (index1->first != index2->first) {
01679             return false;
01680         }
01681         if (index1->second != index2->second) {
01682             return false;
01683         }
01684 
01685         // operator== doesn't compare names.  only compare destination
01686         // names.
01687         if (!CStringUtil::CaselessCmp::equal(index1->second.getName(),
01688                                 index2->second.getName())) {
01689             return false;
01690         }
01691     }
01692     return true;
01693 }
01694 
01695 bool
01696 CConfig::CCell::operator!=(const CCell& x) const
01697 {
01698     return !operator==(x);
01699 }
01700 
01701 CConfig::CCell::const_iterator
01702 CConfig::CCell::begin() const
01703 {
01704     return m_neighbors.begin();
01705 }
01706 
01707 CConfig::CCell::const_iterator
01708 CConfig::CCell::end() const
01709 {
01710     return m_neighbors.end();
01711 }
01712 
01713 
01714 //
01715 // CConfig I/O
01716 //
01717 
01718 std::istream&
01719 operator>>(std::istream& s, CConfig& config)
01720 {
01721     CConfigReadContext context(s);
01722     config.read(context);
01723     return s;
01724 }
01725 
01726 std::ostream&
01727 operator<<(std::ostream& s, const CConfig& config)
01728 {
01729     // screens section
01730     s << "section: screens" << std::endl;
01731     for (CConfig::const_iterator screen = config.begin();
01732                                 screen != config.end(); ++screen) {
01733         s << "\t" << screen->c_str() << ":" << std::endl;
01734         const CConfig::CScreenOptions* options = config.getOptions(*screen);
01735         if (options != NULL && options->size() > 0) {
01736             for (CConfig::CScreenOptions::const_iterator
01737                                 option  = options->begin();
01738                                 option != options->end(); ++option) {
01739                 const char* name = CConfig::getOptionName(option->first);
01740                 CString value    = CConfig::getOptionValue(option->first,
01741                                                             option->second);
01742                 if (name != NULL && !value.empty()) {
01743                     s << "\t\t" << name << " = " << value << std::endl;
01744                 }
01745             }
01746         }
01747     }
01748     s << "end" << std::endl;
01749 
01750     // links section
01751     CString neighbor;
01752     s << "section: links" << std::endl;
01753     for (CConfig::const_iterator screen = config.begin();
01754                                 screen != config.end(); ++screen) {
01755         s << "\t" << screen->c_str() << ":" << std::endl;
01756 
01757         for (CConfig::link_const_iterator
01758                 link = config.beginNeighbor(*screen),
01759                 nend = config.endNeighbor(*screen); link != nend; ++link) {         
01760             s << "\t\t" << CConfig::dirName(link->first.getSide()) <<
01761                 CConfig::formatInterval(link->first.getInterval()) <<
01762                 " = " << link->second.getName().c_str() <<
01763                 CConfig::formatInterval(link->second.getInterval()) <<
01764                 std::endl;
01765         }
01766     }
01767     s << "end" << std::endl;
01768 
01769     // aliases section (if there are any)
01770     if (config.m_map.size() != config.m_nameToCanonicalName.size()) {
01771         // map canonical to alias
01772         typedef std::multimap<CString, CString,
01773                                 CStringUtil::CaselessCmp> CMNameMap;
01774         CMNameMap aliases;
01775         for (CConfig::CNameMap::const_iterator
01776                                 index = config.m_nameToCanonicalName.begin();
01777                                 index != config.m_nameToCanonicalName.end();
01778                                 ++index) {
01779             if (index->first != index->second) {
01780                 aliases.insert(std::make_pair(index->second, index->first));
01781             }
01782         }
01783 
01784         // dump it
01785         CString screen;
01786         s << "section: aliases" << std::endl;
01787         for (CMNameMap::const_iterator index = aliases.begin();
01788                                 index != aliases.end(); ++index) {
01789             if (index->first != screen) {
01790                 screen = index->first;
01791                 s << "\t" << screen.c_str() << ":" << std::endl;
01792             }
01793             s << "\t\t" << index->second.c_str() << std::endl;
01794         }
01795         s << "end" << std::endl;
01796     }
01797 
01798     // options section
01799     s << "section: options" << std::endl;
01800     const CConfig::CScreenOptions* options = config.getOptions("");
01801     if (options != NULL && options->size() > 0) {
01802         for (CConfig::CScreenOptions::const_iterator
01803                             option  = options->begin();
01804                             option != options->end(); ++option) {
01805             const char* name = CConfig::getOptionName(option->first);
01806             CString value    = CConfig::getOptionValue(option->first,
01807                                                         option->second);
01808             if (name != NULL && !value.empty()) {
01809                 s << "\t" << name << " = " << value << std::endl;
01810             }
01811         }
01812     }
01813     if (config.m_synergyAddress.isValid()) {
01814         s << "\taddress = " <<
01815             config.m_synergyAddress.getHostname().c_str() << std::endl;
01816     }
01817     s << config.m_inputFilter.format("\t");
01818     s << "end" << std::endl;
01819 
01820     return s;
01821 }
01822 
01823 
01824 //
01825 // CConfigReadContext
01826 //
01827 
01828 CConfigReadContext::CConfigReadContext(std::istream& s, SInt32 firstLine) :
01829     m_stream(s),
01830     m_line(firstLine - 1)
01831 {
01832     // do nothing
01833 }
01834 
01835 CConfigReadContext::~CConfigReadContext()
01836 {
01837     // do nothing
01838 }
01839 
01840 bool
01841 CConfigReadContext::readLine(CString& line)
01842 {
01843     ++m_line;
01844     while (std::getline(m_stream, line)) {
01845         // strip leading whitespace
01846         CString::size_type i = line.find_first_not_of(" \t");
01847         if (i != CString::npos) {
01848             line.erase(0, i);
01849         }
01850 
01851         // strip comments and then trailing whitespace
01852         i = line.find('#');
01853         if (i != CString::npos) {
01854             line.erase(i);
01855         }
01856         i = line.find_last_not_of(" \r\t");
01857         if (i != CString::npos) {
01858             line.erase(i + 1);
01859         }
01860 
01861         // return non empty line
01862         if (!line.empty()) {
01863             // make sure there are no invalid characters
01864             for (i = 0; i < line.length(); ++i) {
01865                 if (!isgraph(line[i]) && line[i] != ' ' && line[i] != '\t') {
01866                     throw XConfigRead(*this,
01867                                 "invalid character %{1}",
01868                                 CStringUtil::print("%#2x", line[i]));
01869                 }
01870             }
01871 
01872             return true;
01873         }
01874 
01875         // next line
01876         ++m_line;
01877     }
01878     return false;
01879 }
01880 
01881 UInt32
01882 CConfigReadContext::getLineNumber() const
01883 {
01884     return m_line;
01885 }
01886 
01887 CConfigReadContext::operator void*() const
01888 {
01889     return m_stream;
01890 }
01891 
01892 bool
01893 CConfigReadContext::operator!() const
01894 {
01895     return !m_stream;
01896 }
01897 
01898 OptionValue
01899 CConfigReadContext::parseBoolean(const CString& arg) const
01900 {
01901     if (CStringUtil::CaselessCmp::equal(arg, "true")) {
01902         return static_cast<OptionValue>(true);
01903     }
01904     if (CStringUtil::CaselessCmp::equal(arg, "false")) {
01905         return static_cast<OptionValue>(false);
01906     }
01907     throw XConfigRead(*this, "invalid boolean argument \"%{1}\"", arg);
01908 }
01909 
01910 OptionValue
01911 CConfigReadContext::parseInt(const CString& arg) const
01912 {
01913     const char* s = arg.c_str();
01914     char* end;
01915     long tmp      = strtol(s, &end, 10);
01916     if (*end != '\0') {
01917         // invalid characters
01918         throw XConfigRead(*this, "invalid integer argument \"%{1}\"", arg);
01919     }
01920     OptionValue value = static_cast<OptionValue>(tmp);
01921     if (value != tmp) {
01922         // out of range
01923         throw XConfigRead(*this, "integer argument \"%{1}\" out of range", arg);
01924     }
01925     return value;
01926 }
01927 
01928 OptionValue
01929 CConfigReadContext::parseModifierKey(const CString& arg) const
01930 {
01931     if (CStringUtil::CaselessCmp::equal(arg, "shift")) {
01932         return static_cast<OptionValue>(kKeyModifierIDShift);
01933     }
01934     if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) {
01935         return static_cast<OptionValue>(kKeyModifierIDControl);
01936     }
01937     if (CStringUtil::CaselessCmp::equal(arg, "alt")) {
01938         return static_cast<OptionValue>(kKeyModifierIDAlt);
01939     }
01940     if (CStringUtil::CaselessCmp::equal(arg, "meta")) {
01941         return static_cast<OptionValue>(kKeyModifierIDMeta);
01942     }
01943     if (CStringUtil::CaselessCmp::equal(arg, "super")) {
01944         return static_cast<OptionValue>(kKeyModifierIDSuper);
01945     }
01946     if (CStringUtil::CaselessCmp::equal(arg, "none")) {
01947         return static_cast<OptionValue>(kKeyModifierIDNull);
01948     }
01949     throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
01950 }
01951 
01952 OptionValue
01953 CConfigReadContext::parseCorner(const CString& arg) const
01954 {
01955     if (CStringUtil::CaselessCmp::equal(arg, "left")) {
01956         return kTopLeftMask | kBottomLeftMask;
01957     }
01958     else if (CStringUtil::CaselessCmp::equal(arg, "right")) {
01959         return kTopRightMask | kBottomRightMask;
01960     }
01961     else if (CStringUtil::CaselessCmp::equal(arg, "top")) {
01962         return kTopLeftMask | kTopRightMask;
01963     }
01964     else if (CStringUtil::CaselessCmp::equal(arg, "bottom")) {
01965         return kBottomLeftMask | kBottomRightMask;
01966     }
01967     else if (CStringUtil::CaselessCmp::equal(arg, "top-left")) {
01968         return kTopLeftMask;
01969     }
01970     else if (CStringUtil::CaselessCmp::equal(arg, "top-right")) {
01971         return kTopRightMask;
01972     }
01973     else if (CStringUtil::CaselessCmp::equal(arg, "bottom-left")) {
01974         return kBottomLeftMask;
01975     }
01976     else if (CStringUtil::CaselessCmp::equal(arg, "bottom-right")) {
01977         return kBottomRightMask;
01978     }
01979     else if (CStringUtil::CaselessCmp::equal(arg, "none")) {
01980         return kNoCornerMask;
01981     }
01982     else if (CStringUtil::CaselessCmp::equal(arg, "all")) {
01983         return kAllCornersMask;
01984     }
01985     throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
01986 }
01987 
01988 OptionValue
01989 CConfigReadContext::parseCorners(const CString& args) const
01990 {
01991     // find first token
01992     CString::size_type i = args.find_first_not_of(" \t", 0);
01993     if (i == CString::npos) {
01994         throw XConfigRead(*this, "missing corner argument");
01995     }
01996     CString::size_type j = args.find_first_of(" \t", i);
01997 
01998     // parse first corner token
01999     OptionValue corners = parseCorner(args.substr(i, j - i));
02000 
02001     // get +/-
02002     i = args.find_first_not_of(" \t", j);
02003     while (i != CString::npos) {
02004         // parse +/-
02005         bool add;
02006         if (args[i] == '-') {
02007             add = false;
02008         }
02009         else if (args[i] == '+') {
02010             add = true;
02011         }
02012         else {
02013             throw XConfigRead(*this,
02014                             "invalid corner operator \"%{1}\"",
02015                             CString(args.c_str() + i, 1));
02016         }
02017 
02018         // get next corner token
02019         i = args.find_first_not_of(" \t", i + 1);
02020         j = args.find_first_of(" \t", i);
02021         if (i == CString::npos) {
02022             throw XConfigRead(*this, "missing corner argument");
02023         }
02024 
02025         // parse next corner token
02026         if (add) {
02027             corners |= parseCorner(args.substr(i, j - i));
02028         }
02029         else {
02030             corners &= ~parseCorner(args.substr(i, j - i));
02031         }
02032         i = args.find_first_not_of(" \t", j);
02033     }
02034 
02035     return corners;
02036 }
02037 
02038 CConfig::CInterval
02039 CConfigReadContext::parseInterval(const ArgList& args) const
02040 {
02041     if (args.size() == 0) {
02042         return CConfig::CInterval(0.0f, 1.0f);
02043     }
02044     if (args.size() != 2 || args[0].empty() || args[1].empty()) {
02045         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02046     }
02047 
02048     char* end;
02049     long startValue = strtol(args[0].c_str(), &end, 10);
02050     if (end[0] != '\0') {
02051         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02052     }
02053     long endValue = strtol(args[1].c_str(), &end, 10);
02054     if (end[0] != '\0') {
02055         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02056     }
02057 
02058     if (startValue < 0 || startValue > 100 ||
02059         endValue   < 0 || endValue   > 100 ||
02060         startValue >= endValue) {
02061         throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
02062     }
02063 
02064     return CConfig::CInterval(startValue / 100.0f, endValue / 100.0f);
02065 }
02066 
02067 void
02068 CConfigReadContext::parseNameWithArgs(
02069                 const CString& type, const CString& line,
02070                 const CString& delim, CString::size_type& index,
02071                 CString& name, ArgList& args) const
02072 {
02073     // skip leading whitespace
02074     CString::size_type i = line.find_first_not_of(" \t", index);
02075     if (i == CString::npos) {
02076         throw XConfigRead(*this, CString("missing ") + type);
02077     }
02078 
02079     // find end of name
02080     CString::size_type j = line.find_first_of(" \t(" + delim, i);
02081     if (j == CString::npos) {
02082         j = line.length();
02083     }
02084 
02085     // save name
02086     name = line.substr(i, j - i);
02087     args.clear();
02088 
02089     // is it okay to not find a delimiter?
02090     bool needDelim = (!delim.empty() && delim.find('\n') == CString::npos);
02091 
02092     // skip whitespace
02093     i = line.find_first_not_of(" \t", j);
02094     if (i == CString::npos && needDelim) {
02095         // expected delimiter but didn't find it
02096         throw XConfigRead(*this, CString("missing ") + delim[0]);
02097     }
02098     if (i == CString::npos) {
02099         // no arguments
02100         index = line.length();
02101         return;
02102     }
02103     if (line[i] != '(') {
02104         // no arguments
02105         index = i;
02106         return;
02107     }
02108 
02109     // eat '('
02110     ++i;
02111 
02112     // parse arguments
02113     j = line.find_first_of(",)", i);
02114     while (j != CString::npos) {
02115         // extract arg
02116         CString arg(line.substr(i, j - i));
02117         i = j;
02118 
02119         // trim whitespace
02120         j = arg.find_first_not_of(" \t");
02121         if (j != CString::npos) {
02122             arg.erase(0, j);
02123         }
02124         j = arg.find_last_not_of(" \t");
02125         if (j != CString::npos) {
02126             arg.erase(j + 1);
02127         }
02128 
02129         // save arg
02130         args.push_back(arg);
02131 
02132         // exit loop at end of arguments
02133         if (line[i] == ')') {
02134             break;
02135         }
02136 
02137         // eat ','
02138         ++i;
02139 
02140         // next
02141         j = line.find_first_of(",)", i);
02142     }
02143 
02144     // verify ')'
02145     if (j == CString::npos) {
02146         // expected )
02147         throw XConfigRead(*this, "missing )");
02148     }
02149 
02150     // eat ')'
02151     ++i;
02152 
02153     // skip whitespace
02154     j = line.find_first_not_of(" \t", i);
02155     if (j == CString::npos && needDelim) {
02156         // expected delimiter but didn't find it
02157         throw XConfigRead(*this, CString("missing ") + delim[0]);
02158     }
02159 
02160     // verify delimiter
02161     if (needDelim && delim.find(line[j]) == CString::npos) {
02162         throw XConfigRead(*this, CString("expected ") + delim[0]);
02163     }
02164 
02165     if (j == CString::npos) {
02166         j = line.length();
02167     }
02168 
02169     index = j;
02170     return;
02171 }
02172 
02173 IPlatformScreen::CKeyInfo*
02174 CConfigReadContext::parseKeystroke(const CString& keystroke) const
02175 {
02176     return parseKeystroke(keystroke, std::set<CString>());
02177 }
02178 
02179 IPlatformScreen::CKeyInfo*
02180 CConfigReadContext::parseKeystroke(const CString& keystroke,
02181                 const std::set<CString>& screens) const
02182 {
02183     CString s = keystroke;
02184 
02185     KeyModifierMask mask;
02186     if (!CKeyMap::parseModifiers(s, mask)) {
02187         throw XConfigRead(*this, "unable to parse key modifiers");
02188     }
02189 
02190     KeyID key;
02191     if (!CKeyMap::parseKey(s, key)) {
02192         throw XConfigRead(*this, "unable to parse key");
02193     }
02194 
02195     if (key == kKeyNone && mask == 0) {
02196         throw XConfigRead(*this, "missing key and/or modifiers in keystroke");
02197     }
02198 
02199     return IPlatformScreen::CKeyInfo::alloc(key, mask, 0, 0, screens);
02200 }
02201 
02202 IPlatformScreen::CButtonInfo*
02203 CConfigReadContext::parseMouse(const CString& mouse) const
02204 {
02205     CString s = mouse;
02206 
02207     KeyModifierMask mask;
02208     if (!CKeyMap::parseModifiers(s, mask)) {
02209         throw XConfigRead(*this, "unable to parse button modifiers");
02210     }
02211 
02212     char* end;
02213     ButtonID button = (ButtonID)strtol(s.c_str(), &end, 10);
02214     if (*end != '\0') {
02215         throw XConfigRead(*this, "unable to parse button");
02216     }
02217     if (s.empty() || button <= 0) {
02218         throw XConfigRead(*this, "invalid button");
02219     }
02220 
02221     return IPlatformScreen::CButtonInfo::alloc(button, mask);
02222 }
02223 
02224 KeyModifierMask
02225 CConfigReadContext::parseModifier(const CString& modifiers) const
02226 {
02227     CString s = modifiers;
02228 
02229     KeyModifierMask mask;
02230     if (!CKeyMap::parseModifiers(s, mask)) {
02231         throw XConfigRead(*this, "unable to parse modifiers");
02232     }
02233 
02234     if (mask == 0) {
02235         throw XConfigRead(*this, "no modifiers specified");
02236     }
02237 
02238     return mask;
02239 }
02240 
02241 CString
02242 CConfigReadContext::concatArgs(const ArgList& args)
02243 {
02244     CString s("(");
02245     for (size_t i = 0; i < args.size(); ++i) {
02246         if (i != 0) {
02247             s += ",";
02248         }
02249         s += args[i];
02250     }
02251     s += ")";
02252     return s;
02253 }
02254 
02255 
02256 //
02257 // CConfig I/O exceptions
02258 //
02259 
02260 XConfigRead::XConfigRead(const CConfigReadContext& context,
02261                 const CString& error) :
02262     m_error(CStringUtil::print("line %d: %s",
02263                             context.getLineNumber(), error.c_str()))
02264 {
02265     // do nothing
02266 }
02267 
02268 XConfigRead::XConfigRead(const CConfigReadContext& context,
02269                 const char* errorFmt, const CString& arg) :
02270     m_error(CStringUtil::print("line %d: ", context.getLineNumber()) +
02271                             CStringUtil::format(errorFmt, arg.c_str()))
02272 {
02273     // do nothing
02274 }
02275 
02276 XConfigRead::~XConfigRead()
02277 {
02278     // do nothing
02279 }
02280 
02281 CString
02282 XConfigRead::getWhat() const throw()
02283 {
02284     return format("XConfigRead", "read error: %{1}", m_error.c_str());
02285 }

Generated on Fri Nov 6 00:21:13 2009 for synergy-plus by  doxygen 1.3.9.1