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