File indexing completed on 2024-04-28 16:45:08
0001 /* 0002 SPDX-FileCopyrightText: 2012 Alejandro Fiestas Olivares <afiestas@kde.org> 0003 SPDX-FileCopyrightText: 2016 Sebastian Kügler <sebas@kde.org> 0004 0005 Work sponsored by the LiMux project of the city of Munich: 0006 SPDX-FileCopyrightText: 2018 Kai Uwe Broulik <kde@broulik.de> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "daemon.h" 0011 0012 #include "../common/orientation_sensor.h" 0013 #include "config.h" 0014 #include "device.h" 0015 #include "generator.h" 0016 #include "kscreen_daemon_debug.h" 0017 #include "kscreenadaptor.h" 0018 #include "osdservice_interface.h" 0019 0020 #include <kscreen/configmonitor.h> 0021 #include <kscreen/getconfigoperation.h> 0022 #include <kscreen/log.h> 0023 #include <kscreen/mode.h> 0024 #include <kscreen/output.h> 0025 #include <kscreen/screen.h> 0026 #include <kscreen/setconfigoperation.h> 0027 #include <kscreendpms/dpms.h> 0028 0029 #include <KActionCollection> 0030 #include <KGlobalAccel> 0031 #include <KLocalizedString> 0032 #include <KPluginFactory> 0033 0034 #include <QAction> 0035 #include <QGuiApplication> 0036 #include <QOrientationReading> 0037 #include <QScreen> 0038 #include <QShortcut> 0039 #include <QTimer> 0040 0041 #if HAVE_X11 0042 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0043 #include <QX11Info> 0044 #else 0045 #include <private/qtx11extras_p.h> 0046 #endif 0047 #include <X11/Xatom.h> 0048 #include <X11/Xlib-xcb.h> 0049 #include <X11/extensions/XInput.h> 0050 #include <X11/extensions/XInput2.h> 0051 #endif 0052 0053 K_PLUGIN_CLASS_WITH_JSON(KScreenDaemon, "kscreen.json") 0054 0055 #if HAVE_X11 0056 struct DeviceListDeleter { 0057 void operator()(XDeviceInfo *p) 0058 { 0059 if (p) { 0060 XFreeDeviceList(p); 0061 } 0062 } 0063 }; 0064 0065 struct XDeleter { 0066 void operator()(void *p) 0067 { 0068 if (p) { 0069 XFree(p); 0070 } 0071 } 0072 }; 0073 #endif 0074 0075 KScreenDaemon::KScreenDaemon(QObject *parent, const QList<QVariant> &) 0076 : KDEDModule(parent) 0077 , m_monitoring(false) 0078 , m_changeCompressor(new QTimer(this)) 0079 , m_saveTimer(nullptr) 0080 , m_lidClosedTimer(new QTimer(this)) 0081 , m_orientationSensor(new OrientationSensor(this)) 0082 { 0083 connect(m_orientationSensor, &OrientationSensor::availableChanged, this, &KScreenDaemon::updateOrientation); 0084 connect(m_orientationSensor, &OrientationSensor::valueChanged, this, &KScreenDaemon::updateOrientation); 0085 0086 KScreen::Log::instance(); 0087 qMetaTypeId<KScreen::OsdAction>(); 0088 QMetaObject::invokeMethod(this, "getInitialConfig", Qt::QueuedConnection); 0089 0090 auto dpms = new KScreen::Dpms(this); 0091 connect(dpms, &KScreen::Dpms::modeChanged, this, [this](KScreen::Dpms::Mode mode, QScreen *screen) { 0092 if (m_monitoredConfig && m_monitoredConfig->data() && screen->geometry() == m_monitoredConfig->data()->primaryOutput()->geometry()) { 0093 if (mode == KScreen::Dpms::On) { 0094 m_orientationSensor->setEnabled(m_monitoredConfig->autoRotationRequested()); 0095 } else { 0096 m_orientationSensor->setEnabled(false); 0097 } 0098 } 0099 }); 0100 } 0101 0102 void KScreenDaemon::getInitialConfig() 0103 { 0104 connect(new KScreen::GetConfigOperation, &KScreen::GetConfigOperation::finished, this, [this](KScreen::ConfigOperation *op) { 0105 if (op->hasError()) { 0106 qCDebug(KSCREEN_KDED) << "Error getting initial configuration" << op->errorString(); 0107 return; 0108 } 0109 0110 m_monitoredConfig = std::unique_ptr<Config>(new Config(qobject_cast<KScreen::GetConfigOperation *>(op)->config())); 0111 m_monitoredConfig->setValidityFlags(KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen); 0112 qCDebug(KSCREEN_KDED) << "Config" << m_monitoredConfig->data().data() << "is ready"; 0113 KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig->data()); 0114 0115 init(); 0116 }); 0117 } 0118 0119 KScreenDaemon::~KScreenDaemon() 0120 { 0121 Generator::destroy(); 0122 Device::destroy(); 0123 } 0124 0125 void KScreenDaemon::init() 0126 { 0127 KActionCollection *coll = new KActionCollection(this); 0128 QAction *action = coll->addAction(QStringLiteral("display")); 0129 action->setText(i18n("Switch Display")); 0130 QList<QKeySequence> switchDisplayShortcuts({Qt::Key_Display, Qt::MetaModifier + Qt::Key_P}); 0131 KGlobalAccel::self()->setGlobalShortcut(action, switchDisplayShortcuts); 0132 connect(action, &QAction::triggered, this, &KScreenDaemon::displayButton); 0133 0134 new KScreenAdaptor(this); 0135 0136 const QString osdService = QStringLiteral("org.kde.kscreen.osdService"); 0137 const QString osdPath = QStringLiteral("/org/kde/kscreen/osdService"); 0138 m_osdServiceInterface = new OrgKdeKscreenOsdServiceInterface(osdService, osdPath, QDBusConnection::sessionBus(), this); 0139 // Set a longer timeout to not assume timeout while the osd is still shown 0140 m_osdServiceInterface->setTimeout(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(60)).count()); 0141 0142 m_changeCompressor->setInterval(10); 0143 m_changeCompressor->setSingleShot(true); 0144 connect(m_changeCompressor, &QTimer::timeout, this, &KScreenDaemon::applyConfig); 0145 0146 m_lidClosedTimer->setInterval(1000); 0147 m_lidClosedTimer->setSingleShot(true); 0148 connect(m_lidClosedTimer, &QTimer::timeout, this, &KScreenDaemon::disableLidOutput); 0149 0150 connect(Device::self(), &Device::lidClosedChanged, this, &KScreenDaemon::lidClosedChanged); 0151 connect(Device::self(), &Device::resumingFromSuspend, this, [this]() { 0152 KScreen::Log::instance()->setContext(QStringLiteral("resuming")); 0153 m_orientationSensor->setEnabled(m_monitoredConfig->autoRotationRequested()); 0154 qCDebug(KSCREEN_KDED) << "Resumed from suspend, checking for screen changes"; 0155 // We don't care about the result, we just want to force the backend 0156 // to query XRandR so that it will detect possible changes that happened 0157 // while the computer was suspended, and will emit the change events. 0158 new KScreen::GetConfigOperation(KScreen::GetConfigOperation::NoEDID, this); 0159 }); 0160 connect(Device::self(), &Device::aboutToSuspend, this, [this]() { 0161 qCDebug(KSCREEN_KDED) << "System is going to suspend, won't be changing config (waited for " 0162 << (m_lidClosedTimer->interval() - m_lidClosedTimer->remainingTime()) << "ms)"; 0163 m_lidClosedTimer->stop(); 0164 m_orientationSensor->setEnabled(false); 0165 }); 0166 0167 connect(Generator::self(), &Generator::ready, this, [this] { 0168 applyConfig(); 0169 0170 if (Device::self()->isLaptop() && Device::self()->isLidClosed()) { 0171 disableLidOutput(); 0172 } 0173 0174 m_startingUp = false; 0175 }); 0176 0177 Generator::self()->setCurrentConfig(m_monitoredConfig->data()); 0178 monitorConnectedChange(); 0179 } 0180 0181 void KScreenDaemon::updateOrientation() 0182 { 0183 if (!m_monitoredConfig) { 0184 return; 0185 } 0186 const auto features = m_monitoredConfig->data()->supportedFeatures(); 0187 if (!features.testFlag(KScreen::Config::Feature::AutoRotation) || !features.testFlag(KScreen::Config::Feature::TabletMode)) { 0188 return; 0189 } 0190 0191 if (!m_orientationSensor->available() || !m_orientationSensor->enabled()) { 0192 return; 0193 } 0194 0195 const auto orientation = m_orientationSensor->value(); 0196 if (orientation == QOrientationReading::Undefined) { 0197 // Orientation sensor went off. Do not change current orientation. 0198 return; 0199 } 0200 if (orientation == QOrientationReading::FaceUp || orientation == QOrientationReading::FaceDown) { 0201 // We currently don't do anything with FaceUp/FaceDown, but in the future we could use them 0202 // to shut off and switch on again a display when display is facing downwards/upwards. 0203 return; 0204 } 0205 0206 m_monitoredConfig->setDeviceOrientation(orientation); 0207 if (m_monitoring) { 0208 doApplyConfig(m_monitoredConfig->data()); 0209 } else { 0210 m_configDirty = true; 0211 } 0212 } 0213 0214 void KScreenDaemon::doApplyConfig(const KScreen::ConfigPtr &config) 0215 { 0216 qCDebug(KSCREEN_KDED) << "Do set and apply specific config"; 0217 auto configWrapper = std::unique_ptr<Config>(new Config(config)); 0218 configWrapper->setValidityFlags(KScreen::Config::ValidityFlag::RequireAtLeastOneEnabledScreen); 0219 0220 doApplyConfig(std::move(configWrapper)); 0221 } 0222 0223 void KScreenDaemon::doApplyConfig(std::unique_ptr<Config> config) 0224 { 0225 m_monitoredConfig = std::move(config); 0226 0227 m_monitoredConfig->activateControlWatching(); 0228 m_orientationSensor->setEnabled(m_monitoredConfig->autoRotationRequested()); 0229 0230 connect(m_monitoredConfig.get(), &Config::controlChanged, this, [this]() { 0231 m_orientationSensor->setEnabled(m_monitoredConfig->autoRotationRequested()); 0232 updateOrientation(); 0233 }); 0234 0235 refreshConfig(); 0236 } 0237 0238 void KScreenDaemon::refreshConfig() 0239 { 0240 setMonitorForChanges(false); 0241 m_configDirty = false; 0242 KScreen::ConfigMonitor::instance()->addConfig(m_monitoredConfig->data()); 0243 0244 connect(new KScreen::SetConfigOperation(m_monitoredConfig->data()), &KScreen::SetConfigOperation::finished, this, [this]() { 0245 qCDebug(KSCREEN_KDED) << "Config applied"; 0246 if (m_configDirty) { 0247 // Config changed in the meantime again, apply. 0248 doApplyConfig(m_monitoredConfig->data()); 0249 } else { 0250 setMonitorForChanges(true); 0251 } 0252 }); 0253 } 0254 0255 void KScreenDaemon::applyConfig() 0256 { 0257 qCDebug(KSCREEN_KDED) << "Applying config"; 0258 if (m_monitoredConfig->fileExists()) { 0259 applyKnownConfig(); 0260 return; 0261 } 0262 applyIdealConfig(); 0263 } 0264 0265 void KScreenDaemon::applyKnownConfig() 0266 { 0267 qCDebug(KSCREEN_KDED) << "Applying known config"; 0268 0269 std::unique_ptr<Config> readInConfig = m_monitoredConfig->readFile(); 0270 if (readInConfig) { 0271 doApplyConfig(std::move(readInConfig)); 0272 } else { 0273 qCDebug(KSCREEN_KDED) << "Loading failed, falling back to the ideal config" << m_monitoredConfig->id(); 0274 applyIdealConfig(); 0275 } 0276 } 0277 0278 void KScreenDaemon::applyLayoutPreset(const QString &presetName) 0279 { 0280 const QMetaEnum actionEnum = QMetaEnum::fromType<KScreen::OsdAction::Action>(); 0281 Q_ASSERT(actionEnum.isValid()); 0282 0283 bool ok; 0284 auto action = static_cast<KScreen::OsdAction::Action>(actionEnum.keyToValue(qPrintable(presetName), &ok)); 0285 if (!ok) { 0286 qCWarning(KSCREEN_KDED) << "Cannot apply unknown screen layout preset named" << presetName; 0287 return; 0288 } 0289 applyOsdAction(action); 0290 } 0291 0292 bool KScreenDaemon::getAutoRotate() 0293 { 0294 return m_monitoredConfig->getAutoRotate(); 0295 } 0296 0297 void KScreenDaemon::setAutoRotate(bool value) 0298 { 0299 if (!m_monitoredConfig) { 0300 return; 0301 } 0302 m_monitoredConfig->setAutoRotate(value); 0303 m_orientationSensor->setEnabled(value); 0304 } 0305 0306 bool KScreenDaemon::isAutoRotateAvailable() 0307 { 0308 return m_orientationSensor->available(); 0309 } 0310 0311 void KScreenDaemon::showOSD() 0312 { 0313 auto call = m_osdServiceInterface->showActionSelector(); 0314 auto watcher = new QDBusPendingCallWatcher(call); 0315 connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher] { 0316 watcher->deleteLater(); 0317 QDBusReply<int> reply = *watcher; 0318 if (!reply.isValid()) { 0319 return; 0320 } 0321 applyOsdAction(static_cast<KScreen::OsdAction::Action>(reply.value())); 0322 }); 0323 } 0324 0325 void KScreenDaemon::applyOsdAction(KScreen::OsdAction::Action action) 0326 { 0327 switch (action) { 0328 case KScreen::OsdAction::NoAction: 0329 qCDebug(KSCREEN_KDED) << "OSD: no action"; 0330 return; 0331 case KScreen::OsdAction::SwitchToInternal: 0332 qCDebug(KSCREEN_KDED) << "OSD: switch to internal"; 0333 doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffExternal)); 0334 return; 0335 case KScreen::OsdAction::SwitchToExternal: 0336 qCDebug(KSCREEN_KDED) << "OSD: switch to external"; 0337 doApplyConfig(Generator::self()->displaySwitch(Generator::TurnOffEmbedded)); 0338 return; 0339 case KScreen::OsdAction::ExtendLeft: 0340 qCDebug(KSCREEN_KDED) << "OSD: extend left"; 0341 doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToLeft)); 0342 return; 0343 case KScreen::OsdAction::ExtendRight: 0344 qCDebug(KSCREEN_KDED) << "OSD: extend right"; 0345 doApplyConfig(Generator::self()->displaySwitch(Generator::ExtendToRight)); 0346 return; 0347 case KScreen::OsdAction::Clone: 0348 qCDebug(KSCREEN_KDED) << "OSD: clone"; 0349 doApplyConfig(Generator::self()->displaySwitch(Generator::Clone)); 0350 return; 0351 } 0352 Q_UNREACHABLE(); 0353 } 0354 0355 void KScreenDaemon::applyIdealConfig() 0356 { 0357 const bool showOsd = m_monitoredConfig->data()->connectedOutputs().count() > 1 && !m_startingUp; 0358 0359 doApplyConfig(Generator::self()->idealConfig(m_monitoredConfig->data())); 0360 0361 if (showOsd) { 0362 qCDebug(KSCREEN_KDED) << "Getting ideal config from user via OSD..."; 0363 showOSD(); 0364 } else { 0365 m_osdServiceInterface->hideOsd(); 0366 } 0367 } 0368 0369 void KScreenDaemon::configChanged() 0370 { 0371 qCDebug(KSCREEN_KDED) << "Change detected"; 0372 m_monitoredConfig->log(); 0373 0374 // Modes may have changed, fix-up current mode id 0375 bool changed = false; 0376 const auto outputs = m_monitoredConfig->data()->outputs(); 0377 for (const KScreen::OutputPtr &output : outputs) { 0378 if (output->isConnected() && output->isEnabled() 0379 && (output->currentMode().isNull() || (output->followPreferredMode() && output->currentModeId() != output->preferredModeId()))) { 0380 qCDebug(KSCREEN_KDED) << "Current mode was" << output->currentModeId() << ", setting preferred mode" << output->preferredModeId(); 0381 output->setCurrentModeId(output->preferredModeId()); 0382 changed = true; 0383 } 0384 } 0385 if (changed) { 0386 refreshConfig(); 0387 } 0388 0389 // Reset timer, delay the writeback 0390 if (!m_saveTimer) { 0391 m_saveTimer = new QTimer(this); 0392 m_saveTimer->setInterval(300); 0393 m_saveTimer->setSingleShot(true); 0394 connect(m_saveTimer, &QTimer::timeout, this, &KScreenDaemon::saveCurrentConfig); 0395 } 0396 m_saveTimer->start(); 0397 #if HAVE_X11 0398 alignX11TouchScreen(); 0399 #endif 0400 } 0401 0402 #if HAVE_X11 0403 void KScreenDaemon::alignX11TouchScreen() 0404 { 0405 if (qGuiApp->platformName() != QStringLiteral("xcb")) { 0406 return; 0407 } 0408 auto *display = QX11Info::display(); 0409 if (!display) { 0410 return; 0411 } 0412 auto *connection = QX11Info::connection(); 0413 if (!connection) { 0414 return; 0415 } 0416 0417 const QRect totalRect(QPoint(0, 0), m_monitoredConfig->data()->screen()->currentSize()); 0418 QRect internalOutputRect; 0419 int touchScreenRotationAngle = 0; 0420 0421 for (const auto &output : m_monitoredConfig->data()->connectedOutputs()) { 0422 if (output->isEnabled() && output->type() == KScreen::Output::Panel) { 0423 internalOutputRect = output->geometry(); 0424 0425 switch (output->rotation()) { 0426 case KScreen::Output::Left: 0427 touchScreenRotationAngle = 90; 0428 break; 0429 case KScreen::Output::Right: 0430 touchScreenRotationAngle = 270; 0431 break; 0432 case KScreen::Output::Inverted: 0433 touchScreenRotationAngle = 180; 0434 break; 0435 default: 0436 touchScreenRotationAngle = 0; 0437 } 0438 } 0439 } 0440 0441 // Compute the transformation matrix for the 0442 QTransform transform; 0443 transform = transform.translate(float(internalOutputRect.x()) / float(totalRect.width()), float(internalOutputRect.y()) / float(totalRect.height())); 0444 transform = transform.scale(float(internalOutputRect.width()) / float(totalRect.width()), float(internalOutputRect.height()) / float(totalRect.height())); 0445 transform = transform.rotate(touchScreenRotationAngle); 0446 0447 // After rotation we need to make the matrix origin aligned with the workspace again 0448 // ____ ___ 0449 // |__| -> 90° clockwise -> ___ -> needs to be moved up -> | | 0450 // | | |_| 0451 // |_| 0452 switch (touchScreenRotationAngle) { 0453 case 90: 0454 transform = transform.translate(0, -1); 0455 break; 0456 case 270: 0457 transform = transform.translate(-1, 0); 0458 break; 0459 case 180: 0460 transform = transform.translate(-1, -1); 0461 break; 0462 default: 0463 break; 0464 } 0465 0466 auto getAtom = [](xcb_connection_t *connection, const char *name) { 0467 auto cookie = xcb_intern_atom(connection, true, strlen(name), name); 0468 auto reply = xcb_intern_atom_reply(connection, cookie, nullptr); 0469 if (reply) { 0470 return reply->atom; 0471 } else { 0472 return xcb_atom_t(0); 0473 } 0474 }; 0475 0476 int nDevices = 0; 0477 std::unique_ptr<XDeviceInfo, DeviceListDeleter> deviceInfo(XListInputDevices(display, &nDevices)); 0478 auto touchScreenAtom = getAtom(connection, XI_TOUCHSCREEN); 0479 if (touchScreenAtom == 0) { 0480 return; 0481 } 0482 auto matrixAtom = getAtom(connection, "Coordinate Transformation Matrix"); 0483 if (matrixAtom == 0) { 0484 return; 0485 } 0486 auto floatAtom = getAtom(connection, "FLOAT"); 0487 if (floatAtom == 0) { 0488 return; 0489 } 0490 0491 auto setMatrixAtom = [display, floatAtom](XDeviceInfo *info, Atom atom, const QTransform &transform) { 0492 Atom type; 0493 int format = 0; 0494 unsigned long nItems, bytesAfter; 0495 unsigned char *dataPtr = nullptr; 0496 0497 std::unique_ptr<unsigned char, XDeleter> data(dataPtr); 0498 XIGetProperty(display, info->id, atom, 0, 1000, False, AnyPropertyType, &type, &format, &nItems, &bytesAfter, &dataPtr); 0499 0500 if (nItems != 9) { 0501 return; 0502 } 0503 if (format != sizeof(float) * CHAR_BIT || type != floatAtom) { 0504 return; 0505 } 0506 0507 float *fData = reinterpret_cast<float *>(dataPtr); 0508 0509 fData[0] = transform.m11(); 0510 fData[1] = transform.m21(); 0511 fData[2] = transform.m31(); 0512 0513 fData[3] = transform.m12(); 0514 fData[4] = transform.m22(); 0515 fData[5] = transform.m32(); 0516 0517 fData[6] = transform.m13(); 0518 fData[7] = transform.m23(); 0519 fData[8] = transform.m33(); 0520 0521 XIChangeProperty(display, info->id, atom, type, format, PropModeReplace, dataPtr, nItems); 0522 }; 0523 0524 for (XDeviceInfo *info = deviceInfo.get(); info < deviceInfo.get() + nDevices; info++) { 0525 // Make sure device is touchscreen 0526 if (info->type != touchScreenAtom) { 0527 continue; 0528 } 0529 0530 int nProperties = 0; 0531 std::unique_ptr<Atom, XDeleter> properties(XIListProperties(display, info->id, &nProperties)); 0532 0533 bool matrixAtomFound = false; 0534 0535 Atom *atom = properties.get(); 0536 Atom *atomEnd = properties.get() + nProperties; 0537 for (; atom != atomEnd; atom++) { 0538 if (!internalOutputRect.isEmpty() && *atom == matrixAtom) { 0539 matrixAtomFound = true; 0540 } 0541 } 0542 0543 if (matrixAtomFound) { 0544 setMatrixAtom(info, matrixAtom, transform); 0545 } 0546 0547 // For now we assume there is only one touchscreen 0548 XFlush(display); 0549 break; 0550 } 0551 } 0552 #endif 0553 0554 void KScreenDaemon::saveCurrentConfig() 0555 { 0556 qCDebug(KSCREEN_KDED) << "Saving current config to file"; 0557 0558 // We assume the config is valid, since it's what we got, but we are interested 0559 // in the "at least one enabled screen" check 0560 0561 if (m_monitoredConfig->canBeApplied()) { 0562 m_monitoredConfig->writeFile(); 0563 m_monitoredConfig->log(); 0564 } else { 0565 qCWarning(KSCREEN_KDED) << "Config does not have at least one screen enabled, WILL NOT save this config, this is not what user wants."; 0566 m_monitoredConfig->log(); 0567 } 0568 } 0569 0570 void KScreenDaemon::displayButton() 0571 { 0572 qCDebug(KSCREEN_KDED) << "displayBtn triggered"; 0573 showOSD(); 0574 } 0575 0576 void KScreenDaemon::lidClosedChanged(bool lidIsClosed) 0577 { 0578 // Ignore this when we don't have any external monitors, we can't turn off our 0579 // only screen 0580 if (m_monitoredConfig->data()->connectedOutputs().count() == 1) { 0581 return; 0582 } 0583 0584 if (lidIsClosed) { 0585 // Lid is closed, now we wait for couple seconds to find out whether it 0586 // will trigger a suspend (see Device::aboutToSuspend), or whether we should 0587 // turn off the screen 0588 qCDebug(KSCREEN_KDED) << "Lid closed, waiting to see if the computer goes to sleep..."; 0589 m_lidClosedTimer->start(); 0590 return; 0591 } else { 0592 qCDebug(KSCREEN_KDED) << "Lid opened!"; 0593 // We should have a config with "_lidOpened" suffix lying around. If not, 0594 // then the configuration has changed while the lid was closed and we just 0595 // use applyConfig() and see what we can do ... 0596 if (auto openCfg = m_monitoredConfig->readOpenLidFile()) { 0597 doApplyConfig(std::move(openCfg)); 0598 } 0599 } 0600 } 0601 0602 void KScreenDaemon::disableLidOutput() 0603 { 0604 // Make sure nothing has changed in the past second... :-) 0605 if (!Device::self()->isLidClosed()) { 0606 return; 0607 } 0608 0609 // If we are here, it means that closing the lid did not result in suspend 0610 // action. 0611 // FIXME: This could be because the suspend took longer than m_lidClosedTimer 0612 // timeout. Ideally we need to be able to look into PowerDevil config to see 0613 // what's the configured action for lid events, but there's no API to do that 0614 // and I'm not parsing PowerDevil's configs... 0615 0616 qCDebug(KSCREEN_KDED) << "Lid closed, finding lid to disable"; 0617 for (KScreen::OutputPtr &output : m_monitoredConfig->data()->outputs()) { 0618 if (output->type() == KScreen::Output::Panel) { 0619 if (output->isConnected() && output->isEnabled()) { 0620 // Save the current config with opened lid, just so that we know 0621 // how to restore it later 0622 m_monitoredConfig->writeOpenLidFile(); 0623 disableOutput(output); 0624 refreshConfig(); 0625 return; 0626 } 0627 } 0628 } 0629 } 0630 0631 void KScreenDaemon::outputConnectedChanged() 0632 { 0633 if (!m_changeCompressor->isActive()) { 0634 m_changeCompressor->start(); 0635 } 0636 0637 KScreen::Output *output = qobject_cast<KScreen::Output *>(sender()); 0638 qCDebug(KSCREEN_KDED) << "outputConnectedChanged():" << output->name(); 0639 0640 if (output->isConnected()) { 0641 Q_EMIT outputConnected(output->name()); 0642 0643 if (!m_monitoredConfig->fileExists()) { 0644 Q_EMIT unknownOutputConnected(output->name()); 0645 } 0646 } 0647 } 0648 0649 void KScreenDaemon::monitorConnectedChange() 0650 { 0651 const KScreen::OutputList outputs = m_monitoredConfig->data()->outputs(); 0652 for (const KScreen::OutputPtr &output : outputs) { 0653 connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); 0654 } 0655 connect( 0656 m_monitoredConfig->data().data(), 0657 &KScreen::Config::outputAdded, 0658 this, 0659 [this](const KScreen::OutputPtr &output) { 0660 if (output->isConnected()) { 0661 m_changeCompressor->start(); 0662 } 0663 connect(output.data(), &KScreen::Output::isConnectedChanged, this, &KScreenDaemon::outputConnectedChanged, Qt::UniqueConnection); 0664 }, 0665 Qt::UniqueConnection); 0666 connect(m_monitoredConfig->data().data(), 0667 &KScreen::Config::outputRemoved, 0668 this, 0669 &KScreenDaemon::applyConfig, 0670 static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::UniqueConnection)); 0671 } 0672 0673 void KScreenDaemon::setMonitorForChanges(bool enabled) 0674 { 0675 if (m_monitoring == enabled) { 0676 return; 0677 } 0678 0679 qCDebug(KSCREEN_KDED) << "Monitor for changes: " << enabled; 0680 m_monitoring = enabled; 0681 if (m_monitoring) { 0682 connect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged, Qt::UniqueConnection); 0683 } else { 0684 disconnect(KScreen::ConfigMonitor::instance(), &KScreen::ConfigMonitor::configurationChanged, this, &KScreenDaemon::configChanged); 0685 } 0686 } 0687 0688 void KScreenDaemon::disableOutput(const KScreen::OutputPtr &output) 0689 { 0690 const QRect geom = output->geometry(); 0691 qCDebug(KSCREEN_KDED) << "Laptop geometry:" << geom << output->pos() << (output->currentMode() ? output->currentMode()->size() : QSize()); 0692 0693 // Move all outputs right from the @p output to left 0694 for (KScreen::OutputPtr &otherOutput : m_monitoredConfig->data()->outputs()) { 0695 if (otherOutput == output || !otherOutput->isConnected() || !otherOutput->isEnabled()) { 0696 continue; 0697 } 0698 0699 QPoint otherPos = otherOutput->pos(); 0700 if (otherPos.x() >= geom.right() && otherPos.y() >= geom.top() && otherPos.y() <= geom.bottom()) { 0701 otherPos.setX(otherPos.x() - geom.width()); 0702 } 0703 qCDebug(KSCREEN_KDED) << "Moving" << otherOutput->name() << "from" << otherOutput->pos() << "to" << otherPos; 0704 otherOutput->setPos(otherPos); 0705 } 0706 0707 // Disable the output 0708 output->setEnabled(false); 0709 } 0710 0711 #include "daemon.moc"