File indexing completed on 2024-04-21 05:27:33
0001 /* 0002 SPDX-FileCopyrightText: 2004 Chris Howells <howells@kde.org> 0003 SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 #include "greeterapp.h" 0008 #include "kscreensaversettingsbase.h" 0009 #include "lnf_integration.h" 0010 #include "noaccessnetworkaccessmanagerfactory.h" 0011 #include "powermanagement.h" 0012 #include "wallpaper_integration.h" 0013 0014 #include <config-kscreenlocker.h> 0015 #include <iostream> 0016 #include <kscreenlocker_greet_logging.h> 0017 0018 #include <LayerShellQt/Window> 0019 0020 // KDE 0021 #include <KAuthorized> 0022 #include <KConfigPropertyMap> 0023 #include <KCrash> 0024 #include <KLocalizedContext> 0025 #include <KScreenDpms/Dpms> 0026 #include <KWindowSystem> 0027 #include <PlasmaQuick/QuickViewSharedEngine> 0028 0029 #include <KUser> 0030 // Plasma 0031 #include <KPackage/Package> 0032 #include <KPackage/PackageLoader> 0033 // Qt 0034 #include <QAbstractNativeEventFilter> 0035 #include <QClipboard> 0036 #include <QDBusConnection> 0037 #include <QKeyEvent> 0038 #include <QMimeData> 0039 #include <QSocketNotifier> 0040 #include <QThread> 0041 #include <QTimer> 0042 #include <qscreen.h> 0043 0044 #include <QQmlContext> 0045 #include <QQmlEngine> 0046 #include <QQmlExpression> 0047 #include <QQmlProperty> 0048 #include <QQuickItem> 0049 #include <QQuickView> 0050 0051 #include <private/qtx11extras_p.h> 0052 // Wayland 0053 #include <wayland-client.h> 0054 #include <wayland-ksld-client-protocol.h> 0055 // X11 0056 #include <X11/Xatom.h> 0057 #include <X11/Xlib.h> 0058 #include <fixx11h.h> 0059 // 0060 #include <xcb/xcb.h> 0061 0062 #include "pamauthenticator.h" 0063 #include "pamauthenticators.h" 0064 0065 // this is usable to fake a "screensaver" installation for testing 0066 // *must* be "0" for every public commit! 0067 #define TEST_SCREENSAVER 0 0068 0069 static const QString s_plasmaShellService = QStringLiteral("org.kde.plasmashell"); 0070 static const QString s_osdServicePath = QStringLiteral("/org/kde/osdService"); 0071 static const QString s_osdServiceInterface = QStringLiteral("org.kde.osdService"); 0072 0073 using namespace Qt::Literals; 0074 0075 namespace ScreenLocker 0076 { 0077 // disable DrKonqi as the crash dialog blocks the restart of the locker 0078 void disableDrKonqi() 0079 { 0080 KCrash::setDrKonqiEnabled(false); 0081 } 0082 // run immediately, before Q_CORE_STARTUP functions 0083 // that would enable drkonqi 0084 Q_CONSTRUCTOR_FUNCTION(disableDrKonqi) 0085 0086 // Verify that a package or its fallback is using the right API 0087 bool verifyPackageApi(const KPackage::Package &package) 0088 { 0089 if (package.metadata().value("X-Plasma-APIVersion", QStringLiteral("1")).toInt() >= 2) { 0090 return true; 0091 } 0092 0093 if (!package.filePath("lockscreenmainscript").contains(package.path())) { 0094 // The current package does not contain the lock screen and we are 0095 // using the fallback package. So check to see if that package has 0096 // the right version instead. 0097 if (package.fallbackPackage().metadata().value("X-Plasma-APIVersion", QStringLiteral("1")).toInt() >= 2) { 0098 return true; 0099 } 0100 } 0101 0102 return false; 0103 } 0104 0105 class FocusOutEventFilter : public QAbstractNativeEventFilter 0106 { 0107 public: 0108 bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result) override 0109 { 0110 Q_UNUSED(result) 0111 if (qstrcmp(eventType, "xcb_generic_event_t") != 0) { 0112 return false; 0113 } 0114 xcb_generic_event_t *event = reinterpret_cast<xcb_generic_event_t *>(message); 0115 if ((event->response_type & ~0x80) == XCB_FOCUS_OUT) { 0116 return true; 0117 } 0118 return false; 0119 } 0120 }; 0121 0122 class WallpaperItem : public WallpaperIntegration 0123 { 0124 Q_OBJECT 0125 public: 0126 explicit WallpaperItem(QQuickItem *parent = nullptr) 0127 : WallpaperIntegration(parent) 0128 { 0129 setConfig(KScreenSaverSettingsBase::self()->sharedConfig()); 0130 setPluginName(KScreenSaverSettingsBase::self()->wallpaperPluginId()); 0131 init(); 0132 } 0133 }; 0134 0135 // App 0136 UnlockApp::UnlockApp(int &argc, char **argv) 0137 : QGuiApplication(argc, argv) 0138 , m_resetRequestIgnoreTimer(new QTimer(this)) 0139 , m_delayedLockTimer(nullptr) 0140 , m_testing(false) 0141 , m_ignoreRequests(false) 0142 , m_immediateLock(false) 0143 , m_graceTime(0) 0144 , m_noLock(false) 0145 , m_defaultToSwitchUser(false) 0146 , m_lnfIntegration(new LnFIntegration(this)) 0147 { 0148 auto interactive = std::make_unique<PamAuthenticator>(KSCREENLOCKER_PAM_SERVICE, KUser().loginName()); 0149 std::vector<std::unique_ptr<PamAuthenticator>> noninteractive; 0150 noninteractive.push_back(std::make_unique<PamAuthenticator>(KSCREENLOCKER_PAM_FINGERPRINT_SERVICE, KUser().loginName(), PamAuthenticator::Fingerprint)); 0151 noninteractive.push_back(std::make_unique<PamAuthenticator>(KSCREENLOCKER_PAM_SMARTCARD_SERVICE, KUser().loginName(), PamAuthenticator::Smartcard)); 0152 m_authenticators = new PamAuthenticators(std::move(interactive), std::move(noninteractive), this); 0153 initialize(); 0154 0155 if (QX11Info::isPlatformX11()) { 0156 installNativeEventFilter(new FocusOutEventFilter); 0157 } 0158 } 0159 0160 UnlockApp::~UnlockApp() 0161 { 0162 // workaround QTBUG-55460 0163 // will be fixed when themes port to QQC2 0164 for (auto view : std::as_const(m_views)) { 0165 if (QQuickItem *focusItem = view->activeFocusItem()) { 0166 focusItem->setFocus(false); 0167 } 0168 } 0169 qDeleteAll(m_views); 0170 0171 if (m_ksldInterface) { 0172 org_kde_ksld_destroy(m_ksldInterface); 0173 } 0174 if (m_display) { 0175 wl_display_disconnect(m_display); 0176 } 0177 } 0178 0179 void UnlockApp::initialize() 0180 { 0181 // set up the request ignore timeout, so that multiple requests to sleep/suspend/shutdown 0182 // are not processed in quick (and confusing) succession) 0183 m_resetRequestIgnoreTimer->setSingleShot(true); 0184 m_resetRequestIgnoreTimer->setInterval(2000); 0185 connect(m_resetRequestIgnoreTimer, &QTimer::timeout, this, &UnlockApp::resetRequestIgnore); 0186 0187 KScreenSaverSettingsBase::self()->load(); 0188 KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); 0189 KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); 0190 m_packageName = cg.readEntry("LookAndFeelPackage", QString()); 0191 if (!m_packageName.isEmpty()) { 0192 package.setPath(m_packageName); 0193 } 0194 if (!KScreenSaverSettingsBase::theme().isEmpty()) { 0195 package.setPath(KScreenSaverSettingsBase::theme()); 0196 } 0197 if (!verifyPackageApi(package)) { 0198 qCWarning(KSCREENLOCKER_GREET) << "Lockscreen QML outdated, falling back to default"; 0199 package.setPath(QStringLiteral("org.kde.breeze.desktop")); 0200 } 0201 0202 m_mainQmlPath = package.fileUrl("lockscreenmainscript"); 0203 0204 // The root of wallpaper packages will be a WallpaperItem we provide the same API via WallpaperIntegration 0205 // although with most things nooping 0206 constexpr const char *uri = "org.kde.plasma.plasmoid"; 0207 qmlRegisterType<WallpaperItem>(uri, 2, 0, "WallpaperItem"); 0208 0209 m_wallpaperPackage = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Wallpaper")); 0210 m_wallpaperPackage.setPath(KScreenSaverSettingsBase::self()->wallpaperPluginId()); 0211 0212 m_lnfIntegration->setPackage(package); 0213 m_lnfIntegration->setConfig(KScreenSaverSettingsBase::self()->sharedConfig()); 0214 m_lnfIntegration->init(); 0215 0216 const KUser user; 0217 const QString fullName = user.property(KUser::FullName).toString(); 0218 0219 m_userName = fullName.isEmpty() ? user.loginName() : fullName; 0220 m_userImage = user.faceIconPath(); 0221 0222 installEventFilter(this); 0223 0224 QDBusConnection::sessionBus().connect(s_plasmaShellService, 0225 s_osdServicePath, 0226 s_osdServiceInterface, 0227 QStringLiteral("osdProgress"), 0228 this, 0229 SLOT(osdProgress(QString, int, int, QString))); 0230 QDBusConnection::sessionBus() 0231 .connect(s_plasmaShellService, s_osdServicePath, s_osdServiceInterface, QStringLiteral("osdText"), this, SLOT(osdText(QString, QString))); 0232 0233 connect(PowerManagement::instance(), &PowerManagement::canSuspendChanged, this, &UnlockApp::updateCanSuspend); 0234 connect(PowerManagement::instance(), &PowerManagement::canHibernateChanged, this, &UnlockApp::updateCanHibernate); 0235 } 0236 0237 QWindow *UnlockApp::getActiveScreen() 0238 { 0239 QWindow *activeScreen = nullptr; 0240 0241 if (m_views.isEmpty()) { 0242 return activeScreen; 0243 } 0244 0245 for (PlasmaQuick::QuickViewSharedEngine *view : std::as_const(m_views)) { 0246 if (view->geometry().contains(QCursor::pos())) { 0247 activeScreen = view; 0248 break; 0249 } 0250 } 0251 if (!activeScreen) { 0252 activeScreen = m_views.first(); 0253 } 0254 0255 return activeScreen; 0256 } 0257 0258 PlasmaQuick::SharedQmlEngine *UnlockApp::loadWallpaperPlugin(PlasmaQuick::QuickViewSharedEngine *view) 0259 { 0260 if (!m_wallpaperPackage.isValid()) { 0261 qCWarning(KSCREENLOCKER_GREET) << "Error loading the wallpaper, no valid package loaded"; 0262 return nullptr; 0263 } 0264 0265 auto qmlObject = new PlasmaQuick::SharedQmlEngine(view); 0266 qmlObject->setInitializationDelayed(true); 0267 qmlObject->setSource(QUrl::fromLocalFile(m_wallpaperPackage.filePath("mainscript"))); 0268 view->setProperty("wallpaperGraphicsObject", QVariant::fromValue(qmlObject)); 0269 connect(qmlObject, &PlasmaQuick::SharedQmlEngine::finished, this, [this, qmlObject, view] { 0270 auto item = qobject_cast<WallpaperItem *>(qmlObject->rootObject()); 0271 if (!item) { 0272 qCWarning(KSCREENLOCKER_GREET) << "Root item not a WallpaperItem"; 0273 return; 0274 }; 0275 qmlObject->rootContext()->setContextProperty(QStringLiteral("wallpaper"), item); 0276 view->rootContext()->setContextProperty(QStringLiteral("wallpaper"), item); 0277 view->rootContext()->setContextProperty(QStringLiteral("wallpaperIntegration"), item); 0278 }); 0279 return qmlObject; 0280 } 0281 0282 void UnlockApp::setWallpaperItemProperties(PlasmaQuick::SharedQmlEngine *wallpaperObject, PlasmaQuick::QuickViewSharedEngine *view) 0283 { 0284 if (!wallpaperObject) { 0285 return; 0286 } 0287 0288 auto item = qobject_cast<QQuickItem *>(wallpaperObject->rootObject()); 0289 if (!item) { 0290 qCWarning(KSCREENLOCKER_GREET) << "Wallpaper needs to be a QtQuick Item"; 0291 return; 0292 } 0293 item->setParentItem(view->rootObject()); 0294 item->setZ(-1000); 0295 0296 // set anchors 0297 QQmlExpression expr(wallpaperObject->engine()->rootContext(), item, QStringLiteral("parent")); 0298 QQmlProperty prop(item, QStringLiteral("anchors.fill")); 0299 prop.write(expr.evaluate()); 0300 } 0301 0302 void UnlockApp::initialViewSetup() 0303 { 0304 qmlRegisterUncreatableType<PamAuthenticator>("org.kde.kscreenlocker", 0305 1, 0306 0, 0307 "Authenticator", 0308 QStringLiteral("authenticators must be obtained from the context")); 0309 qmlRegisterUncreatableType<PamAuthenticators>("org.kde.kscreenlocker", 0310 1, 0311 0, 0312 "Authenticators", 0313 QStringLiteral("authenticators must be obtained from the context")); 0314 for (QScreen *screen : screens()) { 0315 handleScreen(screen); 0316 } 0317 connect(this, &UnlockApp::screenAdded, this, &UnlockApp::handleScreen); 0318 } 0319 0320 void UnlockApp::handleScreen(QScreen *screen) 0321 { 0322 if (screen->geometry().isNull()) { 0323 return; 0324 } 0325 auto *view = createViewForScreen(screen); 0326 m_views << view; 0327 connect(this, &QGuiApplication::screenRemoved, view, [this, view, screen](QScreen *removedScreen) { 0328 if (removedScreen != screen) { 0329 return; 0330 } 0331 m_views.removeOne(view); 0332 delete view; 0333 }); 0334 } 0335 0336 PlasmaQuick::QuickViewSharedEngine *UnlockApp::createViewForScreen(QScreen *screen) 0337 { 0338 // create the view 0339 auto *view = new PlasmaQuick::QuickViewSharedEngine(); 0340 0341 view->setColor(Qt::black); 0342 view->setScreen(screen); 0343 view->setGeometry(screen->geometry()); 0344 0345 connect(screen, &QScreen::geometryChanged, view, [view](const QRect &geo) { 0346 view->setGeometry(geo); 0347 }); 0348 0349 view->engine()->setProperty("_kirigamiTheme", QStringLiteral("KirigamiPlasmaStyle")); 0350 view->engine()->rootContext()->setContextObject(new KLocalizedContext(view->engine().get())); 0351 auto oldFactory = view->engine()->networkAccessManagerFactory(); 0352 view->engine()->setNetworkAccessManagerFactory(nullptr); 0353 delete oldFactory; 0354 view->engine()->setNetworkAccessManagerFactory(new NoAccessNetworkAccessManagerFactory); 0355 0356 if (!m_testing) { 0357 if (QX11Info::isPlatformX11()) { 0358 view->setFlags(Qt::X11BypassWindowManagerHint); 0359 } else { 0360 view->setFlags(Qt::FramelessWindowHint); 0361 } 0362 } 0363 0364 if (m_ksldInterface) { 0365 view->create(); 0366 org_kde_ksld_x11window(m_ksldInterface, view->winId()); 0367 wl_display_flush(m_display); 0368 } 0369 0370 // engine stuff 0371 QQmlContext *context = view->engine()->rootContext(); 0372 connect(view->engine().get(), &QQmlEngine::quit, this, [this]() { 0373 if (m_authenticators->isUnlocked()) { 0374 std::cout << "Unlocked" << std::endl; 0375 QCoreApplication::quit(); 0376 } else { 0377 qCWarning(KSCREENLOCKER_GREET) << "Greeter tried to quit without being unlocked"; 0378 } 0379 }); 0380 0381 context->setContextProperty(QStringLiteral("kscreenlocker_userName"), m_userName); 0382 context->setContextProperty(QStringLiteral("kscreenlocker_userImage"), m_userImage); 0383 context->setContextProperty(QStringLiteral("authenticator"), m_authenticators); 0384 context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_interfaceVersion"), 2); 0385 context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_view"), view); 0386 context->setContextProperty(QStringLiteral("defaultToSwitchUser"), m_defaultToSwitchUser); 0387 context->setContextProperty(QStringLiteral("config"), m_lnfIntegration->configuration()); 0388 0389 auto wallpaperObj = loadWallpaperPlugin(view); 0390 if (auto object = view->property("wallpaperGraphicsObject").value<PlasmaQuick::SharedQmlEngine *>()) { 0391 // initialize with our size to avoid as much resize events as possible 0392 object->completeInitialization({ 0393 {QStringLiteral("width"), view->width()}, 0394 {QStringLiteral("height"), view->height()}, 0395 }); 0396 } 0397 0398 view->setSource(m_mainQmlPath); 0399 // on error, load the fallback lockscreen to not lock the user out of the system 0400 if (view->status() != QQmlComponent::Ready) { 0401 static const QUrl fallbackUrl(QUrl(QStringLiteral("qrc:/fallbacktheme/LockScreen.qml"))); 0402 0403 qCWarning(KSCREENLOCKER_GREET) << "Failed to load lockscreen QML, falling back to built-in locker"; 0404 for (const auto &error : view->errors()) { 0405 qCWarning(KSCREENLOCKER_GREET) << error; 0406 } 0407 0408 m_mainQmlPath = fallbackUrl; 0409 view->setSource(fallbackUrl); 0410 0411 if (view->status() != QQmlComponent::Ready) { 0412 qCWarning(KSCREENLOCKER_GREET) << "Failed to load the fallback lockscreen QML, something went really wrong! Terminating..."; 0413 for (const auto &error : view->errors()) { 0414 qCWarning(KSCREENLOCKER_GREET) << error; 0415 } 0416 std::terminate(); 0417 } 0418 } 0419 view->setResizeMode(PlasmaQuick::QuickViewSharedEngine::SizeRootObjectToView); 0420 0421 // we need to set this wallpaper properties separately after the lockscreen QML is loaded 0422 // this is because we need to anchor to the view that gets loaded 0423 setWallpaperItemProperties(wallpaperObj, view); 0424 0425 QQmlProperty lockProperty(view->rootObject(), QStringLiteral("locked")); 0426 lockProperty.write(m_immediateLock || (!m_noLock && !m_delayedLockTimer)); 0427 0428 QQmlProperty sleepProperty(view->rootObject(), QStringLiteral("suspendToRamSupported")); 0429 sleepProperty.write(PowerManagement::instance()->canSuspend()); 0430 if (view->rootObject() && view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToRam()").constData()) != -1) { 0431 connect(view->rootObject(), SIGNAL(suspendToRam()), SLOT(suspendToRam())); 0432 } 0433 0434 QQmlProperty hibernateProperty(view->rootObject(), QStringLiteral("suspendToDiskSupported")); 0435 hibernateProperty.write(PowerManagement::instance()->canHibernate()); 0436 if (view->rootObject() && view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToDisk()").constData()) != -1) { 0437 connect(view->rootObject(), SIGNAL(suspendToDisk()), SLOT(suspendToDisk())); 0438 } 0439 0440 // verify that the engine's controller didn't change 0441 Q_ASSERT(dynamic_cast<NoAccessNetworkAccessManagerFactory *>(view->engine()->networkAccessManagerFactory())); 0442 0443 if (KWindowSystem::isPlatformWayland()) { 0444 if (auto layerShellWindow = LayerShellQt::Window::get(view)) { 0445 layerShellWindow->setExclusiveZone(-1); 0446 layerShellWindow->setLayer(LayerShellQt::Window::LayerOverlay); 0447 layerShellWindow->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityExclusive); 0448 } 0449 } 0450 0451 // showFullScreen is implicit on X11 (through geometry and hints) and Wayland (layer-shell) 0452 view->show(); 0453 view->raise(); 0454 0455 auto onFrameSwapped = [this, view] { 0456 markViewsAsVisible(view); 0457 }; 0458 connect(view, &QQuickWindow::frameSwapped, this, onFrameSwapped, static_cast<Qt::ConnectionType>(Qt::QueuedConnection | Qt::SingleShotConnection)); 0459 0460 return view; 0461 } 0462 0463 void UnlockApp::markViewsAsVisible(PlasmaQuick::QuickViewSharedEngine *view) 0464 { 0465 QQmlProperty showProperty(view->rootObject(), QStringLiteral("viewVisible")); 0466 showProperty.write(true); 0467 // random state update, actually rather required on init only 0468 QMetaObject::invokeMethod(this, "getFocus", Qt::QueuedConnection); 0469 0470 auto mime1 = new QMimeData; 0471 // Effectively we want to clear the clipboard 0472 // however some clipboard managers (like klipper with it's default settings) 0473 // will prevent an empty clipboard 0474 // we need some non-empty non-text mimeData to replace the clipboard so we don't leak real data to a user pasting into the text field 0475 // as the clipboard is cleared on close, klipper will then put the original text back when we exit 0476 mime1->setData(QStringLiteral("x-kde-lockscreen"), QByteArrayLiteral("empty")); 0477 // ownership is transferred 0478 QGuiApplication::clipboard()->setMimeData(mime1, QClipboard::Clipboard); 0479 0480 auto mime2 = new QMimeData; 0481 mime2->setData(QStringLiteral("x-kde-lockscreen"), QByteArrayLiteral("empty")); 0482 QGuiApplication::clipboard()->setMimeData(mime2, QClipboard::Selection); 0483 } 0484 0485 void UnlockApp::getFocus() 0486 { 0487 QWindow *activeScreen = getActiveScreen(); 0488 0489 if (!activeScreen) { 0490 return; 0491 } 0492 // this loop is required to make the qml/graphicsscene properly handle the shared keyboard input 0493 // ie. "type something into the box of every greeter" 0494 for (PlasmaQuick::QuickViewSharedEngine *view : std::as_const(m_views)) { 0495 if (!m_testing) { 0496 view->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master! 0497 } 0498 } 0499 // activate window and grab input to be sure it really ends up there. 0500 // focus setting is still required for proper internal QWidget state (and eg. visual reflection) 0501 if (!m_testing) { 0502 activeScreen->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master! 0503 } 0504 activeScreen->requestActivate(); 0505 } 0506 0507 void UnlockApp::setLockedPropertyOnViews() 0508 { 0509 delete m_delayedLockTimer; 0510 m_delayedLockTimer = nullptr; 0511 0512 for (PlasmaQuick::QuickViewSharedEngine *view : std::as_const(m_views)) { 0513 QQmlProperty lockProperty(view->rootObject(), QStringLiteral("locked")); 0514 lockProperty.write(true); 0515 } 0516 } 0517 0518 void UnlockApp::resetRequestIgnore() 0519 { 0520 m_ignoreRequests = false; 0521 } 0522 0523 void UnlockApp::suspendToRam() 0524 { 0525 if (m_ignoreRequests) { 0526 return; 0527 } 0528 0529 m_ignoreRequests = true; 0530 m_resetRequestIgnoreTimer->start(); 0531 m_authenticators->cancel(); 0532 0533 PowerManagement::instance()->suspend(); 0534 } 0535 0536 void UnlockApp::suspendToDisk() 0537 { 0538 if (m_ignoreRequests) { 0539 return; 0540 } 0541 0542 m_ignoreRequests = true; 0543 m_resetRequestIgnoreTimer->start(); 0544 m_authenticators->cancel(); 0545 0546 PowerManagement::instance()->hibernate(); 0547 } 0548 0549 void UnlockApp::setTesting(bool enable) 0550 { 0551 m_testing = enable; 0552 if (m_views.isEmpty()) { 0553 return; 0554 } 0555 if (enable) { 0556 // remove bypass window manager hint 0557 for (PlasmaQuick::QuickViewSharedEngine *view : std::as_const(m_views)) { 0558 view->setFlags(view->flags() & ~Qt::X11BypassWindowManagerHint); 0559 } 0560 } else { 0561 for (PlasmaQuick::QuickViewSharedEngine *view : std::as_const(m_views)) { 0562 view->setFlags(view->flags() | Qt::X11BypassWindowManagerHint); 0563 } 0564 } 0565 } 0566 0567 void UnlockApp::setTheme(const QString &theme) 0568 { 0569 if (theme.isEmpty() || !m_testing) { 0570 return; 0571 } 0572 0573 m_packageName = theme; 0574 KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); 0575 package.setPath(m_packageName); 0576 0577 m_mainQmlPath = package.fileUrl("lockscreenmainscript"); 0578 } 0579 0580 void UnlockApp::setImmediateLock(bool immediate) 0581 { 0582 m_immediateLock = immediate; 0583 } 0584 0585 void UnlockApp::lockImmediately() 0586 { 0587 setImmediateLock(true); 0588 setLockedPropertyOnViews(); 0589 } 0590 0591 bool UnlockApp::eventFilter(QObject *obj, QEvent *event) 0592 { 0593 if (obj != this && event->type() == QEvent::Show) { 0594 PlasmaQuick::QuickViewSharedEngine *view = nullptr; 0595 for (PlasmaQuick::QuickViewSharedEngine *v : std::as_const(m_views)) { 0596 if (v == obj) { 0597 view = v; 0598 break; 0599 } 0600 } 0601 if (view && view->winId() && QX11Info::isPlatformX11()) { 0602 // showing greeter view window, set property 0603 static Atom tag = XInternAtom(QX11Info::display(), "_KDE_SCREEN_LOCKER", False); 0604 XChangeProperty(QX11Info::display(), view->winId(), tag, tag, 32, PropModeReplace, nullptr, 0); 0605 } 0606 // no further processing 0607 return false; 0608 } 0609 0610 if (event->type() == QEvent::MouseButtonPress && QX11Info::isPlatformX11()) { 0611 if (getActiveScreen()) { 0612 getActiveScreen()->requestActivate(); 0613 } 0614 return false; 0615 } 0616 0617 if (event->type() == QEvent::KeyPress) { // react if saver is visible 0618 shareEvent(event, qobject_cast<PlasmaQuick::QuickViewSharedEngine *>(obj)); 0619 return false; // we don't care 0620 } else if (event->type() == QEvent::KeyRelease) { // conditionally reshow the saver 0621 QKeyEvent *ke = static_cast<QKeyEvent *>(event); 0622 if (ke->key() != Qt::Key_Escape) { 0623 shareEvent(event, qobject_cast<PlasmaQuick::QuickViewSharedEngine *>(obj)); 0624 return false; // irrelevant 0625 } else { 0626 auto dpms = new KScreen::Dpms(this); 0627 if (dpms->isSupported()) { 0628 connect(dpms, &KScreen::Dpms::hasPendingChangesChanged, this, [dpms](bool hasPendingChanges) { 0629 if (!hasPendingChanges) { 0630 dpms->deleteLater(); 0631 } 0632 }); 0633 dpms->switchMode(KScreen::Dpms::Off); 0634 } else { 0635 dpms->deleteLater(); 0636 } 0637 } 0638 return true; // don't pass 0639 } 0640 0641 return false; 0642 } 0643 0644 /* 0645 * This function forwards an event from one greeter window to all others 0646 * It's used to have the keyboard operate on all greeter windows (on every screen) 0647 * at once so that the user gets visual feedback on the screen he's looking at - 0648 * even if the focus is actually on a powered off screen. 0649 */ 0650 0651 void UnlockApp::shareEvent(QEvent *e, PlasmaQuick::QuickViewSharedEngine *from) 0652 { 0653 // from can be NULL any time (because the parameter is passed as qobject_cast) 0654 // m_views.contains(from) is atm. supposed to be true but required if any further 0655 // QQuickView are added (which are not part of m_views) 0656 // this makes "from" an optimization (nullptr check aversion) 0657 if (from && m_views.contains(from)) { 0658 // NOTICE any recursion in the event sharing will prevent authentication on multiscreen setups! 0659 // Any change in regarded event processing shall be tested thoroughly! 0660 removeEventFilter(this); // prevent recursion! 0661 const bool accepted = e->isAccepted(); // store state 0662 for (PlasmaQuick::QuickViewSharedEngine *view : std::as_const(m_views)) { 0663 if (view != from) { 0664 QCoreApplication::sendEvent(view, e); 0665 e->setAccepted(accepted); 0666 } 0667 } 0668 installEventFilter(this); 0669 } 0670 } 0671 0672 void UnlockApp::setGraceTime(int milliseconds) 0673 { 0674 m_graceTime = milliseconds; 0675 if (milliseconds < 0 || m_delayedLockTimer || m_noLock || m_immediateLock) { 0676 return; 0677 } 0678 m_delayedLockTimer = new QTimer(this); 0679 m_delayedLockTimer->setSingleShot(true); 0680 connect(m_delayedLockTimer, &QTimer::timeout, this, &UnlockApp::setLockedPropertyOnViews); 0681 m_delayedLockTimer->start(m_graceTime); 0682 } 0683 0684 void UnlockApp::setNoLock(bool noLock) 0685 { 0686 m_noLock = noLock; 0687 } 0688 0689 void UnlockApp::setDefaultToSwitchUser(bool defaultToSwitchUser) 0690 { 0691 m_defaultToSwitchUser = defaultToSwitchUser; 0692 } 0693 0694 void UnlockApp::setKsldSocket(int socket) 0695 { 0696 m_display = wl_display_connect_to_fd(socket); 0697 auto socketnotifier = new QSocketNotifier(socket, QSocketNotifier::Read, this); 0698 connect(socketnotifier, &QSocketNotifier::activated, this, [this] { 0699 wl_display_dispatch(m_display); 0700 }); 0701 auto registry = wl_display_get_registry(m_display); 0702 auto globalAdded = [](void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { 0703 Q_UNUSED(version) 0704 if (interface != "org_kde_ksld"_ba) { 0705 return; 0706 } 0707 auto self = static_cast<UnlockApp *>(data); 0708 // bind version 1 as we dropped all the V2 features 0709 self->m_ksldInterface = static_cast<org_kde_ksld *>(wl_registry_bind(registry, name, &org_kde_ksld_interface, 1)); 0710 for (auto v : std::as_const(self->m_views)) { 0711 org_kde_ksld_x11window(self->m_ksldInterface, v->winId()); 0712 wl_display_flush(self->m_display); 0713 } 0714 }; 0715 auto noopGlobalRemove = [](void *, struct wl_registry *, uint32_t) {}; 0716 static const wl_registry_listener registryListener = wl_registry_listener{globalAdded, noopGlobalRemove}; 0717 wl_registry_add_listener(registry, ®istryListener, this); 0718 wl_display_flush(m_display); 0719 } 0720 0721 void UnlockApp::osdProgress(const QString &icon, int percent, int maximumPercent, const QString &additionalText) 0722 { 0723 for (auto v : std::as_const(m_views)) { 0724 auto osd = v->rootObject()->findChild<QQuickItem *>(QStringLiteral("onScreenDisplay")); 0725 if (!osd) { 0726 continue; 0727 } 0728 // Update max value first to prevent value from being clamped 0729 osd->setProperty("osdMaxValue", maximumPercent); 0730 osd->setProperty("osdValue", percent); 0731 osd->setProperty("osdAdditionalText", additionalText); 0732 osd->setProperty("showingProgress", true); 0733 osd->setProperty("icon", icon); 0734 QMetaObject::invokeMethod(osd, "show"); 0735 } 0736 } 0737 0738 void UnlockApp::osdText(const QString &icon, const QString &additionalText) 0739 { 0740 for (auto v : std::as_const(m_views)) { 0741 auto osd = v->rootObject()->findChild<QQuickItem *>(QStringLiteral("onScreenDisplay")); 0742 if (!osd) { 0743 continue; 0744 } 0745 osd->setProperty("showingProgress", false); 0746 osd->setProperty("osdValue", additionalText); 0747 osd->setProperty("icon", icon); 0748 QMetaObject::invokeMethod(osd, "show"); 0749 } 0750 } 0751 0752 void UnlockApp::updateCanSuspend() 0753 { 0754 for (auto it = m_views.constBegin(), end = m_views.constEnd(); it != end; ++it) { 0755 QQmlProperty sleepProperty((*it)->rootObject(), QStringLiteral("suspendToRamSupported")); 0756 sleepProperty.write(PowerManagement::instance()->canSuspend()); 0757 } 0758 } 0759 0760 void UnlockApp::updateCanHibernate() 0761 { 0762 for (auto it = m_views.constBegin(), end = m_views.constEnd(); it != end; ++it) { 0763 QQmlProperty hibernateProperty((*it)->rootObject(), QStringLiteral("suspendToDiskSupported")); 0764 hibernateProperty.write(PowerManagement::instance()->canHibernate()); 0765 } 0766 } 0767 0768 } // namespace 0769 0770 #include "greeterapp.moc"