diff --git a/greeter/greeterapp.h b/greeter/greeterapp.h --- a/greeter/greeterapp.h +++ b/greeter/greeterapp.h @@ -69,9 +69,11 @@ bool supportsSeccomp() const { return m_supportsSeccomp; } + void loadCurrentScreens(); public Q_SLOTS: - void desktopResized(); + void screenRemovedImpl(QScreen *screen); + void screenAddedImpl(QScreen *screen, bool shouldResizeDesktops = true); protected: bool eventFilter(QObject *obj, QEvent *event) override; diff --git a/greeter/greeterapp.cpp b/greeter/greeterapp.cpp --- a/greeter/greeterapp.cpp +++ b/greeter/greeterapp.cpp @@ -127,8 +127,8 @@ m_authenticator = createAuthenticator(); connect(m_authenticator, &Authenticator::succeeded, this, &QCoreApplication::quit); initialize(); - connect(this, &UnlockApp::screenAdded, this, &UnlockApp::desktopResized); - connect(this, &UnlockApp::screenRemoved, this, &UnlockApp::desktopResized); + connect(this, &UnlockApp::screenAdded, this, [this](QScreen *screen) { screenAddedImpl(screen, true); }); + connect(this, &UnlockApp::screenRemoved, this, [this](QScreen *screen) { screenRemovedImpl(screen); }); if (QX11Info::isPlatformX11()) { installNativeEventFilter(new FocusOutEventFilter); @@ -287,60 +287,73 @@ ); } -void UnlockApp::desktopResized() -{ - const int nScreens = screens().count(); - // remove useless views and savers - while (m_views.count() > nScreens) { - m_views.takeLast()->deleteLater(); +namespace { + static const char *screenNameProperty = "screenNameProperty"; + QScreen *screenFromName(const QList screens, const QString& name) + { + auto screenIt = std::find_if(std::begin(screens), std::end(screens), + [name](QScreen *screen) { return name == screen->name(); }); + + if (screenIt != std::end(screens)) { + return *screenIt; + } + return nullptr; } +} - // extend views and savers to current demand - for (int i = m_views.count(); i < nScreens; ++i) { - // create the view - auto *view = new KQuickAddons::QuickViewSharedEngine(); - view->setColor(Qt::black); - auto screen = QGuiApplication::screens()[i]; - view->setGeometry(screen->geometry()); +void UnlockApp::screenRemovedImpl(QScreen *removed) +{ + auto viewIt = std::find_if(std::begin(m_views), std::end(m_views), + [removed](KQuickAddons::QuickViewSharedEngine *view) { + const QString viewName = view->property(screenNameProperty).toString(); + return removed->name() == viewName; + }); - // first create KDeclarative, to be sure that it created a KIO Network Factory - KDeclarative::KDeclarative declarative; - declarative.setDeclarativeEngine(view->engine()); - declarative.setupBindings(); + Q_ASSERT(viewIt != std::end(m_views)); + auto view = *viewIt; + m_views.removeOne(view); + view->deleteLater(); +} + +void UnlockApp::screenAddedImpl(QScreen *added, bool shouldResizeDesktops) +{ + // Those are a bunch of helper functions that I think it's better to be here than + // on the class definition, as tey are really internal to this function. + auto setupContext = [this]( KQuickAddons::QuickViewSharedEngine *view) { + auto context = view->engine()->rootContext(); + context->setContextProperty(QStringLiteral("kscreenlocker_userName"), m_userName); + context->setContextProperty(QStringLiteral("kscreenlocker_userImage"), m_userImage); + context->setContextProperty(QStringLiteral("authenticator"), m_authenticator); + context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_interfaceVersion"), 2); + context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_view"), view); + context->setContextProperty(QStringLiteral("defaultToSwitchUser"), m_defaultToSwitchUser); + context->setContextProperty(QStringLiteral("config"), m_lnfIntegration->configuration()); + }; + auto createView = [this, added] () -> KQuickAddons::QuickViewSharedEngine* { + auto *view = new KQuickAddons::QuickViewSharedEngine(); + view->setColor(Qt::black); + view->setProperty(screenNameProperty, added->name()); + view->setGeometry(added->geometry()); if (!m_testing) { if (QX11Info::isPlatformX11()) { view->setFlags(Qt::X11BypassWindowManagerHint); } else { view->setFlags(Qt::FramelessWindowHint); } } - if (m_ksldInterface) { - view->create(); - org_kde_ksld_x11window(m_ksldInterface, view->winId()); - wl_display_flush(m_ksldConnection->display()); - } - - if (m_plasmaShell) { - using namespace KWayland::Client; - if (Surface *surface = Surface::fromWindow(view)) { - PlasmaShellSurface *shellSurface = m_plasmaShell->createSurface(surface, view); - view->setProperty("plasmaShellSurface", QVariant::fromValue(shellSurface)); - } - } - - // engine stuff - QQmlContext* context = view->engine()->rootContext(); + return view; + }; - context->setContextProperty(QStringLiteral("kscreenlocker_userName"), m_userName); - context->setContextProperty(QStringLiteral("kscreenlocker_userImage"), m_userImage); - context->setContextProperty(QStringLiteral("authenticator"), m_authenticator); - context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_interfaceVersion"), 2); - context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_view"), view); - context->setContextProperty(QStringLiteral("defaultToSwitchUser"), m_defaultToSwitchUser); - context->setContextProperty(QStringLiteral("config"), m_lnfIntegration->configuration()); + auto overrideFactory = [](KQuickAddons::QuickViewSharedEngine *view) { + auto oldFactory = view->engine()->networkAccessManagerFactory(); + view->engine()->setNetworkAccessManagerFactory(nullptr); + delete oldFactory; + view->engine()->setNetworkAccessManagerFactory(new NoAccessNetworkAccessManagerFactory); + }; + auto setSourceLocker = [this](KQuickAddons::QuickViewSharedEngine *view) { view->setSource(m_mainQmlPath); // on error, load the fallback lockscreen to not lock the user out of the system if (view->status() != QQmlComponent::Ready) { @@ -350,15 +363,9 @@ m_mainQmlPath = fallbackUrl; view->setSource(fallbackUrl); } - view->setResizeMode(KQuickAddons::QuickViewSharedEngine::SizeRootObjectToView); - - loadWallpaperPlugin(view); - // overwrite the factory set by kdeclarative - auto oldFactory = view->engine()->networkAccessManagerFactory(); - view->engine()->setNetworkAccessManagerFactory(nullptr); - delete oldFactory; - view->engine()->setNetworkAccessManagerFactory(new NoAccessNetworkAccessManagerFactory); + }; + auto setupQmlProperties = [this](KQuickAddons::QuickViewSharedEngine *view) { QQmlProperty lockProperty(view->rootObject(), QStringLiteral("locked")); lockProperty.write(m_immediateLock || (!m_noLock && !m_delayedLockTimer)); @@ -373,53 +380,105 @@ if (view->rootObject() && view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToDisk()").constData()) != -1) { connect(view->rootObject(), SIGNAL(suspendToDisk()), SLOT(suspendToDisk())); } + }; - // verify that the engine's controller didn't change - Q_ASSERT(dynamic_cast(view->engine()->networkAccessManagerFactory())); + auto showView = [this](KQuickAddons::QuickViewSharedEngine *view, KWayland::Client::PlasmaShellSurface *plasmaSurface) { + // on Wayland we may not use fullscreen as that puts all windows on one screen + if (m_testing || plasmaSurface || QX11Info::isPlatformX11()) { + view->show(); + } else { + view->showFullScreen(); + } + view->raise(); + }; - m_views << view; - } + auto handleKsldInterface = [this](KQuickAddons::QuickViewSharedEngine *view) { + if (m_ksldInterface) { + view->create(); + org_kde_ksld_x11window(m_ksldInterface, view->winId()); + wl_display_flush(m_ksldConnection->display()); + } + }; - // update geometry of all views and savers - for (int i = 0; i < nScreens; ++i) { - auto *view = m_views.at(i); + auto setPlasmaShell = [this](KQuickAddons::QuickViewSharedEngine *view) { + if (!m_plasmaShell) { + return; + } + using namespace KWayland::Client; + if (Surface *surface = Surface::fromWindow(view)) { + PlasmaShellSurface *shellSurface = m_plasmaShell->createSurface(surface, view); + view->setProperty("plasmaShellSurface", QVariant::fromValue(shellSurface)); + } + }; - auto screen = QGuiApplication::screens()[i]; - view->setGeometry(screen->geometry()); - KWayland::Client::PlasmaShellSurface *plasmaSurface = view->property("plasmaShellSurface").value(); - if (plasmaSurface) { - plasmaSurface->setPosition(view->geometry().topLeft()); + auto setGeometry = [added](KQuickAddons::QuickViewSharedEngine *view, KWayland::Client::PlasmaShellSurface *surface) { + view->setGeometry(added->geometry()); + if (surface) { + surface->setPosition(view->geometry().topLeft()); } + }; + + auto finishInitialization = [](KQuickAddons::QuickViewSharedEngine * view) { + //initialize with our size to avoid as much resize events as possible if (auto object = view->property("wallpaperGraphicsObject").value()) { - //initialize with our size to avoid as much resize events as possible object->completeInitialization({ {QStringLiteral("width"), view->width()}, {QStringLiteral("height"), view->height()} }); } - - connect(screen, - &QScreen::geometryChanged, - view, - [view, plasmaSurface](const QRect &geo) { - view->setGeometry(geo); - if (plasmaSurface) { - plasmaSurface->setPosition(view->geometry().topLeft()); - } + }; + + auto handleConnections = [this, added](KQuickAddons::QuickViewSharedEngine *view, KWayland::Client::PlasmaShellSurface *surface) { + connect(added, &QScreen::geometryChanged, + view, [view, surface](const QRect &geo) { + view->setGeometry(geo); + if (surface) { + surface->setPosition(view->geometry().topLeft()); } + } ); + connect(view, &QQuickWindow::frameSwapped, this, + [this, view] {markViewsAsVisible(view); }, Qt::QueuedConnection); + }; - // on Wayland we may not use fullscreen as that puts all windows on one screen - if (m_testing || plasmaSurface || QX11Info::isPlatformX11()) { - view->show(); - } else { - view->showFullScreen(); - } - view->raise(); + // Begin of the Function. + auto *view = createView(); - connect(view, &QQuickWindow::frameSwapped, this, [this, view] { - markViewsAsVisible(view); - }, Qt::QueuedConnection); + // first create KDeclarative, to be sure that it created a KIO Network Factory + KDeclarative::KDeclarative declarative; + declarative.setDeclarativeEngine(view->engine()); + declarative.setupBindings(); + + handleKsldInterface(view); + setPlasmaShell(view); + + // engine stuff + setupContext(view); + setSourceLocker(view); + + view->setResizeMode(KQuickAddons::QuickViewSharedEngine::SizeRootObjectToView); + + loadWallpaperPlugin(view); + // overwrite the factory set by kdeclarative + overrideFactory(view); + setupQmlProperties(view); + + // verify that the engine's controller didn't change + Q_ASSERT(dynamic_cast(view->engine()->networkAccessManagerFactory())); + m_views << view; + + auto *plasmaSurface = view->property("plasmaShellSurface").value(); + setGeometry(view, plasmaSurface); + finishInitialization(view); + handleConnections(view, plasmaSurface); + showView(view, plasmaSurface); +} + + +void UnlockApp::loadCurrentScreens() +{ + for (auto *screen : screens()) { + UnlockApp::screenAddedImpl(screen, false); } } diff --git a/greeter/main.cpp b/greeter/main.cpp --- a/greeter/main.cpp +++ b/greeter/main.cpp @@ -184,7 +184,7 @@ } #endif - app.desktopResized(); + app.loadCurrentScreens(); // This allow ksmserver to know when the application has actually finished setting itself up. // Crucial for blocking until it is ready, ensuring locking happens before sleep, e.g.