add more spacing
[personal-kdebase.git] / workspace / libs / kephal / configurations / xml / xmlconfigurations.cpp
blobf5e8386def514a6573444d52729cf60aeb76a338
1 /*
2 * Copyright 2008 Aike J Sommer <dev@aikesommer.name>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2,
7 * or (at your option) any later version.
9 * This program 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
14 * You should have received a copy of the GNU Library General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "xmlconfigurations.h"
23 #include "xml/configurations_xml.h"
24 #include "outputs/backendoutputs.h"
25 #include "kephal/screens.h"
27 #include <QDebug>
28 #include <QDir>
31 namespace Kephal {
33 XMLConfiguration::XMLConfiguration(XMLConfigurations * parent, ConfigurationXML * config)
34 : BackendConfiguration(parent),
35 m_configuration(config)
39 ConfigurationXML * XMLConfiguration::configuration()
41 return m_configuration;
44 QString XMLConfiguration::name()
46 return m_configuration->name();
49 bool XMLConfiguration::isModifiable()
51 return m_configuration->modifiable();
54 bool XMLConfiguration::isActivated()
56 return this == m_parent->activeConfiguration();
59 void XMLConfiguration::activate()
61 emit activate(this);
64 void XMLConfiguration::setLayout(const QMap<int, QPoint> & layout) {
65 m_layout = layout;
68 int XMLConfiguration::primaryScreen() {
69 return m_configuration->primaryScreen();
72 QMap<int, QPoint> XMLConfiguration::layout() {
73 if (! m_layout.empty()) {
74 return m_layout;
77 QMap<int, ScreenXML *> remaining;
78 foreach (ScreenXML * screen, m_configuration->screens()) {
79 remaining.insert(screen->id(), screen);
82 QMap<int, QPoint> layout;
83 bool changed;
84 do {
85 changed = false;
86 QSet<ScreenXML *> added;
87 foreach (ScreenXML * screen, remaining) {
88 QPoint pos;
89 bool found = false;
90 if (layout.empty()) {
91 pos = QPoint(0, 0);
92 found = true;
93 } else if (layout.contains(screen->rightOf())) {
94 pos = QPoint(layout[screen->rightOf()]);
95 pos.rx()++;
96 found = true;
97 } else if (layout.contains(screen->bottomOf())) {
98 pos = QPoint(layout[screen->bottomOf()]);
99 pos.ry()++;
100 found = true;
103 if (found) {
104 layout.insert(screen->id(), pos);
105 changed = true;
106 remaining.remove(screen->id());
107 added.insert(screen);
108 break;
112 while (! added.empty()) {
113 QSet<ScreenXML *>::iterator i = added.begin();
114 while (i != added.end()) {
115 ScreenXML * s = *i;
116 if (remaining.contains(s->rightOf()) && ! layout.contains(s->rightOf())) {
117 ScreenXML * toAdd = remaining[s->rightOf()];
118 QPoint pos = QPoint(layout[s->id()]);
119 pos.rx()--;
121 layout.insert(toAdd->id(), pos);
122 added.insert(toAdd);
123 remaining.remove(toAdd->id());
124 break;
126 if (remaining.contains(s->bottomOf()) && ! layout.contains(s->bottomOf())) {
127 ScreenXML * toAdd = remaining[s->bottomOf()];
128 QPoint pos = QPoint(layout[s->id()]);
129 pos.ry()--;
131 layout.insert(toAdd->id(), pos);
132 added.insert(toAdd);
133 remaining.remove(toAdd->id());
134 break;
136 i = added.erase(i);
137 //++i;
140 } while (changed);
142 if (! remaining.empty()) {
143 //qDebug() << "invalid configuration (remaining):" << name() << remaining;
144 INVALID_CONFIGURATION("remaining screens")
145 layout.clear();
148 Configurations::translateOrigin(layout);
149 m_layout = layout;
151 return layout;
155 XMLConfigurations::XMLConfigurations(QObject * parent)
156 : BackendConfigurations(parent),
157 m_activeConfiguration(0),
158 m_markedConfiguration(0),
159 m_currentOutputs(0),
160 m_currentOutputsKnown(false),
161 m_confirmTimer(new QTimer(this)),
162 m_confirmLeft(0),
163 m_awaitingConfirm(false)
165 QDir dir = QDir::home();
166 dir.cd(".local");
167 m_configPath = dir.filePath("screen-configurations.xml");
169 m_externalConfiguration = new ExternalConfiguration(this);
170 connect(m_externalConfiguration, SIGNAL(activateExternal()), this, SLOT(activateExternal()));
172 connect(m_confirmTimer, SIGNAL(timeout()), this, SLOT(confirmTimerTimeout()));
174 init();
177 QMap<QString, Configuration *> XMLConfigurations::configurations()
179 QMap<QString, Configuration *> result;
180 for (QMap<QString, XMLConfiguration *>::const_iterator i = m_configurations.constBegin();
181 i != m_configurations.constEnd(); ++i) {
182 result.insert(i.key(), i.value());
185 return result;
188 void XMLConfigurations::init()
190 loadXml();
192 if (! m_configXml) {
193 m_configXml = new ConfigurationsXML();
196 * Create default single layout
198 ConfigurationXML * config = new ConfigurationXML();
199 config->setParent(m_configXml);
200 m_configXml->configurations().append(config);
202 config->setName("single");
203 config->setModifiable(false);
205 ScreenXML * screen = new ScreenXML();
206 screen->setParent(config);
207 config->screens().append(screen);
209 screen->setId(0);
210 screen->setPrivacy(false);
214 * Create default extended-right layout
216 config = new ConfigurationXML();
217 config->setParent(m_configXml);
218 m_configXml->configurations().append(config);
220 config->setName("extended-right");
221 config->setModifiable(false);
223 screen = new ScreenXML();
224 screen->setParent(config);
225 config->screens().append(screen);
227 screen->setId(0);
228 screen->setPrivacy(false);
230 screen = new ScreenXML();
231 screen->setParent(config);
232 config->screens().append(screen);
234 screen->setId(1);
235 screen->setPrivacy(false);
236 screen->setRightOf(0);
240 * Create default extended-left layout
242 config = new ConfigurationXML();
243 config->setParent(m_configXml);
244 m_configXml->configurations().append(config);
246 config->setName("extended-left");
247 config->setModifiable(false);
249 screen = new ScreenXML();
250 screen->setParent(config);
251 config->screens().append(screen);
253 screen->setId(0);
254 screen->setPrivacy(false);
255 screen->setRightOf(1);
257 screen = new ScreenXML();
258 screen->setParent(config);
259 config->screens().append(screen);
261 screen->setId(1);
262 screen->setPrivacy(false);
266 * Create outputs section for single output
268 OutputsXML * outputs = new OutputsXML();
269 outputs->setParent(m_configXml);
270 m_configXml->outputs().append(outputs);
272 outputs->setConfiguration("external");
274 OutputXML * output = new OutputXML();
275 output->setParent(outputs);
276 outputs->outputs().append(output);
278 output->setName("*");
279 output->setScreen(0);
280 output->setVendor("*");
284 * Create outputs section for 2 screens
286 outputs = new OutputsXML();
287 outputs->setParent(m_configXml);
288 m_configXml->outputs().append(outputs);
290 outputs->setConfiguration("external");
292 output = new OutputXML();
293 output->setParent(outputs);
294 outputs->outputs().append(output);
296 output->setName("*");
297 output->setScreen(0);
298 output->setVendor("*");
300 output = new OutputXML();
301 output->setParent(outputs);
302 outputs->outputs().append(output);
304 output->setName("*");
305 output->setScreen(1);
306 output->setVendor("*");
310 * Save the default xml
312 saveXml();
315 QList<ConfigurationXML *> configs = m_configXml->configurations();
316 for (int i = 0; i < configs.size(); i++) {
317 ConfigurationXML * config = configs[i];
319 XMLConfiguration * c = new XMLConfiguration(this, config);
320 m_configurations.insert(config->name(), c);
321 connect(c, SIGNAL(activate(XMLConfiguration *)), this, SLOT(activate(XMLConfiguration *)));
324 findOutputs();
327 Configuration * XMLConfigurations::findConfiguration()
329 qDebug() << "looking for a matching configuration...";
330 findOutputs();
331 if (! m_currentOutputs) {
332 return 0;
334 qDebug() << "found outputs, known:" << m_currentOutputsKnown;
336 if (m_currentOutputs->configuration() == "external") {
337 return m_externalConfiguration;
340 XMLConfiguration * config = m_configurations[m_currentOutputs->configuration()];
341 if (! config) {
342 //qDebug() << "config" << m_currentOutputs->configuration() << "does not exist!!";
343 CONFIGURATION_NOT_FOUND(m_currentOutputs->configuration())
344 return 0;
347 return config;
350 void XMLConfigurations::findOutputs()
352 m_currentOutputsKnown = true;
353 m_currentOutputs = findKnownOutputs();
354 if (! m_currentOutputs) {
355 m_currentOutputsKnown = false;
356 m_currentOutputs = findBestOutputs();
360 OutputsXML * XMLConfigurations::findKnownOutputs()
362 QList<Output *> currentOutputs = Outputs::self()->outputs();
363 int connected = 0;
364 foreach (Output * output, currentOutputs) {
365 if (output->isConnected()) {
366 ++connected;
370 foreach (OutputsXML * knownOutputs, m_configXml->outputs()) {
371 if (knownOutputs->outputs().size() != connected) {
372 continue;
375 bool matchedAll = true;
376 foreach (Output * current, currentOutputs) {
377 if (! current->isConnected()) {
378 continue;
381 bool matched = false;
382 foreach (OutputXML * known, knownOutputs->outputs()) {
383 if (known->name() != current->id()) {
384 continue;
387 if ((current->vendor() == known->vendor())
388 && (current->productId() == known->product())
389 && (current->serialNumber() == known->serial())) {
390 matched = true;
391 break;
395 if (! matched) {
396 matchedAll = false;
397 break;
401 if (matchedAll) {
402 return knownOutputs;
406 return 0;
409 OutputsXML * XMLConfigurations::findBestOutputs()
411 QList<Output *> currentOutputs = Outputs::self()->outputs();
412 int connected = 0;
413 foreach (Output * output, currentOutputs) {
414 if (output->isConnected()) {
415 ++connected;
419 qDebug() << "connected:" << connected;
421 qreal scoreAllMax = 0.01;
422 OutputsXML * knownAllMax = 0;
423 foreach (OutputsXML * knownOutputs, m_configXml->outputs()) {
424 if (knownOutputs->outputs().size() != connected) {
425 continue;
428 qreal scoreAll = 1;
429 QSet<OutputXML *> knownTaken;
430 foreach (Output * current, currentOutputs) {
431 if (! current->isConnected()) {
432 continue;
435 qDebug() << "looking for current" << current->id();
437 qreal scoreMax = 0.01;
438 OutputXML * knownMax = 0;
439 foreach (OutputXML * known, knownOutputs->outputs()) {
440 if (knownTaken.contains(known)) {
441 continue;
444 qreal score = 1;
445 score *= match(known->name(), current->id());
446 score *= match(known->vendor(), current->vendor());
447 score *= match(known->product(), current->productId());
449 qDebug() << "known" << known->name() << "has score:" << score;
450 if (score > scoreMax) {
451 knownMax = known;
452 scoreMax = score;
456 if (knownMax) {
457 scoreAll *= scoreMax;
458 knownTaken.insert(knownMax);
459 knownMax->setActualOutput(current->id());
460 } else {
461 scoreAll = 0;
462 break;
466 if (scoreAll > scoreAllMax) {
467 scoreAllMax = scoreAll;
468 knownAllMax = knownOutputs;
472 return knownAllMax;
475 qreal XMLConfigurations::match(QString known, QString current) {
476 if (known == current) {
477 return 1;
478 } else if (known == "*") {
479 return 0.5;
480 } else {
481 return 0;
485 qreal XMLConfigurations::match(int known, int current) {
486 if (known == current) {
487 return 1;
488 } else if (known == -1) {
489 return 0.5;
490 } else {
491 return 0;
495 QList<Configuration *> XMLConfigurations::alternateConfigurations() {
496 QList<Configuration *> configs;
497 foreach (XMLConfiguration * config, m_configurations) {
498 if (config->layout().size() <= m_currentOutputs->outputs().size()) {
499 configs.append(config);
503 return configs;
506 QList<XMLConfiguration *> XMLConfigurations::equivalentConfigurations(int numScreens) {
507 qDebug() << "looking for equivalent configurations with" << numScreens << "screens";
509 QList<XMLConfiguration *> result;
510 foreach (XMLConfiguration * config, m_configurations) {
511 if ((! config->isModifiable()) && (config->layout().size() == numScreens)) {
512 qDebug() << "found:" << config->name();
513 result.append(config);
517 return result;
520 QList<QPoint> XMLConfigurations::possiblePositions(Output * output) {
521 QList<QPoint> result;
522 QSet<QPoint> unique;
523 if (! output->isConnected()) {
524 return result;
527 if (! m_activeConfiguration) {
528 qDebug() << "don't have an active configuration";
529 return result;
532 QMap<XMLConfiguration *, QPoint> positions;
533 if (! m_activeConfiguration->isModifiable()) {
534 positions = equivalentConfigurationsPositions(output);
535 foreach (const QPoint& point, positions) {
536 unique.insert(point);
539 positions = simpleConfigurationsPositions(output, true);
540 foreach (const QPoint& point, positions) {
541 unique.insert(point);
543 } else {
544 positions = sameConfigurationsPositions(output, false);
545 foreach (const QPoint& point, positions) {
546 unique.insert(point);
549 positions = simpleConfigurationsPositions(output, false);
550 foreach (const QPoint& point, positions) {
551 unique.insert(point);
555 foreach (const QPoint& p, unique) {
556 result.append(p);
558 return result;
561 bool XMLConfigurations::move(Output * output, const QPoint & position) {
562 if ((! m_activeConfiguration) || (! output->isConnected())) {
563 return false;
565 if (position == output->position()) {
566 return true;
569 QMap<XMLConfiguration *, QPoint> positions;
570 if (! m_activeConfiguration->isModifiable()) {
571 positions = equivalentConfigurationsPositions(output);
572 qDebug() << "equiv pos for:" << output->id() << position << positions;
573 for (QMap<XMLConfiguration *, QPoint>::const_iterator i = positions.constBegin(); i != positions.constEnd(); ++i) {
574 if (i.value() == position) {
575 requireConfirm();
576 if (! activate(i.key())) {
577 revert();
578 return false;
580 return true;
584 positions = simpleConfigurationsPositions(output, true);
585 foreach (const QPoint& point, positions) {
586 Q_UNUSED(point)
587 FIX_ME("handle moving of output");
589 } else {
590 positions = sameConfigurationsPositions(output, false);
591 foreach (const QPoint& point, positions) {
592 Q_UNUSED(point)
593 FIX_ME("handle moving of output");
596 positions = simpleConfigurationsPositions(output, false);
597 foreach (const QPoint& point, positions) {
598 Q_UNUSED(point)
599 FIX_ME("handle moving of output");
603 return false;
606 QMap<int, QRect> XMLConfigurations::resizeLayout(Output * output, const QSize & size, QMap<Output *, int> & outputScreens, QMap<Output *, QSize> & outputSizes) {
607 outputScreens.unite(currentOutputScreens());
608 QMap<int, QPoint> simpleLayout = m_activeConfiguration->layout();
610 foreach (Output * o, outputScreens.keys()) {
611 if (o == output) {
612 outputSizes.insert(output, size);
613 } else if (o->isActivated()) {
614 outputSizes.insert(o, o->isActivated() ? o->size() : o->preferredSize());
618 return m_activeConfiguration->realLayout(simpleLayout, outputScreens, outputSizes);
621 bool XMLConfigurations::resize(Output * output, const QSize & size) {
622 qDebug() << "XMLConfigurations::resize() called" << output->id() << size;
623 if ((! m_activeConfiguration) || (! output->isConnected()) || (! output->isActivated())) {
624 return false;
626 if (size == output->size()) {
627 return true;
630 QMap<Output *, QSize> outputSizes;
631 QMap<Output *, int> outputScreens;
632 QMap<int, QRect> layout = resizeLayout(output, size, outputScreens, outputSizes);
634 requireConfirm();
635 if (activateLayout(layout, outputScreens, outputSizes)) {
636 OutputXML * o = outputXml(output->id());
637 if (o) {
638 o->setWidth(size.width());
639 o->setHeight(size.height());
641 return true;
644 revert();
645 return false;
648 QMap<XMLConfiguration *, QMap<int, QPoint> > XMLConfigurations::matchingConfigurationsLayouts(const QMap<int, QPoint> & currentLayout, int removedOutputs) {
649 //qDebug() << "searching matching layouts for" << currentLayout;
650 QMap<XMLConfiguration *, QMap<int, QPoint> > result;
651 QList<XMLConfiguration *> configurations = equivalentConfigurations(currentLayout.size() + removedOutputs);
652 foreach (XMLConfiguration * configuration, configurations) {
653 QMap<int, QPoint> layout = configuration->layout();
654 QMap<int, int> match = matchLayouts(currentLayout, layout);
655 if (! match.empty()) {
656 result.insert(configuration, layout);
660 return result;
663 QMap<int, int> XMLConfigurations::matchLayouts(const QMap<int, QPoint> & currentLayout, const QMap<int, QPoint> & layout) {
664 QList<int> indexes = layout.keys();
665 if (! currentLayout.empty()) {
666 indexes.insert(0, currentLayout.keys()[0]);
669 QPoint origin = currentLayout.begin().value();
670 QMap<int, int> result;
671 foreach (int i, indexes) {
672 QMap<int, QPoint> l = layout;
673 translateOrigin(l, l[i] - origin);
674 for (QMap<int, QPoint>::const_iterator j = currentLayout.constBegin(); j != currentLayout.constEnd(); ++j) {
675 bool found = false;
676 for (QMap<int, QPoint>::iterator k = l.begin(); k != l.end(); ++k) {
677 if (j.value() == k.value()) {
678 found = true;
679 result.insert(j.key(), k.key());
680 l.erase(k);
681 break;
684 if (! found) {
685 result.clear();
688 if (! result.empty()) {
689 int j = -1;
690 for (QMap<int, QPoint>::const_iterator k = l.constBegin(); k != l.constEnd(); ++k) {
691 result.insert(j, k.key());
692 --j;
694 return result;
698 return result;
701 QMap<int, QRect> XMLConfigurations::calcMatchingLayout(const QMap<int, QPoint> & currentLayout, XMLConfiguration * configuration, QMap<int, QPoint> layout, Output * output, int * outputScreen) {
702 QMap<int, int> match = matchLayouts(currentLayout, layout);
703 qDebug() << "match:" << match;
704 QMap<Output *, int> outputs;
705 Output * add = (match.contains(-1) ? output : 0);
706 Output * remove = (add ? 0 : output);
707 foreach (Output * o, Outputs::self()->outputs()) {
708 Screen * screen = o->screen();
709 if (remove && (remove == o)) {
710 outputs.insert(o, -1);
711 remove = 0;
712 } else if (screen && match.contains(screen->id())) {
713 outputs.insert(o, match[screen->id()]);
714 } else if (add && (add == o)) {
715 outputs.insert(o, match[-1]);
716 add = 0;
717 if (outputScreen) {
718 *outputScreen = match[-1];
723 QMap<int, QRect> realLayout = configuration->realLayout(layout, outputs);
724 translateToOther(realLayout, output, match);
726 return realLayout;
729 void XMLConfigurations::translateToOther(QMap<int, QRect> & layout, Output * output, QMap<int, int> match) {
730 foreach (Output * o, Outputs::self()->outputs()) {
731 if (o == output) {
732 continue;
735 Screen * screen = o->screen();
736 if (screen && (match.empty() || match.contains(screen->id()))) {
737 QPoint offset = layout[match.empty() ? screen->id() : match[screen->id()]].topLeft() - o->position();
738 translateOrigin(layout, offset);
739 break;
744 QMap<XMLConfiguration *, QPoint> XMLConfigurations::equivalentConfigurationsPositions(Output * output) {
745 bool cloned = false;
746 if (! output->isActivated()) {
747 cloned = true;
748 } else {
749 foreach (Output * o, Outputs::self()->outputs()) {
750 if (o == output) {
751 continue;
753 if (o->screen() == output->screen()) {
754 cloned = true;
755 break;
760 QMap<XMLConfiguration *, QPoint> positions;
761 QMap<int, QPoint> currentLayout = m_activeConfiguration->layout();
762 QMap<XMLConfiguration *, QMap<int, QPoint> > layouts;
763 QMap<int, QPoint> cloneLayout;
764 XMLConfiguration * cloneConfig = 0;
765 if (! cloned) {
766 currentLayout.remove(output->screen()->id());
767 translateOrigin(currentLayout);
768 layouts = matchingConfigurationsLayouts(currentLayout, 0);
769 if (! layouts.empty()) {
770 QMap<XMLConfiguration *, QMap<int, QPoint> >::const_iterator i = layouts.constBegin();
771 cloneLayout = i.value();
772 cloneConfig = i.key();
774 } else {
775 cloneLayout = currentLayout;
776 cloneConfig = m_activeConfiguration;
779 if (cloneConfig) {
780 QMap<int, QRect> layout = calcMatchingLayout(currentLayout, cloneConfig, cloneLayout, output);
781 foreach (const QRect& geom, layout) {
782 positions.insertMulti(cloneConfig, geom.topLeft());
786 qDebug() << "current layout:" << currentLayout;
787 layouts = matchingConfigurationsLayouts(currentLayout, 1);
788 for (QMap<XMLConfiguration *, QMap<int, QPoint> >::const_iterator i = layouts.constBegin(); i != layouts.constEnd(); ++i) {
789 qDebug() << "matching layout:" << i.key()->name() << i.value();
790 int outputScreen = -1;
791 QMap<int, QRect> layout = calcMatchingLayout(currentLayout, i.key(), i.value(), output, & outputScreen);
792 qDebug() << "results in:" << layout;
793 if (layout.contains(outputScreen)) {
794 positions.insertMulti(i.key(), layout[outputScreen].topLeft());
798 for (QMap<XMLConfiguration *, QPoint>::iterator i = positions.begin(); i != positions.end();) {
799 QPoint pos = i.value();
800 for (QMap<XMLConfiguration *, QPoint>::iterator j = i + 1; j != positions.end();) {
801 if (j.value() == pos) {
802 j = positions.erase(j);
803 } else {
804 ++j;
807 ++i;
810 return positions;
813 QMap<XMLConfiguration *, QPoint> XMLConfigurations::simpleConfigurationsPositions(Output * output, bool sameCount) {
814 Screen * screen = output->screen();
815 bool cloned = false;
816 if (! output->isActivated()) {
817 cloned = true;
818 } else {
819 int count = 0;
820 foreach (Output * o, Outputs::self()->outputs()) {
821 if (o->isActivated()) {
822 ++count;
824 if (o == output) {
825 continue;
827 if (o->screen() == screen) {
828 cloned = true;
829 break;
833 if (count <= 1) {
834 return QMap<XMLConfiguration *, QPoint>();
838 QMap<XMLConfiguration *, QPoint> positions;
839 QMap<int, QPoint> currentLayout = m_activeConfiguration->layout();
840 QMap<XMLConfiguration *, QMap<int, QPoint> > layouts;
841 QMap<int, QPoint> cloneLayout = currentLayout;
842 XMLConfiguration * cloneConfig = 0;
843 QMap<int, QPoint> noCloneLayout = currentLayout;
844 XMLConfiguration * noCloneConfig = 0;
846 if (! cloned) {
847 cloneLayout = m_activeConfiguration->cloneLayout(screen->id());
848 cloneConfig = simpleConfiguration(currentLayout.size() - 1);
849 cloneConfig->setLayout(cloneLayout);
851 if (sameCount) {
852 noCloneConfig = simpleConfiguration(currentLayout.size());
854 } else {
855 if (sameCount) {
856 cloneConfig = m_activeConfiguration;
859 noCloneConfig = simpleConfiguration(currentLayout.size() + 1);
862 if (cloneConfig) {
863 QMap<int, QRect> layout = calcMatchingLayout(currentLayout, cloneConfig, cloneLayout, output);
864 foreach (const QRect& geom, layout) {
865 positions.insertMulti(cloneConfig, geom.topLeft());
869 if (noCloneConfig) {
870 noCloneConfig->setLayout(noCloneLayout);
871 int screenId = 0;
872 if (cloned) {
873 while (noCloneLayout.contains(screenId)) {
874 ++screenId;
876 } else {
877 screenId = screen->id();
879 QSet<QPoint> possible = noCloneConfig->possiblePositions(screenId);
881 //qDebug() << "trying" << possible << "as" << screenId << "in layout" << noCloneConfig->name() << noCloneLayout;
883 QMap<Output *, int> outputIndexes;
884 foreach (Output * o, Outputs::self()->outputs()) {
885 Screen * s = o->screen();
886 outputIndexes.insert(o, (s ? s->id() : -1));
888 outputIndexes.insert(output, screenId);
890 foreach (const QPoint& p, possible) {
891 noCloneLayout.insert(screenId, p);
892 //qDebug() << "layout:" << noCloneLayout;
893 QMap<int, QRect> layout = noCloneConfig->realLayout(noCloneLayout, outputIndexes);
894 if (layout.contains(screenId)) {
895 translateToOther(layout, output);
896 //qDebug() << "results in:" << layout;
897 positions.insertMulti(noCloneConfig, layout[screenId].topLeft());
902 return positions;
905 QMap<XMLConfiguration *, QPoint> XMLConfigurations::sameConfigurationsPositions(Output * output, bool sameCount) {
906 Q_UNUSED(sameCount)
908 Screen * screen = output->screen();
909 bool cloned = false;
910 if (! output->isActivated()) {
911 cloned = true;
912 } else {
913 foreach (Output * o, Outputs::self()->outputs()) {
914 if (o == output) {
915 continue;
917 if (o->screen() == screen) {
918 cloned = true;
919 break;
924 QMap<XMLConfiguration *, QPoint> positions;
925 QMap<int, QPoint> currentLayout = m_activeConfiguration->layout();
926 QMap<XMLConfiguration *, QMap<int, QPoint> > layouts;
927 int screenId = (screen ? screen->id() : currentLayout.keys()[0]);
929 QSet<QPoint> possible = (cloned ? m_activeConfiguration->positions() : m_activeConfiguration->possiblePositions(screenId));
931 //qDebug() << "trying" << possible << "as" << screenId << "in layout" << m_activeConfiguration->name() << currentLayout;
933 QMap<Output *, int> outputIndexes;
934 foreach (Output * o, Outputs::self()->outputs()) {
935 Screen * s = o->screen();
936 outputIndexes.insert(o, (s ? s->id() : -1));
938 outputIndexes.insert(output, screenId);
940 foreach (const QPoint& p, possible) {
941 currentLayout.insert(screenId, p);
942 //qDebug() << "layout:" << currentLayout;
943 QMap<int, QRect> layout = m_activeConfiguration->realLayout(currentLayout, outputIndexes);
944 if (layout.contains(screenId)) {
945 translateToOther(layout, output);
946 //qDebug() << "results in:" << layout;
947 positions.insertMulti(m_activeConfiguration, layout[screenId].topLeft());
951 return positions;
954 void XMLConfigurations::activateExternal() {
955 qDebug() << "activate external configuration!!";
956 m_activeConfiguration = 0;
959 bool XMLConfigurations::activate(XMLConfiguration * configuration) {
960 qDebug() << "activate configuration:" << configuration->name();
961 if (configuration == m_activeConfiguration) {
962 return true;
964 QMap<int, QPoint> layout = configuration->layout();
966 if (! m_currentOutputsKnown) {
967 qDebug() << "saving xml for current outputs...";
969 OutputsXML * known = new OutputsXML();
970 known->setParent(m_configXml);
971 m_configXml->outputs().append(known);
973 known->setConfiguration(configuration->name());
975 QMap<QString, OutputXML *> currentMap;
976 foreach (OutputXML * o, m_currentOutputs->outputs()) {
977 currentMap.insert(o->actualOutput(), o);
980 QList<Output *> outputs = Outputs::self()->outputs();
981 foreach (Output * output, outputs) {
982 if (! output->isConnected()) {
983 continue;
985 if (! currentMap.contains(output->id())) {
986 INVALID_CONFIGURATION("m_currentOutputs not up to date");
987 return false;
990 OutputXML * outputXml = new OutputXML();
991 outputXml->setParent(known);
992 known->outputs().append(outputXml);
994 outputXml->setName(output->id());
995 outputXml->setScreen(currentMap[output->id()]->screen());
996 outputXml->setVendor(output->vendor());
997 outputXml->setProduct(output->productId());
998 outputXml->setSerial(output->serialNumber());
999 outputXml->setWidth(output->size().width());
1000 outputXml->setHeight(output->size().height());
1003 m_currentOutputs = known;
1004 m_currentOutputsKnown = true;
1005 } else {
1006 m_currentOutputs->setConfiguration(configuration->name());
1007 matchOutputScreens(layout);
1009 if (! m_awaitingConfirm) {
1010 saveXml();
1013 QMap<Output *, int> outputScreens = currentOutputScreens();
1014 QMap<int, QRect> screens = configuration->realLayout(layout, outputScreens);
1015 if (activateLayout(screens, outputScreens)) {
1016 m_activeConfiguration = configuration;
1017 emit configurationActivated(configuration);
1018 return true;
1021 qDebug() << "failed to activate configuration:" << configuration->name();
1022 return false;
1025 void XMLConfigurations::matchOutputScreens(const QMap<int, QPoint> & layout) {
1026 QMap<QString, int> outputScreens;
1027 QSet<int> screens;
1028 QSet<int> unknownScreens;
1029 QSet<int> takenScreens;
1030 QSet<QString> cloned;
1031 foreach (int screen, layout.keys()) {
1032 screens << screen;
1035 if (screens.size() > m_currentOutputs->outputs().size()) {
1036 INVALID_CONFIGURATION("configuration and outputs don't match");
1039 foreach (OutputXML * output, m_currentOutputs->outputs()) {
1040 if (output->screen() >= 0) {
1041 if (screens.contains(output->screen())) {
1042 outputScreens.insert(output->name(), output->screen());
1043 if (takenScreens.contains(output->screen())) {
1044 cloned << output->name();
1046 takenScreens << output->screen();
1047 } else {
1048 unknownScreens << output->screen();
1053 foreach (int taken, outputScreens) {
1054 screens.remove(taken);
1057 while (! (screens.empty() || unknownScreens.empty())) {
1058 int from = * unknownScreens.begin();
1059 unknownScreens.remove(from);
1060 int to = * screens.begin();
1061 screens.remove(to);
1063 foreach (OutputXML * output, m_currentOutputs->outputs()) {
1064 if (output->screen() == from) {
1065 outputScreens.insert(output->name(), to);
1070 while (! (screens.empty() || cloned.empty())) {
1071 QString o = * cloned.begin();
1072 cloned.remove(o);
1073 int to = * screens.begin();
1074 screens.remove(to);
1075 outputScreens.insert(o, to);
1078 foreach (OutputXML * output, m_currentOutputs->outputs()) {
1079 if (outputScreens.contains(output->name())) {
1080 output->setScreen(outputScreens[output->name()]);
1081 } else {
1082 output->setScreen(-1);
1087 QMap<Output *, int> XMLConfigurations::currentOutputScreens() {
1088 QMap<Output *, int> outputScreens;
1089 foreach (Output * output, Outputs::self()->outputs()) {
1090 int screen = this->screen(output);
1091 if (screen >= 0) {
1092 outputScreens.insert(output, screen);
1095 return outputScreens;
1098 bool XMLConfigurations::activateLayout(const QMap<int, QRect> & screensLayout, const QMap<Output *, int> & outputScreens) {
1099 QMap<Output *, QSize> outputSizes;
1100 foreach (Output * output, outputScreens.keys()) {
1101 outputSizes.insert(output, output->isActivated() ? output->size() : output->preferredSize());
1103 return activateLayout(screensLayout, outputScreens, outputSizes);
1106 bool XMLConfigurations::activateLayout(const QMap<int, QRect> & screensLayout, const QMap<Output *, int> & outputScreens, const QMap<Output *, QSize> & outputSizes) {
1107 if (screensLayout.empty()) {
1108 INVALID_CONFIGURATION("layout is empty");
1109 return false;
1112 if (! BackendOutputs::self()) {
1113 return false;
1116 QMap<Output *, QRect> layout;
1117 for (QMap<int, QRect>::const_iterator i = screensLayout.constBegin(); i != screensLayout.constEnd(); ++i) {
1118 for (QMap<Output *, int>::const_iterator j = outputScreens.constBegin(); j != outputScreens.constEnd(); ++j) {
1119 if (j.value() == i.key()) {
1120 layout.insert(j.key(), QRect(i.value().topLeft(), outputSizes[j.key()]));
1125 qDebug() << "layout:" << layout;
1126 if (! m_awaitingConfirm) {
1127 foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
1128 o->mark();
1132 if (! BackendOutputs::self()->activateLayout(layout)) {
1133 if (! m_awaitingConfirm) {
1134 foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
1135 o->revert();
1138 return false;
1141 return true;
1144 Configuration * XMLConfigurations::activeConfiguration() {
1145 return m_activeConfiguration ? (Configuration *) m_activeConfiguration : (Configuration *) m_externalConfiguration;
1148 XMLConfiguration * XMLConfigurations::simpleConfiguration(int numScreens) {
1149 QString name = "simple-" + QString::number(numScreens);
1150 if (m_configurations.contains(name)) {
1151 return m_configurations[name];
1154 ConfigurationXML * config = new ConfigurationXML();
1155 config->setParent(m_configXml);
1156 m_configXml->configurations().append(config);
1158 config->setName(name);
1159 config->setModifiable(true);
1161 for (int i = 0; i < numScreens; ++i) {
1162 ScreenXML * screen = new ScreenXML();
1163 screen->setParent(config);
1164 config->screens().append(screen);
1166 screen->setId(i);
1167 screen->setPrivacy(false);
1168 screen->setRightOf(i - 1);
1171 saveXml();
1173 m_configurations.insert(name, new XMLConfiguration(this, config));
1174 return m_configurations[name];
1177 void XMLConfigurations::saveXml() {
1178 qDebug() << "save xml";
1179 ConfigurationsXMLFactory * factory = new ConfigurationsXMLFactory();
1180 factory->save(m_configXml, m_configPath);
1181 delete factory;
1184 void XMLConfigurations::loadXml() {
1185 qDebug() << "load xml";
1186 ConfigurationsXMLFactory * factory = new ConfigurationsXMLFactory();
1187 m_configXml = (ConfigurationsXML *) factory->load(m_configPath);
1188 delete factory;
1191 int XMLConfigurations::screen(Output * output) {
1192 foreach (OutputXML * o, m_currentOutputs->outputs()) {
1193 if (output->id() == o->name()) {
1194 return o->screen();
1197 return -1;
1200 void XMLConfigurations::applyOutputSettings() {
1201 if (! BackendOutputs::self()) {
1202 return;
1205 findOutputs();
1206 if (! m_currentOutputs) {
1207 return;
1210 foreach (OutputXML * o, m_currentOutputs->outputs()) {
1211 BackendOutput * output = BackendOutputs::self()->backendOutput(o->name());
1212 if (output) {
1213 bool failed = false;
1214 output->mark();
1216 Rotation rotation = (Rotation) o->rotation();
1217 bool reflectX = o->reflectX();
1218 bool reflectY = o->reflectY();
1219 if ((rotation != output->rotation()) || (reflectX != output->reflectX()) || (reflectY != output->reflectY())) {
1220 qDebug() << "applying orientation to" << output->id() << rotation << reflectX << reflectY;
1221 if (! output->applyOrientation(rotation, reflectX, reflectY)) {
1222 OPERATION_FAILED("apply orientation")
1223 failed = true;
1227 QSize size(o->width(), o->height());
1228 float rate = o->rate();
1229 if ((! failed) && (! size.isEmpty()) && ((size != output->size()) || ((rate > 1) && (! qFuzzyCompare(rate, output->rate()))))) {
1230 qDebug() << "applying geom to" << output->id() << size << rate;
1231 if (! output->applyGeom(QRect(output->position(), size), rate)) {
1232 OPERATION_FAILED("apply geometry")
1233 failed = true;
1237 if (failed) {
1238 qDebug() << "reverting output" << output->id();
1239 output->revert();
1245 OutputXML * XMLConfigurations::outputXml(const QString & id) {
1246 foreach (OutputXML * o, m_currentOutputs->outputs()) {
1247 if (o->name() == id) {
1248 return o;
1251 return 0;
1254 bool XMLConfigurations::rotate(Output * output, Rotation rotation) {
1255 if (! BackendOutputs::self()) {
1256 return false;
1259 if (! m_activeConfiguration) {
1260 return false;
1263 BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
1264 if (o) {
1265 bool resizeNeeded = ((output->rotation() + rotation) % 180) != 0;
1266 if (resizeNeeded) {
1267 qDebug() << "resize is needed for changing rotation from" << output->rotation() << "to" << rotation;
1269 QSize size(output->size().height(), output->size().width());
1271 QMap<Output *, QSize> outputSizes;
1272 QMap<Output *, int> outputScreens;
1273 QMap<int, QRect> layout = resizeLayout(output, size, outputScreens, outputSizes);
1275 if (layout.empty()) {
1276 INVALID_CONFIGURATION("layout is empty")
1277 return false;
1280 requireConfirm();
1281 if (o->applyOrientation(rotation, o->reflectX(), o->reflectY()) && activateLayout(layout, outputScreens, outputSizes)) {
1282 OutputXML * xml = outputXml(output->id());
1283 if (xml) {
1284 xml->setWidth(size.width());
1285 xml->setHeight(size.height());
1286 xml->setRotation(rotation);
1287 //saveXml();
1290 return true;
1291 } else {
1292 qDebug() << "setting rotation to" << rotation << "for" << o->id() << "failed";
1294 revert();
1295 return false;
1297 } else {
1298 requireConfirm();
1299 if (o->applyOrientation(rotation, o->reflectX(), o->reflectY())) {
1300 OutputXML * xml = outputXml(o->id());
1301 if (xml) {
1302 xml->setRotation(rotation);
1303 //saveXml();
1306 return true;
1307 } else {
1308 qDebug() << "setting rotation to" << rotation << "for" << o->id() << "failed";
1310 revert();
1311 return false;
1316 return false;
1319 bool XMLConfigurations::reflectX(Output * output, bool reflect) {
1320 if (! BackendOutputs::self()) {
1321 return false;
1324 BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
1325 if (o) {
1326 requireConfirm();
1327 if (o->applyOrientation(o->rotation(), reflect, o->reflectY())) {
1328 OutputXML * xml = outputXml(o->id());
1329 if (xml) {
1330 xml->setReflectX(reflect);
1333 return true;
1334 } else {
1335 qDebug() << "setting reflect-x to" << reflect << "for" << o->id() << "failed";
1339 revert();
1340 return false;
1343 bool XMLConfigurations::reflectY(Output * output, bool reflect) {
1344 if (! BackendOutputs::self()) {
1345 return false;
1348 BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
1349 if (o) {
1350 requireConfirm();
1351 if (o->applyOrientation(o->rotation(), o->reflectX(), reflect)) {
1352 OutputXML * xml = outputXml(o->id());
1353 if (xml) {
1354 xml->setReflectY(reflect);
1357 return true;
1358 } else {
1359 qDebug() << "setting reflect-y to" << reflect << "for" << o->id() << "failed";
1363 revert();
1364 return false;
1367 bool XMLConfigurations::changeRate(Output * output, float rate) {
1368 if (! BackendOutputs::self()) {
1369 return false;
1372 BackendOutput * o = BackendOutputs::self()->backendOutput(output->id());
1373 if (o) {
1374 requireConfirm();
1375 if (o->applyGeom(o->geom(), rate)) {
1376 OutputXML * xml = outputXml(o->id());
1377 if (xml) {
1378 xml->setRate(rate);
1381 return true;
1382 } else {
1383 qDebug() << "setting rate to" << rate << "for" << o->id() << "failed";
1387 revert();
1388 return false;
1391 void XMLConfigurations::setPolling(bool polling) {
1392 if (polling != this->polling()) {
1393 m_configXml->setPolling(polling);
1394 saveXml();
1395 if (polling) {
1396 emit pollingActivated();
1397 } else {
1398 emit pollingDeactivated();
1403 bool XMLConfigurations::polling() {
1404 return m_configXml->polling();
1407 void XMLConfigurations::confirmTimerTimeout() {
1408 --m_confirmLeft;
1409 if (m_confirmLeft <= 0) {
1410 revert();
1411 } else {
1412 emit confirmTimeout(m_confirmLeft);
1416 void XMLConfigurations::confirm() {
1417 m_confirmTimer->stop();
1418 m_awaitingConfirm = false;
1419 saveXml();
1420 emit confirmed();
1423 void XMLConfigurations::revert() {
1424 m_confirmTimer->stop();
1425 if (! m_awaitingConfirm) {
1426 return;
1429 m_awaitingConfirm = false;
1431 m_activeConfiguration = m_markedConfiguration;
1432 if (BackendOutputs::self()) {
1433 foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
1434 o->revert();
1437 loadXml();
1439 if (m_activeConfiguration) {
1440 emit configurationActivated(m_activeConfiguration);
1443 emit reverted();
1446 void XMLConfigurations::requireConfirm() {
1447 if (! BackendOutputs::self()) {
1448 return;
1451 m_confirmLeft = CONFIRMATION_TIME;
1452 if (! m_awaitingConfirm) {
1453 m_awaitingConfirm = true;
1454 m_confirmTimer->start(1000);
1456 foreach (BackendOutput * o, BackendOutputs::self()->backendOutputs()) {
1457 o->mark();
1459 m_markedConfiguration = m_activeConfiguration;
1462 emit confirmTimeout(m_confirmLeft);