Changeset View
Changeset View
Standalone View
Standalone View
kded/output.cpp
Show All 21 Lines | |||||
22 | #include "generator.h" | 22 | #include "generator.h" | ||
23 | 23 | | |||
24 | #include <QStringList> | 24 | #include <QStringList> | ||
25 | #include <QFile> | 25 | #include <QFile> | ||
26 | #include <QStringBuilder> | 26 | #include <QStringBuilder> | ||
27 | #include <QJsonDocument> | 27 | #include <QJsonDocument> | ||
28 | #include <QDir> | 28 | #include <QDir> | ||
29 | #include <QLoggingCategory> | 29 | #include <QLoggingCategory> | ||
30 | #include <QRect> | ||||
30 | 31 | | |||
31 | #include <kscreen/output.h> | 32 | #include <kscreen/output.h> | ||
32 | #include <kscreen/edid.h> | 33 | #include <kscreen/edid.h> | ||
33 | 34 | | |||
34 | QString Output::s_dirName = QStringLiteral("outputs/"); | 35 | QString Output::s_dirName = QStringLiteral("outputs/"); | ||
35 | 36 | | |||
36 | QString Output::dirPath() | 37 | QString Output::dirPath() | ||
37 | { | 38 | { | ||
▲ Show 20 Lines • Show All 71 Lines • ▼ Show 20 Line(s) | 108 | { | |||
109 | if (info.empty()) { | 110 | if (info.empty()) { | ||
110 | // if info is empty, the global file does not exists, or is in an unreadable state | 111 | // if info is empty, the global file does not exists, or is in an unreadable state | ||
111 | return false; | 112 | return false; | ||
112 | } | 113 | } | ||
113 | readInGlobalPartFromInfo(output, info); | 114 | readInGlobalPartFromInfo(output, info); | ||
114 | return true; | 115 | return true; | ||
115 | } | 116 | } | ||
116 | 117 | | |||
118 | void Output::adjustPositions(KScreen::ConfigPtr config, const QVariantList &outputsInfo) | ||||
119 | { | ||||
120 | typedef QPair<int, QPoint> Out; | ||||
121 | | ||||
122 | KScreen::OutputList outputs = config->outputs(); | ||||
123 | QVector<Out> sortedOutputs; // <id, pos> | ||||
124 | for (const KScreen::OutputPtr output : outputs) { | ||||
125 | sortedOutputs.append(Out(output->id(), output->pos())); | ||||
126 | } | ||||
127 | | ||||
128 | // go from left to right, top to bottom | ||||
129 | std::sort(sortedOutputs.begin(), sortedOutputs.end(), [](const Out &o1, const Out &o2) { | ||||
130 | const int x1 = o1.second.x(); | ||||
131 | const int x2 = o2.second.x(); | ||||
132 | return x1 < x2 || (x1 == x2 && o1.second.y() < o2.second.y()); | ||||
133 | }); | ||||
134 | | ||||
135 | for (int cnt = 1; cnt < sortedOutputs.length(); cnt++) { | ||||
136 | auto getOutputInfoProperties = [outputsInfo](KScreen::OutputPtr output, QRect &geo) -> bool { | ||||
137 | if (!output) { | ||||
138 | return false; | ||||
139 | } | ||||
140 | const auto hash = output->hash(); | ||||
141 | | ||||
142 | auto it = std::find_if(outputsInfo.begin(), outputsInfo.end(), | ||||
143 | [hash](QVariant v) { | ||||
144 | const QVariantMap info = v.toMap(); | ||||
145 | return info[QStringLiteral("id")].toString() == hash; | ||||
146 | } | ||||
147 | ); | ||||
148 | if (it == outputsInfo.end()) { | ||||
149 | return false; | ||||
150 | } | ||||
151 | const QVariantMap outputInfo = it->toMap(); | ||||
152 | | ||||
153 | const QVariantMap posInfo = outputInfo[QStringLiteral("pos")].toMap(); | ||||
154 | const QVariant scaleInfo = outputInfo[QStringLiteral("scale")]; | ||||
155 | const QVariantMap modeInfo = outputInfo[QStringLiteral("mode")].toMap(); | ||||
156 | const QVariantMap modeSize = modeInfo[QStringLiteral("size")].toMap(); | ||||
157 | if (posInfo.isEmpty() || modeSize.isEmpty() || !scaleInfo.canConvert<int>()) { | ||||
158 | return false; | ||||
159 | } | ||||
160 | | ||||
161 | const int scale = scaleInfo.toInt(); | ||||
162 | if (scale <= 0) { | ||||
163 | return false; | ||||
164 | } | ||||
165 | const QPoint pos = QPoint(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); | ||||
166 | const QSize size = QSize(modeSize[QStringLiteral("width")].toInt() / scale, modeSize[QStringLiteral("height")].toInt() / scale); | ||||
167 | geo = QRect(pos, size); | ||||
168 | | ||||
169 | return true; | ||||
170 | }; | ||||
171 | | ||||
172 | // it's guaranteed that we find the following values in the QMap | ||||
173 | KScreen::OutputPtr prevPtr = outputs.find(sortedOutputs[cnt - 1].first).value(); | ||||
174 | KScreen::OutputPtr curPtr = outputs.find(sortedOutputs[cnt].first).value(); | ||||
175 | | ||||
176 | QRect prevInfoGeo, curInfoGeo; | ||||
177 | if (!getOutputInfoProperties(prevPtr, prevInfoGeo) || | ||||
178 | !getOutputInfoProperties(curPtr, curInfoGeo)) { | ||||
179 | // no info found, nothing can be adjusted for the next output | ||||
180 | continue; | ||||
181 | } | ||||
182 | | ||||
183 | const QRect prevGeo = prevPtr->geometry(); | ||||
184 | const QRect curGeo = curPtr->geometry(); | ||||
185 | | ||||
186 | // the old difference between previous and current output read from the config file | ||||
187 | const int xInfoDiff = curInfoGeo.x() - (prevInfoGeo.x() + prevInfoGeo.width()); | ||||
188 | | ||||
189 | // the proposed new difference | ||||
190 | const int prevRight = prevGeo.x() + prevGeo.width(); | ||||
191 | const int xCorrected = prevRight + prevGeo.width() * xInfoDiff / (double)prevInfoGeo.width(); | ||||
192 | const int xDiff = curGeo.x() - prevRight; | ||||
193 | | ||||
194 | // In the following calculate the y-coorection. This is more involved since we | ||||
195 | // differentiate between overlapping and non-overlapping pairs and align either | ||||
196 | // top to top/bottom or bottom to top/bottom | ||||
197 | const bool yOverlap = prevInfoGeo.y() + prevInfoGeo.height() > curInfoGeo.y() && | ||||
198 | prevInfoGeo.y() < curInfoGeo.y() + curInfoGeo.height(); | ||||
199 | | ||||
200 | // these values determine which horizontal edge of previous output we align with | ||||
201 | const int topToTopDiffAbs = qAbs(prevInfoGeo.y() - curInfoGeo.y()); | ||||
202 | const int topToBottomDiffAbs = qAbs(prevInfoGeo.y() - curInfoGeo.y() - curInfoGeo.height()); | ||||
203 | const int bottomToBottomDiffAbs = qAbs(prevInfoGeo.y() + prevInfoGeo.height() - curInfoGeo.y() - curInfoGeo.height()); | ||||
204 | const int bottomToTopDiffAbs = qAbs(prevInfoGeo.y() + prevInfoGeo.height() - curInfoGeo.y()); | ||||
205 | | ||||
206 | const bool yTopAligned = topToTopDiffAbs < bottomToBottomDiffAbs && topToTopDiffAbs <= bottomToTopDiffAbs || | ||||
207 | topToBottomDiffAbs < bottomToBottomDiffAbs; | ||||
208 | | ||||
209 | int yInfoDiff = curInfoGeo.y() - prevInfoGeo.y(); | ||||
210 | int yDiff = curGeo.y() - prevGeo.y(); | ||||
211 | int yCorrected; | ||||
212 | | ||||
213 | if (yTopAligned) { | ||||
214 | // align to previous top | ||||
215 | if (!yOverlap) { | ||||
216 | // align previous top with current bottom | ||||
217 | yInfoDiff += curInfoGeo.height(); | ||||
218 | yDiff += curGeo.height(); | ||||
219 | } | ||||
220 | // When we align with previous top we are interested in the changes to the | ||||
221 | // current geometry and not in the ones of the previous one. | ||||
222 | const double yInfoRel = yInfoDiff / (double)curInfoGeo.height(); | ||||
223 | yCorrected = prevGeo.y() + yInfoRel * curGeo.height(); | ||||
224 | } else { | ||||
225 | // align previous bottom... | ||||
226 | yInfoDiff -= prevInfoGeo.height(); | ||||
227 | yDiff -= prevGeo.height(); | ||||
228 | yCorrected = prevGeo.y() + prevGeo.height(); | ||||
229 | | ||||
230 | if (yOverlap) { | ||||
231 | // ... with current bottom | ||||
232 | yInfoDiff += curInfoGeo.height(); | ||||
233 | yDiff += curGeo.height(); | ||||
234 | yCorrected -= curGeo.height(); | ||||
235 | } // ... else with current top | ||||
236 | | ||||
237 | // When we align with previous bottom we are interested in changes to the | ||||
238 | // previous geometry. | ||||
239 | const double yInfoRel = yInfoDiff / (double)prevInfoGeo.height(); | ||||
240 | yCorrected += yInfoRel * prevGeo.height(); | ||||
241 | } | ||||
242 | | ||||
243 | const int x = xDiff == xInfoDiff ? curGeo.x() : xCorrected; | ||||
244 | const int y = yDiff == yInfoDiff ? curGeo.y() : yCorrected; | ||||
245 | curPtr->setPos(QPoint(x, y)); | ||||
246 | } | ||||
247 | } | ||||
248 | | ||||
117 | void Output::readIn(KScreen::OutputPtr output, const QVariantMap &info, Control::OutputRetention retention) | 249 | void Output::readIn(KScreen::OutputPtr output, const QVariantMap &info, Control::OutputRetention retention) | ||
118 | { | 250 | { | ||
119 | const QVariantMap posInfo = info[QStringLiteral("pos")].toMap(); | 251 | const QVariantMap posInfo = info[QStringLiteral("pos")].toMap(); | ||
120 | QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); | 252 | QPoint point(posInfo[QStringLiteral("x")].toInt(), posInfo[QStringLiteral("y")].toInt()); | ||
121 | output->setPos(point); | 253 | output->setPos(point); | ||
122 | output->setPrimary(info[QStringLiteral("primary")].toBool()); | 254 | output->setPrimary(info[QStringLiteral("primary")].toBool()); | ||
123 | output->setEnabled(info[QStringLiteral("enabled")].toBool()); | 255 | output->setEnabled(info[QStringLiteral("enabled")].toBool()); | ||
124 | 256 | | |||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Line(s) | 311 | if (!infoFound) { | |||
182 | qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current info data - this means that our info is corrupted" | 314 | qCWarning(KSCREEN_KDED) << "\tFailed to find a matching output in the current info data - this means that our info is corrupted" | ||
183 | "or a different device with the same serial number has been connected (very unlikely)."; | 315 | "or a different device with the same serial number has been connected (very unlikely)."; | ||
184 | if (!readInGlobal(output)) { | 316 | if (!readInGlobal(output)) { | ||
185 | // set some default values instead | 317 | // set some default values instead | ||
186 | readInGlobalPartFromInfo(output, QVariantMap()); | 318 | readInGlobalPartFromInfo(output, QVariantMap()); | ||
187 | } | 319 | } | ||
188 | } | 320 | } | ||
189 | } | 321 | } | ||
322 | // correct positional config regressions on global output data changes | ||||
323 | adjustPositions(config, outputsInfo); | ||||
190 | } | 324 | } | ||
191 | 325 | | |||
192 | static QVariantMap metadata(const KScreen::OutputPtr &output) | 326 | static QVariantMap metadata(const KScreen::OutputPtr &output) | ||
193 | { | 327 | { | ||
194 | QVariantMap metadata; | 328 | QVariantMap metadata; | ||
195 | metadata[QStringLiteral("name")] = output->name(); | 329 | metadata[QStringLiteral("name")] = output->name(); | ||
196 | if (!output->edid() || !output->edid()->isValid()) { | 330 | if (!output->edid() || !output->edid()->isValid()) { | ||
197 | return metadata; | 331 | return metadata; | ||
▲ Show 20 Lines • Show All 52 Lines • Show Last 20 Lines |