source: vendor/synergy/current/lib/server/CConfig.cpp

Last change on this file was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

File size: 50.7 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2002 Chris Schoeneman
4 *
5 * This package is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * found in the file COPYING that should have accompanied this file.
8 *
9 * This package is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "CConfig.h"
16#include "CServer.h"
17#include "CKeyMap.h"
18#include "KeyTypes.h"
19#include "XSocket.h"
20#include "stdistream.h"
21#include "stdostream.h"
22#include <stdlib.h>
23
24//
25// CConfig
26//
27
28CConfig::CConfig() : m_hasLockToScreenAction(false)
29{
30 // do nothing
31}
32
33CConfig::~CConfig()
34{
35 // do nothing
36}
37
38bool
39CConfig::addScreen(const CString& name)
40{
41 // alias name must not exist
42 if (m_nameToCanonicalName.find(name) != m_nameToCanonicalName.end()) {
43 return false;
44 }
45
46 // add cell
47 m_map.insert(std::make_pair(name, CCell()));
48
49 // add name
50 m_nameToCanonicalName.insert(std::make_pair(name, name));
51
52 return true;
53}
54
55bool
56CConfig::renameScreen(const CString& oldName,
57 const CString& newName)
58{
59 // get canonical name and find cell
60 CString oldCanonical = getCanonicalName(oldName);
61 CCellMap::iterator index = m_map.find(oldCanonical);
62 if (index == m_map.end()) {
63 return false;
64 }
65
66 // accept if names are equal but replace with new name to maintain
67 // case. otherwise, the new name must not exist.
68 if (!CStringUtil::CaselessCmp::equal(oldName, newName) &&
69 m_nameToCanonicalName.find(newName) != m_nameToCanonicalName.end()) {
70 return false;
71 }
72
73 // update cell
74 CCell tmpCell = index->second;
75 m_map.erase(index);
76 m_map.insert(std::make_pair(newName, tmpCell));
77
78 // update name
79 m_nameToCanonicalName.erase(oldCanonical);
80 m_nameToCanonicalName.insert(std::make_pair(newName, newName));
81
82 // update connections
83 CName oldNameObj(this, oldName);
84 for (index = m_map.begin(); index != m_map.end(); ++index) {
85 index->second.rename(oldNameObj, newName);
86 }
87
88 // update alias targets
89 if (CStringUtil::CaselessCmp::equal(oldName, oldCanonical)) {
90 for (CNameMap::iterator index = m_nameToCanonicalName.begin();
91 index != m_nameToCanonicalName.end(); ++index) {
92 if (CStringUtil::CaselessCmp::equal(
93 index->second, oldCanonical)) {
94 index->second = newName;
95 }
96 }
97 }
98
99 return true;
100}
101
102void
103CConfig::removeScreen(const CString& name)
104{
105 // get canonical name and find cell
106 CString canonical = getCanonicalName(name);
107 CCellMap::iterator index = m_map.find(canonical);
108 if (index == m_map.end()) {
109 return;
110 }
111
112 // remove from map
113 m_map.erase(index);
114
115 // disconnect
116 CName nameObj(this, name);
117 for (index = m_map.begin(); index != m_map.end(); ++index) {
118 index->second.remove(nameObj);
119 }
120
121 // remove aliases (and canonical name)
122 for (CNameMap::iterator index = m_nameToCanonicalName.begin();
123 index != m_nameToCanonicalName.end(); ) {
124 if (index->second == canonical) {
125 m_nameToCanonicalName.erase(index++);
126 }
127 else {
128 ++index;
129 }
130 }
131}
132
133void
134CConfig::removeAllScreens()
135{
136 m_map.clear();
137 m_nameToCanonicalName.clear();
138}
139
140bool
141CConfig::addAlias(const CString& canonical, const CString& alias)
142{
143 // alias name must not exist
144 if (m_nameToCanonicalName.find(alias) != m_nameToCanonicalName.end()) {
145 return false;
146 }
147
148 // canonical name must be known
149 if (m_map.find(canonical) == m_map.end()) {
150 return false;
151 }
152
153 // insert alias
154 m_nameToCanonicalName.insert(std::make_pair(alias, canonical));
155
156 return true;
157}
158
159bool
160CConfig::removeAlias(const CString& alias)
161{
162 // must not be a canonical name
163 if (m_map.find(alias) != m_map.end()) {
164 return false;
165 }
166
167 // find alias
168 CNameMap::iterator index = m_nameToCanonicalName.find(alias);
169 if (index == m_nameToCanonicalName.end()) {
170 return false;
171 }
172
173 // remove alias
174 m_nameToCanonicalName.erase(index);
175
176 return true;
177}
178
179bool
180CConfig::removeAliases(const CString& canonical)
181{
182 // must be a canonical name
183 if (m_map.find(canonical) == m_map.end()) {
184 return false;
185 }
186
187 // find and removing matching aliases
188 for (CNameMap::iterator index = m_nameToCanonicalName.begin();
189 index != m_nameToCanonicalName.end(); ) {
190 if (index->second == canonical && index->first != canonical) {
191 m_nameToCanonicalName.erase(index++);
192 }
193 else {
194 ++index;
195 }
196 }
197
198 return true;
199}
200
201void
202CConfig::removeAllAliases()
203{
204 // remove all names
205 m_nameToCanonicalName.clear();
206
207 // put the canonical names back in
208 for (CCellMap::iterator index = m_map.begin();
209 index != m_map.end(); ++index) {
210 m_nameToCanonicalName.insert(
211 std::make_pair(index->first, index->first));
212 }
213}
214
215bool
216CConfig::connect(const CString& srcName,
217 EDirection srcSide,
218 float srcStart, float srcEnd,
219 const CString& dstName,
220 float dstStart, float dstEnd)
221{
222 assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
223
224 // find source cell
225 CCellMap::iterator index = m_map.find(getCanonicalName(srcName));
226 if (index == m_map.end()) {
227 return false;
228 }
229
230 // add link
231 CCellEdge srcEdge(srcSide, CInterval(srcStart, srcEnd));
232 CCellEdge dstEdge(dstName, srcSide, CInterval(dstStart, dstEnd));
233 return index->second.add(srcEdge, dstEdge);
234}
235
236bool
237CConfig::disconnect(const CString& srcName, EDirection srcSide)
238{
239 assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
240
241 // find source cell
242 CCellMap::iterator index = m_map.find(srcName);
243 if (index == m_map.end()) {
244 return false;
245 }
246
247 // disconnect side
248 index->second.remove(srcSide);
249
250 return true;
251}
252
253bool
254CConfig::disconnect(const CString& srcName, EDirection srcSide, float position)
255{
256 assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
257
258 // find source cell
259 CCellMap::iterator index = m_map.find(srcName);
260 if (index == m_map.end()) {
261 return false;
262 }
263
264 // disconnect side
265 index->second.remove(srcSide, position);
266
267 return true;
268}
269
270void
271CConfig::setSynergyAddress(const CNetworkAddress& addr)
272{
273 m_synergyAddress = addr;
274}
275
276bool
277CConfig::addOption(const CString& name, OptionID option, OptionValue value)
278{
279 // find options
280 CScreenOptions* options = NULL;
281 if (name.empty()) {
282 options = &m_globalOptions;
283 }
284 else {
285 CCellMap::iterator index = m_map.find(name);
286 if (index != m_map.end()) {
287 options = &index->second.m_options;
288 }
289 }
290 if (options == NULL) {
291 return false;
292 }
293
294 // add option
295 options->insert(std::make_pair(option, value));
296 return true;
297}
298
299bool
300CConfig::removeOption(const CString& name, OptionID option)
301{
302 // find options
303 CScreenOptions* options = NULL;
304 if (name.empty()) {
305 options = &m_globalOptions;
306 }
307 else {
308 CCellMap::iterator index = m_map.find(name);
309 if (index != m_map.end()) {
310 options = &index->second.m_options;
311 }
312 }
313 if (options == NULL) {
314 return false;
315 }
316
317 // remove option
318 options->erase(option);
319 return true;
320}
321
322bool
323CConfig::removeOptions(const CString& name)
324{
325 // find options
326 CScreenOptions* options = NULL;
327 if (name.empty()) {
328 options = &m_globalOptions;
329 }
330 else {
331 CCellMap::iterator index = m_map.find(name);
332 if (index != m_map.end()) {
333 options = &index->second.m_options;
334 }
335 }
336 if (options == NULL) {
337 return false;
338 }
339
340 // remove options
341 options->clear();
342 return true;
343}
344
345bool
346CConfig::isValidScreenName(const CString& name) const
347{
348 // name is valid if matches validname
349 // name ::= [_A-Za-z0-9] | [_A-Za-z0-9][-_A-Za-z0-9]*[_A-Za-z0-9]
350 // domain ::= . name
351 // validname ::= name domain*
352 // we also accept names ending in . because many OS X users have
353 // so misconfigured their systems.
354
355 // empty name is invalid
356 if (name.empty()) {
357 return false;
358 }
359
360 // check each dot separated part
361 CString::size_type b = 0;
362 for (;;) {
363 // accept trailing .
364 if (b == name.size()) {
365 break;
366 }
367
368 // find end of part
369 CString::size_type e = name.find('.', b);
370 if (e == CString::npos) {
371 e = name.size();
372 }
373
374 // part may not be empty
375 if (e - b < 1) {
376 return false;
377 }
378
379 // check first and last characters
380 if (!(isalnum(name[b]) || name[b] == '_') ||
381 !(isalnum(name[e - 1]) || name[e - 1] == '_')) {
382 return false;
383 }
384
385 // check interior characters
386 for (CString::size_type i = b; i < e; ++i) {
387 if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-') {
388 return false;
389 }
390 }
391
392 // next part
393 if (e == name.size()) {
394 // no more parts
395 break;
396 }
397 b = e + 1;
398 }
399
400 return true;
401}
402
403CConfig::const_iterator
404CConfig::begin() const
405{
406 return const_iterator(m_map.begin());
407}
408
409CConfig::const_iterator
410CConfig::end() const
411{
412 return const_iterator(m_map.end());
413}
414
415CConfig::all_const_iterator
416CConfig::beginAll() const
417{
418 return m_nameToCanonicalName.begin();
419}
420
421CConfig::all_const_iterator
422CConfig::endAll() const
423{
424 return m_nameToCanonicalName.end();
425}
426
427bool
428CConfig::isScreen(const CString& name) const
429{
430 return (m_nameToCanonicalName.count(name) > 0);
431}
432
433bool
434CConfig::isCanonicalName(const CString& name) const
435{
436 return (!name.empty() &&
437 CStringUtil::CaselessCmp::equal(getCanonicalName(name), name));
438}
439
440CString
441CConfig::getCanonicalName(const CString& name) const
442{
443 CNameMap::const_iterator index = m_nameToCanonicalName.find(name);
444 if (index == m_nameToCanonicalName.end()) {
445 return CString();
446 }
447 else {
448 return index->second;
449 }
450}
451
452CString
453CConfig::getNeighbor(const CString& srcName, EDirection srcSide,
454 float position, float* positionOut) const
455{
456 assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
457
458 // find source cell
459 CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
460 if (index == m_map.end()) {
461 return CString();
462 }
463
464 // find edge
465 const CCellEdge* srcEdge, *dstEdge;
466 if (!index->second.getLink(srcSide, position, srcEdge, dstEdge)) {
467 // no neighbor
468 return "";
469 }
470 else {
471 // compute position on neighbor
472 if (positionOut != NULL) {
473 *positionOut =
474 dstEdge->inverseTransform(srcEdge->transform(position));
475 }
476
477 // return neighbor's name
478 return getCanonicalName(dstEdge->getName());
479 }
480}
481
482bool
483CConfig::hasNeighbor(const CString& srcName, EDirection srcSide) const
484{
485 return hasNeighbor(srcName, srcSide, 0.0f, 1.0f);
486}
487
488bool
489CConfig::hasNeighbor(const CString& srcName, EDirection srcSide,
490 float start, float end) const
491{
492 assert(srcSide >= kFirstDirection && srcSide <= kLastDirection);
493
494 // find source cell
495 CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
496 if (index == m_map.end()) {
497 return false;
498 }
499
500 return index->second.overlaps(CCellEdge(srcSide, CInterval(start, end)));
501}
502
503CConfig::link_const_iterator
504CConfig::beginNeighbor(const CString& srcName) const
505{
506 CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
507 assert(index != m_map.end());
508 return index->second.begin();
509}
510
511CConfig::link_const_iterator
512CConfig::endNeighbor(const CString& srcName) const
513{
514 CCellMap::const_iterator index = m_map.find(getCanonicalName(srcName));
515 assert(index != m_map.end());
516 return index->second.end();
517}
518
519const CNetworkAddress&
520CConfig::getSynergyAddress() const
521{
522 return m_synergyAddress;
523}
524
525const CConfig::CScreenOptions*
526CConfig::getOptions(const CString& name) const
527{
528 // find options
529 const CScreenOptions* options = NULL;
530 if (name.empty()) {
531 options = &m_globalOptions;
532 }
533 else {
534 CCellMap::const_iterator index = m_map.find(name);
535 if (index != m_map.end()) {
536 options = &index->second.m_options;
537 }
538 }
539
540 // return options
541 return options;
542}
543
544bool
545CConfig::hasLockToScreenAction() const
546{
547 return m_hasLockToScreenAction;
548}
549
550bool
551CConfig::operator==(const CConfig& x) const
552{
553 if (m_synergyAddress != x.m_synergyAddress) {
554 return false;
555 }
556 if (m_map.size() != x.m_map.size()) {
557 return false;
558 }
559 if (m_nameToCanonicalName.size() != x.m_nameToCanonicalName.size()) {
560 return false;
561 }
562
563 // compare global options
564 if (m_globalOptions != x.m_globalOptions) {
565 return false;
566 }
567
568 for (CCellMap::const_iterator index1 = m_map.begin(),
569 index2 = x.m_map.begin();
570 index1 != m_map.end(); ++index1, ++index2) {
571 // compare names
572 if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first)) {
573 return false;
574 }
575
576 // compare cells
577 if (index1->second != index2->second) {
578 return false;
579 }
580 }
581
582 for (CNameMap::const_iterator index1 = m_nameToCanonicalName.begin(),
583 index2 = x.m_nameToCanonicalName.begin();
584 index1 != m_nameToCanonicalName.end();
585 ++index1, ++index2) {
586 if (!CStringUtil::CaselessCmp::equal(index1->first, index2->first) ||
587 !CStringUtil::CaselessCmp::equal(index1->second, index2->second)) {
588 return false;
589 }
590 }
591
592 // compare input filters
593 if (m_inputFilter != x.m_inputFilter) {
594 return false;
595 }
596
597 return true;
598}
599
600bool
601CConfig::operator!=(const CConfig& x) const
602{
603 return !operator==(x);
604}
605
606void
607CConfig::read(CConfigReadContext& context)
608{
609 CConfig tmp;
610 while (context) {
611 tmp.readSection(context);
612 }
613 *this = tmp;
614}
615
616const char*
617CConfig::dirName(EDirection dir)
618{
619 static const char* s_name[] = { "left", "right", "up", "down" };
620
621 assert(dir >= kFirstDirection && dir <= kLastDirection);
622
623 return s_name[dir - kFirstDirection];
624}
625
626CInputFilter*
627CConfig::getInputFilter()
628{
629 return &m_inputFilter;
630}
631
632CString
633CConfig::formatInterval(const CInterval& x)
634{
635 if (x.first == 0.0f && x.second == 1.0f) {
636 return "";
637 }
638 return CStringUtil::print("(%d,%d)", (int)(x.first * 100.0f + 0.5f),
639 (int)(x.second * 100.0f + 0.5f));
640}
641
642void
643CConfig::readSection(CConfigReadContext& s)
644{
645 static const char s_section[] = "section:";
646 static const char s_options[] = "options";
647 static const char s_screens[] = "screens";
648 static const char s_links[] = "links";
649 static const char s_aliases[] = "aliases";
650
651 CString line;
652 if (!s.readLine(line)) {
653 // no more sections
654 return;
655 }
656
657 // should be a section header
658 if (line.find(s_section) != 0) {
659 throw XConfigRead(s, "found data outside section");
660 }
661
662 // get section name
663 CString::size_type i = line.find_first_not_of(" \t", sizeof(s_section) - 1);
664 if (i == CString::npos) {
665 throw XConfigRead(s, "section name is missing");
666 }
667 CString name = line.substr(i);
668 i = name.find_first_of(" \t");
669 if (i != CString::npos) {
670 throw XConfigRead(s, "unexpected data after section name");
671 }
672
673 // read section
674 if (name == s_options) {
675 readSectionOptions(s);
676 }
677 else if (name == s_screens) {
678 readSectionScreens(s);
679 }
680 else if (name == s_links) {
681 readSectionLinks(s);
682 }
683 else if (name == s_aliases) {
684 readSectionAliases(s);
685 }
686 else {
687 throw XConfigRead(s, "unknown section name \"%{1}\"", name);
688 }
689}
690
691void
692CConfig::readSectionOptions(CConfigReadContext& s)
693{
694 CString line;
695 while (s.readLine(line)) {
696 // check for end of section
697 if (line == "end") {
698 return;
699 }
700
701 // parse argument: `nameAndArgs = [values][;[values]]'
702 // nameAndArgs := <name>[(arg[,...])]
703 // values := valueAndArgs[,valueAndArgs]...
704 // valueAndArgs := <value>[(arg[,...])]
705 CString::size_type i = 0;
706 CString name, value;
707 CConfigReadContext::ArgList nameArgs, valueArgs;
708 s.parseNameWithArgs("name", line, "=", i, name, nameArgs);
709 ++i;
710 s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
711
712 bool handled = true;
713 if (name == "address") {
714 try {
715 m_synergyAddress = CNetworkAddress(value, kDefaultPort);
716 m_synergyAddress.resolve();
717 }
718 catch (XSocketAddress& e) {
719 throw XConfigRead(s,
720 CString("invalid address argument ") + e.what());
721 }
722 }
723 else if (name == "heartbeat") {
724 addOption("", kOptionHeartbeat, s.parseInt(value));
725 }
726 else if (name == "switchCorners") {
727 addOption("", kOptionScreenSwitchCorners, s.parseCorners(value));
728 }
729 else if (name == "switchCornerSize") {
730 addOption("", kOptionScreenSwitchCornerSize, s.parseInt(value));
731 }
732 else if (name == "switchDelay") {
733 addOption("", kOptionScreenSwitchDelay, s.parseInt(value));
734 }
735 else if (name == "switchDoubleTap") {
736 addOption("", kOptionScreenSwitchTwoTap, s.parseInt(value));
737 }
738 else if (name == "screenSaverSync") {
739 addOption("", kOptionScreenSaverSync, s.parseBoolean(value));
740 }
741 else if (name == "relativeMouseMoves") {
742 addOption("", kOptionRelativeMouseMoves, s.parseBoolean(value));
743 }
744 else if (name == "win32KeepForeground") {
745 addOption("", kOptionWin32KeepForeground, s.parseBoolean(value));
746 }
747 else {
748 handled = false;
749 }
750
751 if (handled) {
752 // make sure handled options aren't followed by more values
753 if (i < line.size() && (line[i] == ',' || line[i] == ';')) {
754 throw XConfigRead(s, "to many arguments to %s", name.c_str());
755 }
756 }
757 else {
758 // make filter rule
759 CInputFilter::CRule rule(parseCondition(s, name, nameArgs));
760
761 // save first action (if any)
762 if (!value.empty() || line[i] != ';') {
763 parseAction(s, value, valueArgs, rule, true);
764 }
765
766 // get remaining activate actions
767 while (i < line.length() && line[i] != ';') {
768 ++i;
769 s.parseNameWithArgs("value", line, ",;\n", i, value, valueArgs);
770 parseAction(s, value, valueArgs, rule, true);
771 }
772
773 // get deactivate actions
774 if (i < line.length() && line[i] == ';') {
775 // allow trailing ';'
776 i = line.find_first_not_of(" \t", i + 1);
777 if (i == CString::npos) {
778 i = line.length();
779 }
780 else {
781 --i;
782 }
783
784 // get actions
785 while (i < line.length()) {
786 ++i;
787 s.parseNameWithArgs("value", line, ",\n",
788 i, value, valueArgs);
789 parseAction(s, value, valueArgs, rule, false);
790 }
791 }
792
793 // add rule
794 m_inputFilter.addFilterRule(rule);
795 }
796 }
797 throw XConfigRead(s, "unexpected end of options section");
798}
799
800void
801CConfig::readSectionScreens(CConfigReadContext& s)
802{
803 CString line;
804 CString screen;
805 while (s.readLine(line)) {
806 // check for end of section
807 if (line == "end") {
808 return;
809 }
810
811 // see if it's the next screen
812 if (line[line.size() - 1] == ':') {
813 // strip :
814 screen = line.substr(0, line.size() - 1);
815
816 // verify validity of screen name
817 if (!isValidScreenName(screen)) {
818 throw XConfigRead(s, "invalid screen name \"%{1}\"", screen);
819 }
820
821 // add the screen to the configuration
822 if (!addScreen(screen)) {
823 throw XConfigRead(s, "duplicate screen name \"%{1}\"", screen);
824 }
825 }
826 else if (screen.empty()) {
827 throw XConfigRead(s, "argument before first screen");
828 }
829 else {
830 // parse argument: `<name>=<value>'
831 CString::size_type i = line.find_first_of(" \t=");
832 if (i == 0) {
833 throw XConfigRead(s, "missing argument name");
834 }
835 if (i == CString::npos) {
836 throw XConfigRead(s, "missing =");
837 }
838 CString name = line.substr(0, i);
839 i = line.find_first_not_of(" \t", i);
840 if (i == CString::npos || line[i] != '=') {
841 throw XConfigRead(s, "missing =");
842 }
843 i = line.find_first_not_of(" \t", i + 1);
844 CString value;
845 if (i != CString::npos) {
846 value = line.substr(i);
847 }
848
849 // handle argument
850 if (name == "halfDuplexCapsLock") {
851 addOption(screen, kOptionHalfDuplexCapsLock,
852 s.parseBoolean(value));
853 }
854 else if (name == "halfDuplexNumLock") {
855 addOption(screen, kOptionHalfDuplexNumLock,
856 s.parseBoolean(value));
857 }
858 else if (name == "halfDuplexScrollLock") {
859 addOption(screen, kOptionHalfDuplexScrollLock,
860 s.parseBoolean(value));
861 }
862 else if (name == "shift") {
863 addOption(screen, kOptionModifierMapForShift,
864 s.parseModifierKey(value));
865 }
866 else if (name == "ctrl") {
867 addOption(screen, kOptionModifierMapForControl,
868 s.parseModifierKey(value));
869 }
870 else if (name == "alt") {
871 addOption(screen, kOptionModifierMapForAlt,
872 s.parseModifierKey(value));
873 }
874 else if (name == "meta") {
875 addOption(screen, kOptionModifierMapForMeta,
876 s.parseModifierKey(value));
877 }
878 else if (name == "super") {
879 addOption(screen, kOptionModifierMapForSuper,
880 s.parseModifierKey(value));
881 }
882 else if (name == "xtestIsXineramaUnaware") {
883 addOption(screen, kOptionXTestXineramaUnaware,
884 s.parseBoolean(value));
885 }
886 else if (name == "switchCorners") {
887 addOption(screen, kOptionScreenSwitchCorners,
888 s.parseCorners(value));
889 }
890 else if (name == "switchCornerSize") {
891 addOption(screen, kOptionScreenSwitchCornerSize,
892 s.parseInt(value));
893 }
894 else {
895 // unknown argument
896 throw XConfigRead(s, "unknown argument \"%{1}\"", name);
897 }
898 }
899 }
900 throw XConfigRead(s, "unexpected end of screens section");
901}
902
903void
904CConfig::readSectionLinks(CConfigReadContext& s)
905{
906 CString line;
907 CString screen;
908 while (s.readLine(line)) {
909 // check for end of section
910 if (line == "end") {
911 return;
912 }
913
914 // see if it's the next screen
915 if (line[line.size() - 1] == ':') {
916 // strip :
917 screen = line.substr(0, line.size() - 1);
918
919 // verify we know about the screen
920 if (!isScreen(screen)) {
921 throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
922 }
923 if (!isCanonicalName(screen)) {
924 throw XConfigRead(s, "cannot use screen name alias here");
925 }
926 }
927 else if (screen.empty()) {
928 throw XConfigRead(s, "argument before first screen");
929 }
930 else {
931 // parse argument: `<name>[(<s0>,<e0>)]=<value>[(<s1>,<e1>)]'
932 // the stuff in brackets is optional. interval values must be
933 // in the range [0,100] and start < end. if not given the
934 // interval is taken to be (0,100).
935 CString::size_type i = 0;
936 CString side, dstScreen, srcArgString, dstArgString;
937 CConfigReadContext::ArgList srcArgs, dstArgs;
938 s.parseNameWithArgs("link", line, "=", i, side, srcArgs);
939 ++i;
940 s.parseNameWithArgs("screen", line, "", i, dstScreen, dstArgs);
941 CInterval srcInterval(s.parseInterval(srcArgs));
942 CInterval dstInterval(s.parseInterval(dstArgs));
943
944 // handle argument
945 EDirection dir;
946 if (side == "left") {
947 dir = kLeft;
948 }
949 else if (side == "right") {
950 dir = kRight;
951 }
952 else if (side == "up") {
953 dir = kTop;
954 }
955 else if (side == "down") {
956 dir = kBottom;
957 }
958 else {
959 // unknown argument
960 throw XConfigRead(s, "unknown side \"%{1}\" in link", side);
961 }
962 if (!isScreen(dstScreen)) {
963 throw XConfigRead(s, "unknown screen name \"%{1}\"", dstScreen);
964 }
965 if (!connect(screen, dir,
966 srcInterval.first, srcInterval.second,
967 dstScreen,
968 dstInterval.first, dstInterval.second)) {
969 throw XConfigRead(s, "overlapping range");
970 }
971 }
972 }
973 throw XConfigRead(s, "unexpected end of links section");
974}
975
976void
977CConfig::readSectionAliases(CConfigReadContext& s)
978{
979 CString line;
980 CString screen;
981 while (s.readLine(line)) {
982 // check for end of section
983 if (line == "end") {
984 return;
985 }
986
987 // see if it's the next screen
988 if (line[line.size() - 1] == ':') {
989 // strip :
990 screen = line.substr(0, line.size() - 1);
991
992 // verify we know about the screen
993 if (!isScreen(screen)) {
994 throw XConfigRead(s, "unknown screen name \"%{1}\"", screen);
995 }
996 if (!isCanonicalName(screen)) {
997 throw XConfigRead(s, "cannot use screen name alias here");
998 }
999 }
1000 else if (screen.empty()) {
1001 throw XConfigRead(s, "argument before first screen");
1002 }
1003 else {
1004 // verify validity of screen name
1005 if (!isValidScreenName(line)) {
1006 throw XConfigRead(s, "invalid screen alias \"%{1}\"", line);
1007 }
1008
1009 // add alias
1010 if (!addAlias(screen, line)) {
1011 throw XConfigRead(s, "alias \"%{1}\" is already used", line);
1012 }
1013 }
1014 }
1015 throw XConfigRead(s, "unexpected end of aliases section");
1016}
1017
1018
1019CInputFilter::CCondition*
1020CConfig::parseCondition(CConfigReadContext& s,
1021 const CString& name, const std::vector<CString>& args)
1022{
1023 if (name == "keystroke") {
1024 if (args.size() != 1) {
1025 throw XConfigRead(s, "syntax for condition: keystroke(modifiers+key)");
1026 }
1027
1028 IPlatformScreen::CKeyInfo* keyInfo = s.parseKeystroke(args[0]);
1029
1030 return new CInputFilter::CKeystrokeCondition(keyInfo);
1031 }
1032
1033 if (name == "mousebutton") {
1034 if (args.size() != 1) {
1035 throw XConfigRead(s, "syntax for condition: mousebutton(modifiers+button)");
1036 }
1037
1038 IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]);
1039
1040 return new CInputFilter::CMouseButtonCondition(mouseInfo);
1041 }
1042
1043 if (name == "connect") {
1044 if (args.size() != 1) {
1045 throw XConfigRead(s, "syntax for condition: connect([screen])");
1046 }
1047
1048 CString screen = args[0];
1049 if (isScreen(screen)) {
1050 screen = getCanonicalName(screen);
1051 }
1052 else if (!screen.empty()) {
1053 throw XConfigRead(s, "unknown screen name \"%{1}\" in connect", screen);
1054 }
1055
1056 return new CInputFilter::CScreenConnectedCondition(screen);
1057 }
1058
1059 throw XConfigRead(s, "unknown argument \"%{1}\"", name);
1060}
1061
1062void
1063CConfig::parseAction(CConfigReadContext& s,
1064 const CString& name, const std::vector<CString>& args,
1065 CInputFilter::CRule& rule, bool activate)
1066{
1067 CInputFilter::CAction* action;
1068
1069 if (name == "keystroke" || name == "keyDown" || name == "keyUp") {
1070 if (args.size() < 1 || args.size() > 2) {
1071 throw XConfigRead(s, "syntax for action: keystroke(modifiers+key[,screens])");
1072 }
1073
1074 IPlatformScreen::CKeyInfo* keyInfo;
1075 if (args.size() == 1) {
1076 keyInfo = s.parseKeystroke(args[0]);
1077 }
1078 else {
1079 std::set<CString> screens;
1080 parseScreens(s, args[1], screens);
1081 keyInfo = s.parseKeystroke(args[0], screens);
1082 }
1083
1084 if (name == "keystroke") {
1085 IPlatformScreen::CKeyInfo* keyInfo2 =
1086 IKeyState::CKeyInfo::alloc(*keyInfo);
1087 action = new CInputFilter::CKeystrokeAction(keyInfo2, true);
1088 rule.adoptAction(action, true);
1089 action = new CInputFilter::CKeystrokeAction(keyInfo, false);
1090 activate = false;
1091 }
1092 else if (name == "keyDown") {
1093 action = new CInputFilter::CKeystrokeAction(keyInfo, true);
1094 }
1095 else {
1096 action = new CInputFilter::CKeystrokeAction(keyInfo, false);
1097 }
1098 }
1099
1100 else if (name == "mousebutton" ||
1101 name == "mouseDown" || name == "mouseUp") {
1102 if (args.size() != 1) {
1103 throw XConfigRead(s, "syntax for action: mousebutton(modifiers+button)");
1104 }
1105
1106 IPlatformScreen::CButtonInfo* mouseInfo = s.parseMouse(args[0]);
1107
1108 if (name == "mousebutton") {
1109 IPlatformScreen::CButtonInfo* mouseInfo2 =
1110 IPlatformScreen::CButtonInfo::alloc(*mouseInfo);
1111 action = new CInputFilter::CMouseButtonAction(mouseInfo2, true);
1112 rule.adoptAction(action, true);
1113 action = new CInputFilter::CMouseButtonAction(mouseInfo, false);
1114 activate = false;
1115 }
1116 else if (name == "mouseDown") {
1117 action = new CInputFilter::CMouseButtonAction(mouseInfo, true);
1118 }
1119 else {
1120 action = new CInputFilter::CMouseButtonAction(mouseInfo, false);
1121 }
1122 }
1123
1124/* XXX -- not supported
1125 else if (name == "modifier") {
1126 if (args.size() != 1) {
1127 throw XConfigRead(s, "syntax for action: modifier(modifiers)");
1128 }
1129
1130 KeyModifierMask mask = s.parseModifier(args[0]);
1131
1132 action = new CInputFilter::CModifierAction(mask, ~mask);
1133 }
1134*/
1135
1136 else if (name == "switchToScreen") {
1137 if (args.size() != 1) {
1138 throw XConfigRead(s, "syntax for action: switchToScreen(name)");
1139 }
1140
1141 CString screen = args[0];
1142 if (isScreen(screen)) {
1143 screen = getCanonicalName(screen);
1144 }
1145 else if (!screen.empty()) {
1146 throw XConfigRead(s, "unknown screen name in switchToScreen");
1147 }
1148
1149 action = new CInputFilter::CSwitchToScreenAction(screen);
1150 }
1151
1152 else if (name == "switchInDirection") {
1153 if (args.size() != 1) {
1154 throw XConfigRead(s, "syntax for action: switchInDirection(<left|right|up|down>)");
1155 }
1156
1157 EDirection direction;
1158 if (args[0] == "left") {
1159 direction = kLeft;
1160 }
1161 else if (args[0] == "right") {
1162 direction = kRight;
1163 }
1164 else if (args[0] == "up") {
1165 direction = kTop;
1166 }
1167 else if (args[0] == "down") {
1168 direction = kBottom;
1169 }
1170 else {
1171 throw XConfigRead(s, "unknown direction \"%{1}\" in switchToScreen", args[0]);
1172 }
1173
1174 action = new CInputFilter::CSwitchInDirectionAction(direction);
1175 }
1176
1177 else if (name == "lockCursorToScreen") {
1178 if (args.size() > 1) {
1179 throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
1180 }
1181
1182 CInputFilter::CLockCursorToScreenAction::Mode mode =
1183 CInputFilter::CLockCursorToScreenAction::kToggle;
1184 if (args.size() == 1) {
1185 if (args[0] == "off") {
1186 mode = CInputFilter::CLockCursorToScreenAction::kOff;
1187 }
1188 else if (args[0] == "on") {
1189 mode = CInputFilter::CLockCursorToScreenAction::kOn;
1190 }
1191 else if (args[0] == "toggle") {
1192 mode = CInputFilter::CLockCursorToScreenAction::kToggle;
1193 }
1194 else {
1195 throw XConfigRead(s, "syntax for action: lockCursorToScreen([{off|on|toggle}])");
1196 }
1197 }
1198
1199 if (mode != CInputFilter::CLockCursorToScreenAction::kOff) {
1200 m_hasLockToScreenAction = true;
1201 }
1202
1203 action = new CInputFilter::CLockCursorToScreenAction(mode);
1204 }
1205
1206 else {
1207 throw XConfigRead(s, "unknown action argument \"%{1}\"", name);
1208 }
1209
1210 rule.adoptAction(action, activate);
1211}
1212
1213void
1214CConfig::parseScreens(CConfigReadContext& c,
1215 const CString& s, std::set<CString>& screens) const
1216{
1217 screens.clear();
1218
1219 CString::size_type i = 0;
1220 while (i < s.size()) {
1221 // find end of next screen name
1222 CString::size_type j = s.find(':', i);
1223 if (j == CString::npos) {
1224 j = s.size();
1225 }
1226
1227 // extract name
1228 CString rawName;
1229 i = s.find_first_not_of(" \t", i);
1230 if (i < j) {
1231 rawName = s.substr(i, s.find_last_not_of(" \t", j - 1) - i + 1);
1232 }
1233
1234 // add name
1235 if (rawName == "*") {
1236 screens.insert("*");
1237 }
1238 else if (!rawName.empty()) {
1239 CString name = getCanonicalName(rawName);
1240 if (name.empty()) {
1241 throw XConfigRead(c, "unknown screen name \"%{1}\"", rawName);
1242 }
1243 screens.insert(name);
1244 }
1245
1246 // next
1247 i = j + 1;
1248 }
1249}
1250
1251const char*
1252CConfig::getOptionName(OptionID id)
1253{
1254 if (id == kOptionHalfDuplexCapsLock) {
1255 return "halfDuplexCapsLock";
1256 }
1257 if (id == kOptionHalfDuplexNumLock) {
1258 return "halfDuplexNumLock";
1259 }
1260 if (id == kOptionHalfDuplexScrollLock) {
1261 return "halfDuplexScrollLock";
1262 }
1263 if (id == kOptionModifierMapForShift) {
1264 return "shift";
1265 }
1266 if (id == kOptionModifierMapForControl) {
1267 return "ctrl";
1268 }
1269 if (id == kOptionModifierMapForAlt) {
1270 return "alt";
1271 }
1272 if (id == kOptionModifierMapForMeta) {
1273 return "meta";
1274 }
1275 if (id == kOptionModifierMapForSuper) {
1276 return "super";
1277 }
1278 if (id == kOptionHeartbeat) {
1279 return "heartbeat";
1280 }
1281 if (id == kOptionScreenSwitchCorners) {
1282 return "switchCorners";
1283 }
1284 if (id == kOptionScreenSwitchCornerSize) {
1285 return "switchCornerSize";
1286 }
1287 if (id == kOptionScreenSwitchDelay) {
1288 return "switchDelay";
1289 }
1290 if (id == kOptionScreenSwitchTwoTap) {
1291 return "switchDoubleTap";
1292 }
1293 if (id == kOptionScreenSaverSync) {
1294 return "screenSaverSync";
1295 }
1296 if (id == kOptionXTestXineramaUnaware) {
1297 return "xtestIsXineramaUnaware";
1298 }
1299 if (id == kOptionRelativeMouseMoves) {
1300 return "relativeMouseMoves";
1301 }
1302 if (id == kOptionWin32KeepForeground) {
1303 return "win32KeepForeground";
1304 }
1305 return NULL;
1306}
1307
1308CString
1309CConfig::getOptionValue(OptionID id, OptionValue value)
1310{
1311 if (id == kOptionHalfDuplexCapsLock ||
1312 id == kOptionHalfDuplexNumLock ||
1313 id == kOptionHalfDuplexScrollLock ||
1314 id == kOptionScreenSaverSync ||
1315 id == kOptionXTestXineramaUnaware ||
1316 id == kOptionRelativeMouseMoves ||
1317 id == kOptionWin32KeepForeground) {
1318 return (value != 0) ? "true" : "false";
1319 }
1320 if (id == kOptionModifierMapForShift ||
1321 id == kOptionModifierMapForControl ||
1322 id == kOptionModifierMapForAlt ||
1323 id == kOptionModifierMapForMeta ||
1324 id == kOptionModifierMapForSuper) {
1325 switch (value) {
1326 case kKeyModifierIDShift:
1327 return "shift";
1328
1329 case kKeyModifierIDControl:
1330 return "ctrl";
1331
1332 case kKeyModifierIDAlt:
1333 return "alt";
1334
1335 case kKeyModifierIDMeta:
1336 return "meta";
1337
1338 case kKeyModifierIDSuper:
1339 return "super";
1340
1341 default:
1342 return "none";
1343 }
1344 }
1345 if (id == kOptionHeartbeat ||
1346 id == kOptionScreenSwitchCornerSize ||
1347 id == kOptionScreenSwitchDelay ||
1348 id == kOptionScreenSwitchTwoTap) {
1349 return CStringUtil::print("%d", value);
1350 }
1351 if (id == kOptionScreenSwitchCorners) {
1352 std::string result("none");
1353 if ((value & kTopLeftMask) != 0) {
1354 result += " +top-left";
1355 }
1356 if ((value & kTopRightMask) != 0) {
1357 result += " +top-right";
1358 }
1359 if ((value & kBottomLeftMask) != 0) {
1360 result += " +bottom-left";
1361 }
1362 if ((value & kBottomRightMask) != 0) {
1363 result += " +bottom-right";
1364 }
1365 return result;
1366 }
1367
1368 return "";
1369}
1370
1371
1372//
1373// CConfig::CName
1374//
1375
1376CConfig::CName::CName(CConfig* config, const CString& name) :
1377 m_config(config),
1378 m_name(config->getCanonicalName(name))
1379{
1380 // do nothing
1381}
1382
1383bool
1384CConfig::CName::operator==(const CString& name) const
1385{
1386 CString canonical = m_config->getCanonicalName(name);
1387 return CStringUtil::CaselessCmp::equal(canonical, m_name);
1388}
1389
1390
1391//
1392// CConfig::CCellEdge
1393//
1394
1395CConfig::CCellEdge::CCellEdge(EDirection side, float position)
1396{
1397 init("", side, CInterval(position, position));
1398}
1399
1400CConfig::CCellEdge::CCellEdge(EDirection side, const CInterval& interval)
1401{
1402 assert(interval.first >= 0.0f);
1403 assert(interval.second <= 1.0f);
1404 assert(interval.first < interval.second);
1405
1406 init("", side, interval);
1407}
1408
1409CConfig::CCellEdge::CCellEdge(const CString& name,
1410 EDirection side, const CInterval& interval)
1411{
1412 assert(interval.first >= 0.0f);
1413 assert(interval.second <= 1.0f);
1414 assert(interval.first < interval.second);
1415
1416 init(name, side, interval);
1417}
1418
1419CConfig::CCellEdge::~CCellEdge()
1420{
1421 // do nothing
1422}
1423
1424void
1425CConfig::CCellEdge::init(const CString& name, EDirection side,
1426 const CInterval& interval)
1427{
1428 assert(side != kNoDirection);
1429
1430 m_name = name;
1431 m_side = side;
1432 m_interval = interval;
1433}
1434
1435CConfig::CInterval
1436CConfig::CCellEdge::getInterval() const
1437{
1438 return m_interval;
1439}
1440
1441void
1442CConfig::CCellEdge::setName(const CString& newName)
1443{
1444 m_name = newName;
1445}
1446
1447CString
1448CConfig::CCellEdge::getName() const
1449{
1450 return m_name;
1451}
1452
1453EDirection
1454CConfig::CCellEdge::getSide() const
1455{
1456 return m_side;
1457}
1458
1459bool
1460CConfig::CCellEdge::overlaps(const CCellEdge& edge) const
1461{
1462 const CInterval& x = m_interval;
1463 const CInterval& y = edge.m_interval;
1464 if (m_side != edge.m_side) {
1465 return false;
1466 }
1467 return (x.first >= y.first && x.first < y.second) ||
1468 (x.second > y.first && x.second <= y.second) ||
1469 (y.first >= x.first && y.first < x.second) ||
1470 (y.second > x.first && y.second <= x.second);
1471}
1472
1473bool
1474CConfig::CCellEdge::isInside(float x) const
1475{
1476 return (x >= m_interval.first && x < m_interval.second);
1477}
1478
1479float
1480CConfig::CCellEdge::transform(float x) const
1481{
1482 return (x - m_interval.first) / (m_interval.second - m_interval.first);
1483}
1484
1485
1486float
1487CConfig::CCellEdge::inverseTransform(float x) const
1488{
1489 return x * (m_interval.second - m_interval.first) + m_interval.first;
1490}
1491
1492bool
1493CConfig::CCellEdge::operator<(const CCellEdge& o) const
1494{
1495 if (static_cast<int>(m_side) < static_cast<int>(o.m_side)) {
1496 return true;
1497 }
1498 else if (static_cast<int>(m_side) > static_cast<int>(o.m_side)) {
1499 return false;
1500 }
1501
1502 return (m_interval.first < o.m_interval.first);
1503}
1504
1505bool
1506CConfig::CCellEdge::operator==(const CCellEdge& x) const
1507{
1508 return (m_side == x.m_side && m_interval == x.m_interval);
1509}
1510
1511bool
1512CConfig::CCellEdge::operator!=(const CCellEdge& x) const
1513{
1514 return !operator==(x);
1515}
1516
1517
1518//
1519// CConfig::CCell
1520//
1521
1522bool
1523CConfig::CCell::add(const CCellEdge& src, const CCellEdge& dst)
1524{
1525 // cannot add an edge that overlaps other existing edges but we
1526 // can exactly replace an edge.
1527 if (!hasEdge(src) && overlaps(src)) {
1528 return false;
1529 }
1530
1531 m_neighbors.erase(src);
1532 m_neighbors.insert(std::make_pair(src, dst));
1533 return true;
1534}
1535
1536void
1537CConfig::CCell::remove(EDirection side)
1538{
1539 for (CEdgeLinks::iterator j = m_neighbors.begin();
1540 j != m_neighbors.end(); ) {
1541 if (j->first.getSide() == side) {
1542 m_neighbors.erase(j++);
1543 }
1544 else {
1545 ++j;
1546 }
1547 }
1548}
1549
1550void
1551CConfig::CCell::remove(EDirection side, float position)
1552{
1553 for (CEdgeLinks::iterator j = m_neighbors.begin();
1554 j != m_neighbors.end(); ++j) {
1555 if (j->first.getSide() == side && j->first.isInside(position)) {
1556 m_neighbors.erase(j);
1557 break;
1558 }
1559 }
1560}
1561void
1562CConfig::CCell::remove(const CName& name)
1563{
1564 for (CEdgeLinks::iterator j = m_neighbors.begin();
1565 j != m_neighbors.end(); ) {
1566 if (name == j->second.getName()) {
1567 m_neighbors.erase(j++);
1568 }
1569 else {
1570 ++j;
1571 }
1572 }
1573}
1574
1575void
1576CConfig::CCell::rename(const CName& oldName, const CString& newName)
1577{
1578 for (CEdgeLinks::iterator j = m_neighbors.begin();
1579 j != m_neighbors.end(); ++j) {
1580 if (oldName == j->second.getName()) {
1581 j->second.setName(newName);
1582 }
1583 }
1584}
1585
1586bool
1587CConfig::CCell::hasEdge(const CCellEdge& edge) const
1588{
1589 CEdgeLinks::const_iterator i = m_neighbors.find(edge);
1590 return (i != m_neighbors.end() && i->first == edge);
1591}
1592
1593bool
1594CConfig::CCell::overlaps(const CCellEdge& edge) const
1595{
1596 CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
1597 if (i != m_neighbors.end() && i->first.overlaps(edge)) {
1598 return true;
1599 }
1600 if (i != m_neighbors.begin() && (--i)->first.overlaps(edge)) {
1601 return true;
1602 }
1603 return false;
1604}
1605
1606bool
1607CConfig::CCell::getLink(EDirection side, float position,
1608 const CCellEdge*& src, const CCellEdge*& dst) const
1609{
1610 CCellEdge edge(side, position);
1611 CEdgeLinks::const_iterator i = m_neighbors.upper_bound(edge);
1612 if (i == m_neighbors.begin()) {
1613 return false;
1614 }
1615 --i;
1616 if (i->first.getSide() == side && i->first.isInside(position)) {
1617 src = &i->first;
1618 dst = &i->second;
1619 return true;
1620 }
1621 return false;
1622}
1623
1624bool
1625CConfig::CCell::operator==(const CCell& x) const
1626{
1627 // compare options
1628 if (m_options != x.m_options) {
1629 return false;
1630 }
1631
1632 // compare links
1633 if (m_neighbors.size() != x.m_neighbors.size()) {
1634 return false;
1635 }
1636 for (CEdgeLinks::const_iterator index1 = m_neighbors.begin(),
1637 index2 = x.m_neighbors.begin();
1638 index1 != m_neighbors.end();
1639 ++index1, ++index2) {
1640 if (index1->first != index2->first) {
1641 return false;
1642 }
1643 if (index1->second != index2->second) {
1644 return false;
1645 }
1646
1647 // operator== doesn't compare names. only compare destination
1648 // names.
1649 if (!CStringUtil::CaselessCmp::equal(index1->second.getName(),
1650 index2->second.getName())) {
1651 return false;
1652 }
1653 }
1654 return true;
1655}
1656
1657bool
1658CConfig::CCell::operator!=(const CCell& x) const
1659{
1660 return !operator==(x);
1661}
1662
1663CConfig::CCell::const_iterator
1664CConfig::CCell::begin() const
1665{
1666 return m_neighbors.begin();
1667}
1668
1669CConfig::CCell::const_iterator
1670CConfig::CCell::end() const
1671{
1672 return m_neighbors.end();
1673}
1674
1675
1676//
1677// CConfig I/O
1678//
1679
1680std::istream&
1681operator>>(std::istream& s, CConfig& config)
1682{
1683 CConfigReadContext context(s);
1684 config.read(context);
1685 return s;
1686}
1687
1688std::ostream&
1689operator<<(std::ostream& s, const CConfig& config)
1690{
1691 // screens section
1692 s << "section: screens" << std::endl;
1693 for (CConfig::const_iterator screen = config.begin();
1694 screen != config.end(); ++screen) {
1695 s << "\t" << screen->c_str() << ":" << std::endl;
1696 const CConfig::CScreenOptions* options = config.getOptions(*screen);
1697 if (options != NULL && options->size() > 0) {
1698 for (CConfig::CScreenOptions::const_iterator
1699 option = options->begin();
1700 option != options->end(); ++option) {
1701 const char* name = CConfig::getOptionName(option->first);
1702 CString value = CConfig::getOptionValue(option->first,
1703 option->second);
1704 if (name != NULL && !value.empty()) {
1705 s << "\t\t" << name << " = " << value << std::endl;
1706 }
1707 }
1708 }
1709 }
1710 s << "end" << std::endl;
1711
1712 // links section
1713 CString neighbor;
1714 s << "section: links" << std::endl;
1715 for (CConfig::const_iterator screen = config.begin();
1716 screen != config.end(); ++screen) {
1717 s << "\t" << screen->c_str() << ":" << std::endl;
1718
1719 for (CConfig::link_const_iterator
1720 link = config.beginNeighbor(*screen),
1721 nend = config.endNeighbor(*screen); link != nend; ++link) {
1722 s << "\t\t" << CConfig::dirName(link->first.getSide()) <<
1723 CConfig::formatInterval(link->first.getInterval()) <<
1724 " = " << link->second.getName().c_str() <<
1725 CConfig::formatInterval(link->second.getInterval()) <<
1726 std::endl;
1727 }
1728 }
1729 s << "end" << std::endl;
1730
1731 // aliases section (if there are any)
1732 if (config.m_map.size() != config.m_nameToCanonicalName.size()) {
1733 // map canonical to alias
1734 typedef std::multimap<CString, CString,
1735 CStringUtil::CaselessCmp> CMNameMap;
1736 CMNameMap aliases;
1737 for (CConfig::CNameMap::const_iterator
1738 index = config.m_nameToCanonicalName.begin();
1739 index != config.m_nameToCanonicalName.end();
1740 ++index) {
1741 if (index->first != index->second) {
1742 aliases.insert(std::make_pair(index->second, index->first));
1743 }
1744 }
1745
1746 // dump it
1747 CString screen;
1748 s << "section: aliases" << std::endl;
1749 for (CMNameMap::const_iterator index = aliases.begin();
1750 index != aliases.end(); ++index) {
1751 if (index->first != screen) {
1752 screen = index->first;
1753 s << "\t" << screen.c_str() << ":" << std::endl;
1754 }
1755 s << "\t\t" << index->second.c_str() << std::endl;
1756 }
1757 s << "end" << std::endl;
1758 }
1759
1760 // options section
1761 s << "section: options" << std::endl;
1762 const CConfig::CScreenOptions* options = config.getOptions("");
1763 if (options != NULL && options->size() > 0) {
1764 for (CConfig::CScreenOptions::const_iterator
1765 option = options->begin();
1766 option != options->end(); ++option) {
1767 const char* name = CConfig::getOptionName(option->first);
1768 CString value = CConfig::getOptionValue(option->first,
1769 option->second);
1770 if (name != NULL && !value.empty()) {
1771 s << "\t" << name << " = " << value << std::endl;
1772 }
1773 }
1774 }
1775 if (config.m_synergyAddress.isValid()) {
1776 s << "\taddress = " <<
1777 config.m_synergyAddress.getHostname().c_str() << std::endl;
1778 }
1779 s << config.m_inputFilter.format("\t");
1780 s << "end" << std::endl;
1781
1782 return s;
1783}
1784
1785
1786//
1787// CConfigReadContext
1788//
1789
1790CConfigReadContext::CConfigReadContext(std::istream& s, SInt32 firstLine) :
1791 m_stream(s),
1792 m_line(firstLine - 1)
1793{
1794 // do nothing
1795}
1796
1797CConfigReadContext::~CConfigReadContext()
1798{
1799 // do nothing
1800}
1801
1802bool
1803CConfigReadContext::readLine(CString& line)
1804{
1805 ++m_line;
1806 while (std::getline(m_stream, line)) {
1807 // strip leading whitespace
1808 CString::size_type i = line.find_first_not_of(" \t");
1809 if (i != CString::npos) {
1810 line.erase(0, i);
1811 }
1812
1813 // strip comments and then trailing whitespace
1814 i = line.find('#');
1815 if (i != CString::npos) {
1816 line.erase(i);
1817 }
1818 i = line.find_last_not_of(" \r\t");
1819 if (i != CString::npos) {
1820 line.erase(i + 1);
1821 }
1822
1823 // return non empty line
1824 if (!line.empty()) {
1825 // make sure there are no invalid characters
1826 for (i = 0; i < line.length(); ++i) {
1827 if (!isgraph(line[i]) && line[i] != ' ' && line[i] != '\t') {
1828 throw XConfigRead(*this,
1829 "invalid character %{1}",
1830 CStringUtil::print("%#2x", line[i]));
1831 }
1832 }
1833
1834 return true;
1835 }
1836
1837 // next line
1838 ++m_line;
1839 }
1840 return false;
1841}
1842
1843UInt32
1844CConfigReadContext::getLineNumber() const
1845{
1846 return m_line;
1847}
1848
1849CConfigReadContext::operator void*() const
1850{
1851 return m_stream;
1852}
1853
1854bool
1855CConfigReadContext::operator!() const
1856{
1857 return !m_stream;
1858}
1859
1860OptionValue
1861CConfigReadContext::parseBoolean(const CString& arg) const
1862{
1863 if (CStringUtil::CaselessCmp::equal(arg, "true")) {
1864 return static_cast<OptionValue>(true);
1865 }
1866 if (CStringUtil::CaselessCmp::equal(arg, "false")) {
1867 return static_cast<OptionValue>(false);
1868 }
1869 throw XConfigRead(*this, "invalid boolean argument \"%{1}\"", arg);
1870}
1871
1872OptionValue
1873CConfigReadContext::parseInt(const CString& arg) const
1874{
1875 const char* s = arg.c_str();
1876 char* end;
1877 long tmp = strtol(s, &end, 10);
1878 if (*end != '\0') {
1879 // invalid characters
1880 throw XConfigRead(*this, "invalid integer argument \"%{1}\"", arg);
1881 }
1882 OptionValue value = static_cast<OptionValue>(tmp);
1883 if (value != tmp) {
1884 // out of range
1885 throw XConfigRead(*this, "integer argument \"%{1}\" out of range", arg);
1886 }
1887 return value;
1888}
1889
1890OptionValue
1891CConfigReadContext::parseModifierKey(const CString& arg) const
1892{
1893 if (CStringUtil::CaselessCmp::equal(arg, "shift")) {
1894 return static_cast<OptionValue>(kKeyModifierIDShift);
1895 }
1896 if (CStringUtil::CaselessCmp::equal(arg, "ctrl")) {
1897 return static_cast<OptionValue>(kKeyModifierIDControl);
1898 }
1899 if (CStringUtil::CaselessCmp::equal(arg, "alt")) {
1900 return static_cast<OptionValue>(kKeyModifierIDAlt);
1901 }
1902 if (CStringUtil::CaselessCmp::equal(arg, "meta")) {
1903 return static_cast<OptionValue>(kKeyModifierIDMeta);
1904 }
1905 if (CStringUtil::CaselessCmp::equal(arg, "super")) {
1906 return static_cast<OptionValue>(kKeyModifierIDSuper);
1907 }
1908 if (CStringUtil::CaselessCmp::equal(arg, "none")) {
1909 return static_cast<OptionValue>(kKeyModifierIDNull);
1910 }
1911 throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
1912}
1913
1914OptionValue
1915CConfigReadContext::parseCorner(const CString& arg) const
1916{
1917 if (CStringUtil::CaselessCmp::equal(arg, "left")) {
1918 return kTopLeftMask | kBottomLeftMask;
1919 }
1920 else if (CStringUtil::CaselessCmp::equal(arg, "right")) {
1921 return kTopRightMask | kBottomRightMask;
1922 }
1923 else if (CStringUtil::CaselessCmp::equal(arg, "top")) {
1924 return kTopLeftMask | kTopRightMask;
1925 }
1926 else if (CStringUtil::CaselessCmp::equal(arg, "bottom")) {
1927 return kBottomLeftMask | kBottomRightMask;
1928 }
1929 else if (CStringUtil::CaselessCmp::equal(arg, "top-left")) {
1930 return kTopLeftMask;
1931 }
1932 else if (CStringUtil::CaselessCmp::equal(arg, "top-right")) {
1933 return kTopRightMask;
1934 }
1935 else if (CStringUtil::CaselessCmp::equal(arg, "bottom-left")) {
1936 return kBottomLeftMask;
1937 }
1938 else if (CStringUtil::CaselessCmp::equal(arg, "bottom-right")) {
1939 return kBottomRightMask;
1940 }
1941 else if (CStringUtil::CaselessCmp::equal(arg, "none")) {
1942 return kNoCornerMask;
1943 }
1944 else if (CStringUtil::CaselessCmp::equal(arg, "all")) {
1945 return kAllCornersMask;
1946 }
1947 throw XConfigRead(*this, "invalid argument \"%{1}\"", arg);
1948}
1949
1950OptionValue
1951CConfigReadContext::parseCorners(const CString& args) const
1952{
1953 // find first token
1954 CString::size_type i = args.find_first_not_of(" \t", 0);
1955 if (i == CString::npos) {
1956 throw XConfigRead(*this, "missing corner argument");
1957 }
1958 CString::size_type j = args.find_first_of(" \t", i);
1959
1960 // parse first corner token
1961 OptionValue corners = parseCorner(args.substr(i, j - i));
1962
1963 // get +/-
1964 i = args.find_first_not_of(" \t", j);
1965 while (i != CString::npos) {
1966 // parse +/-
1967 bool add;
1968 if (args[i] == '-') {
1969 add = false;
1970 }
1971 else if (args[i] == '+') {
1972 add = true;
1973 }
1974 else {
1975 throw XConfigRead(*this,
1976 "invalid corner operator \"%{1}\"",
1977 CString(args.c_str() + i, 1));
1978 }
1979
1980 // get next corner token
1981 i = args.find_first_not_of(" \t", i + 1);
1982 j = args.find_first_of(" \t", i);
1983 if (i == CString::npos) {
1984 throw XConfigRead(*this, "missing corner argument");
1985 }
1986
1987 // parse next corner token
1988 if (add) {
1989 corners |= parseCorner(args.substr(i, j - i));
1990 }
1991 else {
1992 corners &= ~parseCorner(args.substr(i, j - i));
1993 }
1994 i = args.find_first_not_of(" \t", j);
1995 }
1996
1997 return corners;
1998}
1999
2000CConfig::CInterval
2001CConfigReadContext::parseInterval(const ArgList& args) const
2002{
2003 if (args.size() == 0) {
2004 return CConfig::CInterval(0.0f, 1.0f);
2005 }
2006 if (args.size() != 2 || args[0].empty() || args[1].empty()) {
2007 throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
2008 }
2009
2010 char* end;
2011 long startValue = strtol(args[0].c_str(), &end, 10);
2012 if (end[0] != '\0') {
2013 throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
2014 }
2015 long endValue = strtol(args[1].c_str(), &end, 10);
2016 if (end[0] != '\0') {
2017 throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
2018 }
2019
2020 if (startValue < 0 || startValue > 100 ||
2021 endValue < 0 || endValue > 100 ||
2022 startValue >= endValue) {
2023 throw XConfigRead(*this, "invalid interval \"%{1}\"", concatArgs(args));
2024 }
2025
2026 return CConfig::CInterval(startValue / 100.0f, endValue / 100.0f);
2027}
2028
2029void
2030CConfigReadContext::parseNameWithArgs(
2031 const CString& type, const CString& line,
2032 const CString& delim, CString::size_type& index,
2033 CString& name, ArgList& args) const
2034{
2035 // skip leading whitespace
2036 CString::size_type i = line.find_first_not_of(" \t", index);
2037 if (i == CString::npos) {
2038 throw XConfigRead(*this, CString("missing ") + type);
2039 }
2040
2041 // find end of name
2042 CString::size_type j = line.find_first_of(" \t(" + delim, i);
2043 if (j == CString::npos) {
2044 j = line.length();
2045 }
2046
2047 // save name
2048 name = line.substr(i, j - i);
2049 args.clear();
2050
2051 // is it okay to not find a delimiter?
2052 bool needDelim = (!delim.empty() && delim.find('\n') == CString::npos);
2053
2054 // skip whitespace
2055 i = line.find_first_not_of(" \t", j);
2056 if (i == CString::npos && needDelim) {
2057 // expected delimiter but didn't find it
2058 throw XConfigRead(*this, CString("missing ") + delim[0]);
2059 }
2060 if (i == CString::npos) {
2061 // no arguments
2062 index = line.length();
2063 return;
2064 }
2065 if (line[i] != '(') {
2066 // no arguments
2067 index = i;
2068 return;
2069 }
2070
2071 // eat '('
2072 ++i;
2073
2074 // parse arguments
2075 j = line.find_first_of(",)", i);
2076 while (j != CString::npos) {
2077 // extract arg
2078 CString arg(line.substr(i, j - i));
2079 i = j;
2080
2081 // trim whitespace
2082 j = arg.find_first_not_of(" \t");
2083 if (j != CString::npos) {
2084 arg.erase(0, j);
2085 }
2086 j = arg.find_last_not_of(" \t");
2087 if (j != CString::npos) {
2088 arg.erase(j + 1);
2089 }
2090
2091 // save arg
2092 args.push_back(arg);
2093
2094 // exit loop at end of arguments
2095 if (line[i] == ')') {
2096 break;
2097 }
2098
2099 // eat ','
2100 ++i;
2101
2102 // next
2103 j = line.find_first_of(",)", i);
2104 }
2105
2106 // verify ')'
2107 if (j == CString::npos) {
2108 // expected )
2109 throw XConfigRead(*this, "missing )");
2110 }
2111
2112 // eat ')'
2113 ++i;
2114
2115 // skip whitespace
2116 j = line.find_first_not_of(" \t", i);
2117 if (j == CString::npos && needDelim) {
2118 // expected delimiter but didn't find it
2119 throw XConfigRead(*this, CString("missing ") + delim[0]);
2120 }
2121
2122 // verify delimiter
2123 if (needDelim && delim.find(line[j]) == CString::npos) {
2124 throw XConfigRead(*this, CString("expected ") + delim[0]);
2125 }
2126
2127 if (j == CString::npos) {
2128 j = line.length();
2129 }
2130
2131 index = j;
2132 return;
2133}
2134
2135IPlatformScreen::CKeyInfo*
2136CConfigReadContext::parseKeystroke(const CString& keystroke) const
2137{
2138 return parseKeystroke(keystroke, std::set<CString>());
2139}
2140
2141IPlatformScreen::CKeyInfo*
2142CConfigReadContext::parseKeystroke(const CString& keystroke,
2143 const std::set<CString>& screens) const
2144{
2145 CString s = keystroke;
2146
2147 KeyModifierMask mask;
2148 if (!CKeyMap::parseModifiers(s, mask)) {
2149 throw XConfigRead(*this, "unable to parse key modifiers");
2150 }
2151
2152 KeyID key;
2153 if (!CKeyMap::parseKey(s, key)) {
2154 throw XConfigRead(*this, "unable to parse key");
2155 }
2156
2157 if (key == kKeyNone && mask == 0) {
2158 throw XConfigRead(*this, "missing key and/or modifiers in keystroke");
2159 }
2160
2161 return IPlatformScreen::CKeyInfo::alloc(key, mask, 0, 0, screens);
2162}
2163
2164IPlatformScreen::CButtonInfo*
2165CConfigReadContext::parseMouse(const CString& mouse) const
2166{
2167 CString s = mouse;
2168
2169 KeyModifierMask mask;
2170 if (!CKeyMap::parseModifiers(s, mask)) {
2171 throw XConfigRead(*this, "unable to parse button modifiers");
2172 }
2173
2174 char* end;
2175 ButtonID button = (ButtonID)strtol(s.c_str(), &end, 10);
2176 if (*end != '\0') {
2177 throw XConfigRead(*this, "unable to parse button");
2178 }
2179 if (s.empty() || button <= 0) {
2180 throw XConfigRead(*this, "invalid button");
2181 }
2182
2183 return IPlatformScreen::CButtonInfo::alloc(button, mask);
2184}
2185
2186KeyModifierMask
2187CConfigReadContext::parseModifier(const CString& modifiers) const
2188{
2189 CString s = modifiers;
2190
2191 KeyModifierMask mask;
2192 if (!CKeyMap::parseModifiers(s, mask)) {
2193 throw XConfigRead(*this, "unable to parse modifiers");
2194 }
2195
2196 if (mask == 0) {
2197 throw XConfigRead(*this, "no modifiers specified");
2198 }
2199
2200 return mask;
2201}
2202
2203CString
2204CConfigReadContext::concatArgs(const ArgList& args)
2205{
2206 CString s("(");
2207 for (size_t i = 0; i < args.size(); ++i) {
2208 if (i != 0) {
2209 s += ",";
2210 }
2211 s += args[i];
2212 }
2213 s += ")";
2214 return s;
2215}
2216
2217
2218//
2219// CConfig I/O exceptions
2220//
2221
2222XConfigRead::XConfigRead(const CConfigReadContext& context,
2223 const CString& error) :
2224 m_error(CStringUtil::print("line %d: %s",
2225 context.getLineNumber(), error.c_str()))
2226{
2227 // do nothing
2228}
2229
2230XConfigRead::XConfigRead(const CConfigReadContext& context,
2231 const char* errorFmt, const CString& arg) :
2232 m_error(CStringUtil::print("line %d: ", context.getLineNumber()) +
2233 CStringUtil::format(errorFmt, arg.c_str()))
2234{
2235 // do nothing
2236}
2237
2238XConfigRead::~XConfigRead()
2239{
2240 // do nothing
2241}
2242
2243CString
2244XConfigRead::getWhat() const throw()
2245{
2246 return format("XConfigRead", "read error: %{1}", m_error.c_str());
2247}
Note: See TracBrowser for help on using the repository browser.