diff --git a/greeter/CMakeLists.txt b/greeter/CMakeLists.txt index 49836b8..d8733ae 100644 --- a/greeter/CMakeLists.txt +++ b/greeter/CMakeLists.txt @@ -1,56 +1,57 @@ remove_definitions(-DTRANSLATION_DOMAIN=\"kscreenlocker\") add_definitions(-DTRANSLATION_DOMAIN=\"kscreenlocker_greet\") include_directories( ${CMAKE_CURRENT_BINARY_DIR} ../kcheckpass ${CMAKE_CURRENT_BINARY_DIR}/../ ) set(kscreenlocker_greet_SRCS authenticator.cpp greeterapp.cpp main.cpp noaccessnetworkaccessmanagerfactory.cpp + lnf_integration.cpp wallpaper_integration.cpp kwinglplatform.cpp ) if(HAVE_SECCOMP) set(kscreenlocker_greet_SRCS ${kscreenlocker_greet_SRCS} seccomp_filter.cpp) endif() qt5_add_resources(kscreenlocker_greet_SRCS fallbacktheme.qrc) kconfig_add_kcfg_files(kscreenlocker_greet_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../kcfg/kscreensaversettings.kcfgc) ecm_add_wayland_client_protocol(kscreenlocker_greet_SRCS PROTOCOL ../protocols/ksld.xml BASENAME ksld ) add_executable(kscreenlocker_greet ${kscreenlocker_greet_SRCS}) target_link_libraries(kscreenlocker_greet KF5::Package KF5::Crash KF5::I18n KF5::ConfigGui KF5::Declarative KF5::QuickAddons KF5::WindowSystem Qt5::Quick Qt5::Qml Qt5::X11Extras ${X11_LIBRARIES} KF5::WaylandClient Wayland::Client ) if(HAVE_SECCOMP) target_link_libraries(kscreenlocker_greet Qt5::DBus Seccomp::Seccomp) endif() install(TARGETS kscreenlocker_greet DESTINATION ${KDE_INSTALL_LIBEXECDIR}) install(DIRECTORY themes/org.kde.passworddialog DESTINATION ${KDE_INSTALL_DATADIR}/ksmserver/screenlocker) add_subdirectory(autotests) diff --git a/greeter/greeterapp.cpp b/greeter/greeterapp.cpp index fd8bcb2..606de86 100644 --- a/greeter/greeterapp.cpp +++ b/greeter/greeterapp.cpp @@ -1,733 +1,743 @@ /******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 2004 Chris Howells Copyright (C) 2011 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "greeterapp.h" #include "kscreensaversettings.h" #include "authenticator.h" #include "noaccessnetworkaccessmanagerfactory.h" #include "wallpaper_integration.h" +#include "lnf_integration.h" + #include // KDE #include #include #include #include #include +#include + #include //Plasma #include #include #include // KWayland #include #include #include #include #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include // Wayland #include #include // X11 #include #include #include // #include #if HAVE_SECCOMP #include #include #endif // this is usable to fake a "screensaver" installation for testing // *must* be "0" for every public commit! #define TEST_SCREENSAVER 0 namespace ScreenLocker { class FocusOutEventFilter : public QAbstractNativeEventFilter { public: bool nativeEventFilter(const QByteArray &eventType, void *message, long int *result) override { Q_UNUSED(result) if (qstrcmp(eventType, "xcb_generic_event_t") != 0) { return false; } xcb_generic_event_t *event = reinterpret_cast(message); if ((event->response_type & ~0x80) == XCB_FOCUS_OUT) { return true; } return false; } }; // App UnlockApp::UnlockApp(int &argc, char **argv) : QGuiApplication(argc, argv) , m_resetRequestIgnoreTimer(new QTimer(this)) , m_delayedLockTimer(0) , m_testing(false) , m_ignoreRequests(false) , m_immediateLock(false) , m_graceTime(0) , m_noLock(false) , m_defaultToSwitchUser(false) , m_wallpaperIntegration(new WallpaperIntegration(this)) + , m_lnfIntegration(new LnFIntegration(this)) { 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); if (QX11Info::isPlatformX11()) { installNativeEventFilter(new FocusOutEventFilter); } } UnlockApp::~UnlockApp() { //workaround QTBUG-55460 //will be fixed when themes port to QQC2 for (auto view: m_views) { if (QQuickItem *focusItem = view->activeFocusItem()) { focusItem->setFocus(false); } } qDeleteAll(m_views); if (m_ksldInterface) { org_kde_ksld_destroy(m_ksldInterface); } if (m_ksldRegistry) { delete m_ksldRegistry; } if (m_ksldConnection) { m_ksldConnection->deleteLater(); m_ksldConnectionThread->quit(); m_ksldConnectionThread->wait(); } } Authenticator *UnlockApp::createAuthenticator() { #if HAVE_SECCOMP struct stat buf; stat(KCHECKPASS_BIN, &buf); if (!(buf.st_mode & S_ISUID)) { m_supportsSeccomp = true; return new Authenticator(AuthenticationMode::Delayed, this); } #endif return new Authenticator(AuthenticationMode::Direct, this); } void UnlockApp::initialize() { initializeWayland(); // set up the request ignore timeout, so that multiple requests to sleep/suspend/shutdown // are not processed in quick (and confusing) succession) m_resetRequestIgnoreTimer->setSingleShot(true); m_resetRequestIgnoreTimer->setInterval(2000); connect(m_resetRequestIgnoreTimer, &QTimer::timeout, this, &UnlockApp::resetRequestIgnore); // disable DrKonqi as the crash dialog blocks the restart of the locker KCrash::setDrKonqiEnabled(false); KScreenSaverSettings::self()->load(); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); m_packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!m_packageName.isEmpty()) { package.setPath(m_packageName); } if (!KScreenSaverSettings::theme().isEmpty()) { package.setPath(KScreenSaverSettings::theme()); } m_mainQmlPath = package.fileUrl("lockscreenmainscript"); m_wallpaperIntegration->setConfig(KScreenSaverSettings::self()->sharedConfig()); m_wallpaperIntegration->setPluginName(KScreenSaverSettings::self()->wallpaperPlugin()); m_wallpaperIntegration->init(); + m_lnfIntegration->setPackage(package); + m_lnfIntegration->setConfig(KScreenSaverSettings::self()->sharedConfig()); + m_lnfIntegration->init(); + + installEventFilter(this); } void UnlockApp::initializeWayland() { if (!platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) { return; } using namespace KWayland::Client; auto *c = ConnectionThread::fromApplication(this); if (!c) { return; } Registry *r = new Registry(this); r->create(c); r->setup(); c->roundtrip(); const auto i = r->interface(Registry::Interface::PlasmaShell); if (i.name == 0) { return; } m_plasmaShell = r->createPlasmaShell(i.name, i.version, this); } void UnlockApp::loadWallpaperPlugin(KQuickAddons::QuickViewSharedEngine *view) { auto package = m_wallpaperIntegration->package(); if (!package.isValid()) { qWarning() << "Error loading the wallpaper, no valid package loaded"; return; } auto qmlObject = new KDeclarative::QmlObjectSharedEngine(view); qmlObject->setInitializationDelayed(true); qmlObject->setPackage(package); qmlObject->rootContext()->setContextProperty(QStringLiteral("wallpaper"), m_wallpaperIntegration); view->setProperty("wallpaperGraphicsObject", QVariant::fromValue(qmlObject)); connect(qmlObject, &KDeclarative::QmlObject::finished, this, [this, qmlObject, view] { auto item = qobject_cast(qmlObject->rootObject()); if (!item) { qWarning() << "Wallpaper needs to be a QtQuick Item"; return; } item->setParentItem(view->rootObject()); item->setZ(-1000); //set anchors QQmlExpression expr(qmlObject->engine()->rootContext(), item, QStringLiteral("parent")); QQmlProperty prop(item, QStringLiteral("anchors.fill")); prop.write(expr.evaluate()); view->rootContext()->setContextProperty(QStringLiteral("wallpaper"), item); } ); } void UnlockApp::desktopResized() { const int nScreens = screens().count(); // remove useless views and savers while (m_views.count() > nScreens) { m_views.takeLast()->deleteLater(); } // 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); // first create KDeclarative, to be sure that it created a KIO Network Factory KDeclarative::KDeclarative declarative; declarative.setDeclarativeEngine(view->engine()); declarative.setupBindings(); 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(); const KUser user; const QString fullName = user.property(KUser::FullName).toString(); context->setContextProperty(QStringLiteral("kscreenlocker_userName"), fullName.isEmpty() ? user.loginName() : fullName); context->setContextProperty(QStringLiteral("kscreenlocker_userImage"), user.faceIconPath()); 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()); view->setSource(m_mainQmlPath); // on error, load the fallback lockscreen to not lock the user out of the system if (view->status() == QQmlComponent::Error) { static const QUrl fallbackUrl(QUrl(QStringLiteral("qrc:/fallbacktheme/LockScreen.qml"))); qWarning() << "Failed to load lockscreen QML, falling back to built-in locker"; 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); QQmlProperty lockProperty(view->rootObject(), QStringLiteral("locked")); lockProperty.write(m_immediateLock || (!m_noLock && !m_delayedLockTimer)); QQmlProperty sleepProperty(view->rootObject(), QStringLiteral("suspendToRamSupported")); sleepProperty.write(m_canSuspend); if (view->rootObject() && view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToRam()").constData()) != -1) { connect(view->rootObject(), SIGNAL(suspendToRam()), SLOT(suspendToRam())); } QQmlProperty hibernateProperty(view->rootObject(), QStringLiteral("suspendToDiskSupported")); hibernateProperty.write(m_canHibernate); 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())); m_views << view; } // update geometry of all views and savers for (int i = 0; i < nScreens; ++i) { auto *view = m_views.at(i); auto screen = QGuiApplication::screens()[i]; view->setGeometry(screen->geometry()); KWayland::Client::PlasmaShellSurface *plasmaSurface = view->property("plasmaShellSurface").value(); if (plasmaSurface) { plasmaSurface->setPosition(view->geometry().topLeft()); } 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()); } } ); if (m_testing) { view->show(); } else { // on Wayland we may not use fullscreen as that puts all windows on one screen if (plasmaSurface) { view->show(); } else { view->showFullScreen(); } } view->raise(); connect(view, &QQuickWindow::frameSwapped, this, [this, view] { markViewsAsVisible(view); }, Qt::QueuedConnection); } } void UnlockApp::markViewsAsVisible(KQuickAddons::QuickViewSharedEngine *view) { disconnect(view, &QQuickWindow::frameSwapped, this, 0); QQmlProperty showProperty(view->rootObject(), QStringLiteral("viewVisible")); showProperty.write(true); // random state update, actually rather required on init only QMetaObject::invokeMethod(this, "getFocus", Qt::QueuedConnection); QGuiApplication::clipboard()->clear(); QGuiApplication::clipboard()->clear(QClipboard::Selection); } void UnlockApp::getFocus() { if (m_views.isEmpty()) { return; } QWindow *w = 0; // this loop is required to make the qml/graphicsscene properly handle the shared keyboard input // ie. "type something into the box of every greeter" foreach (KQuickAddons::QuickViewSharedEngine *view, m_views) { if (!m_testing) { view->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master! } // w->setFocus(Qt::OtherFocusReason); // FIXME } // determine which window should actually be active and have the real input focus/grab // FIXME - QWidget::underMouse() // foreach (QQuickView *view, m_views) { // if (view->underMouse()) { // w = view; // break; // } // } if (!w) { // try harder foreach (KQuickAddons::QuickViewSharedEngine *view, m_views) { if (view->geometry().contains(QCursor::pos())) { w = view; break; } } } if (!w) { // fallback solution w = m_views.first(); } // activate window and grab input to be sure it really ends up there. // focus setting is still required for proper internal QWidget state (and eg. visual reflection) if (!m_testing) { w->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master! } w->requestActivate(); // w->setFocus(Qt::OtherFocusReason); // FIXME } void UnlockApp::setLockedPropertyOnViews() { delete m_delayedLockTimer; m_delayedLockTimer = 0; foreach (KQuickAddons::QuickViewSharedEngine *view, m_views) { QQmlProperty lockProperty(view->rootObject(), QStringLiteral("locked")); lockProperty.write(true); } } void UnlockApp::resetRequestIgnore() { m_ignoreRequests = false; } void UnlockApp::suspendToRam() { if (m_ignoreRequests) { return; } m_ignoreRequests = true; m_resetRequestIgnoreTimer->start(); org_kde_ksld_suspendSystem(m_ksldInterface); } void UnlockApp::suspendToDisk() { if (m_ignoreRequests) { return; } m_ignoreRequests = true; m_resetRequestIgnoreTimer->start(); org_kde_ksld_hibernateSystem(m_ksldInterface); } void UnlockApp::setTesting(bool enable) { m_testing = enable; if (m_views.isEmpty()) { return; } if (enable) { // remove bypass window manager hint foreach (KQuickAddons::QuickViewSharedEngine * view, m_views) { view->setFlags(view->flags() & ~Qt::X11BypassWindowManagerHint); } } else { foreach (KQuickAddons::QuickViewSharedEngine * view, m_views) { view->setFlags(view->flags() | Qt::X11BypassWindowManagerHint); } } } void UnlockApp::setTheme(const QString &theme) { if (theme.isEmpty() || !m_testing) { return; } m_packageName = theme; KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); package.setPath(m_packageName); m_mainQmlPath = package.fileUrl("lockscreenmainscript"); } void UnlockApp::setImmediateLock(bool immediate) { m_immediateLock = immediate; } void UnlockApp::lockImmediately() { setImmediateLock(true); setLockedPropertyOnViews(); } bool UnlockApp::eventFilter(QObject *obj, QEvent *event) { if (obj != this && event->type() == QEvent::Show) { KQuickAddons::QuickViewSharedEngine *view(0); foreach (KQuickAddons::QuickViewSharedEngine *v, m_views) { if (v == obj) { view = v; break; } } if (view && view->winId() && QX11Info::isPlatformX11()) { // showing greeter view window, set property static Atom tag = XInternAtom(QX11Info::display(), "_KDE_SCREEN_LOCKER", False); XChangeProperty(QX11Info::display(), view->winId(), tag, tag, 32, PropModeReplace, 0, 0); } // no further processing return false; } if (event->type() == QEvent::KeyPress) { // react if saver is visible shareEvent(event, qobject_cast(obj)); return false; // we don't care } else if (event->type() == QEvent::KeyRelease) { // conditionally reshow the saver QKeyEvent *ke = static_cast(event); if (ke->key() != Qt::Key_Escape) { shareEvent(event, qobject_cast(obj)); return false; // irrelevant } return true; // don't pass } return false; } /* * This function forwards an event from one greeter window to all others * It's used to have the keyboard operate on all greeter windows (on every screen) * at once so that the user gets visual feedback on the screen he's looking at - * even if the focus is actually on a powered off screen. */ void UnlockApp::shareEvent(QEvent *e, KQuickAddons::QuickViewSharedEngine *from) { // from can be NULL any time (because the parameter is passed as qobject_cast) // m_views.contains(from) is atm. supposed to be true but required if any further // QQuickView are added (which are not part of m_views) // this makes "from" an optimization (nullptr check aversion) if (from && m_views.contains(from)) { // NOTICE any recursion in the event sharing will prevent authentication on multiscreen setups! // Any change in regarded event processing shall be tested thoroughly! removeEventFilter(this); // prevent recursion! const bool accepted = e->isAccepted(); // store state foreach (KQuickAddons::QuickViewSharedEngine *view, m_views) { if (view != from) { QCoreApplication::sendEvent(view, e); e->setAccepted(accepted); } } installEventFilter(this); } } void UnlockApp::setGraceTime(int milliseconds) { m_graceTime = milliseconds; if (milliseconds < 0 || m_delayedLockTimer || m_noLock || m_immediateLock) { return; } m_delayedLockTimer = new QTimer(this); m_delayedLockTimer->setSingleShot(true); connect(m_delayedLockTimer, &QTimer::timeout, this, &UnlockApp::setLockedPropertyOnViews); m_delayedLockTimer->start(m_graceTime); } void UnlockApp::setNoLock(bool noLock) { m_noLock = noLock; } void UnlockApp::setDefaultToSwitchUser(bool defaultToSwitchUser) { m_defaultToSwitchUser = defaultToSwitchUser; } static void osdProgress(void *data, org_kde_ksld *org_kde_ksld, const char *icon, int32_t percent, const char *text) { Q_UNUSED(org_kde_ksld) reinterpret_cast(data)->osdProgress(QString::fromUtf8(icon), percent, QString::fromUtf8(text)); } static void osdText(void *data, org_kde_ksld *org_kde_ksld, const char *icon, const char *text) { Q_UNUSED(org_kde_ksld) reinterpret_cast(data)->osdText(QString::fromUtf8(icon), QString::fromUtf8(text)); } static void canSuspend(void *data, org_kde_ksld *org_kde_ksld, uint suspend) { Q_UNUSED(org_kde_ksld) reinterpret_cast(data)->updateCanSuspend(suspend); } static void canHibernate(void *data, org_kde_ksld *org_kde_ksld, uint hibernate) { Q_UNUSED(org_kde_ksld) reinterpret_cast(data)->updateCanHibernate(hibernate); } static const struct org_kde_ksld_listener s_listener { osdProgress, osdText, canSuspend, canHibernate }; void UnlockApp::setKsldSocket(int socket) { using namespace KWayland::Client; m_ksldConnection = new ConnectionThread; m_ksldConnection->setSocketFd(socket); m_ksldRegistry = new Registry(); EventQueue *queue = new EventQueue(m_ksldRegistry); connect(m_ksldRegistry, &Registry::interfaceAnnounced, this, [this, queue] (QByteArray interface, quint32 name, quint32 version) { if (interface != QByteArrayLiteral("org_kde_ksld")) { return; } m_ksldInterface = reinterpret_cast(wl_registry_bind(*m_ksldRegistry, name, &org_kde_ksld_interface, version)); queue->addProxy(m_ksldInterface); if (version >= 2) { org_kde_ksld_add_listener(m_ksldInterface, &s_listener, this); } for (auto v : m_views) { org_kde_ksld_x11window(m_ksldInterface, v->winId()); wl_display_flush(m_ksldConnection->display()); } } ); connect(m_ksldConnection, &ConnectionThread::connected, this, [this, queue] { m_ksldRegistry->create(m_ksldConnection); queue->setup(m_ksldConnection); m_ksldRegistry->setEventQueue(queue); m_ksldRegistry->setup(); wl_display_flush(m_ksldConnection->display()); }, Qt::QueuedConnection); m_ksldConnectionThread = new QThread(this); m_ksldConnection->moveToThread(m_ksldConnectionThread); m_ksldConnectionThread->start(); m_ksldConnection->initConnection(); } void UnlockApp::osdProgress(const QString &icon, int percent, const QString &additionalText) { for (auto v : m_views) { auto osd = v->rootObject()->findChild(QStringLiteral("onScreenDisplay")); if (!osd) { continue; } osd->setProperty("osdValue", percent); osd->setProperty("osdAdditionalText", additionalText); osd->setProperty("showingProgress", true); osd->setProperty("icon", icon); QMetaObject::invokeMethod(osd, "show"); } } void UnlockApp::osdText(const QString &icon, const QString &additionalText) { for (auto v : m_views) { auto osd = v->rootObject()->findChild(QStringLiteral("onScreenDisplay")); if (!osd) { continue; } osd->setProperty("showingProgress", false); osd->setProperty("osdValue", additionalText); osd->setProperty("icon", icon); QMetaObject::invokeMethod(osd, "show"); } } void UnlockApp::updateCanSuspend(bool set) { if (m_canSuspend == set) { return; } m_canSuspend = set; for (auto it = m_views.constBegin(), end = m_views.constEnd(); it != end; ++it) { QQmlProperty sleepProperty((*it)->rootObject(), QStringLiteral("suspendToRamSupported")); sleepProperty.write(m_canSuspend); } } void UnlockApp::updateCanHibernate(bool set) { if (m_canHibernate == set) { return; } m_canHibernate = set; for (auto it = m_views.constBegin(), end = m_views.constEnd(); it != end; ++it) { QQmlProperty hibernateProperty((*it)->rootObject(), QStringLiteral("suspendToDiskSupported")); hibernateProperty.write(m_canHibernate); } } } // namespace diff --git a/greeter/greeterapp.h b/greeter/greeterapp.h index 5b70015..68cd019 100644 --- a/greeter/greeterapp.h +++ b/greeter/greeterapp.h @@ -1,124 +1,127 @@ /******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 2011 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef SCREENLOCKER_GREETERAPP_H #define SCREENLOCKER_GREETERAPP_H #include #include #include namespace KWayland { namespace Client { class ConnectionThread; class Registry; class PlasmaShell; } } namespace KQuickAddons { class QuickViewSharedEngine; } class Authenticator; struct org_kde_ksld; namespace ScreenLocker { class Unlocker; class WallpaperIntegration; +class LnFIntegration; class UnlockApp : public QGuiApplication { Q_OBJECT public: explicit UnlockApp(int &argc, char **argv); virtual ~UnlockApp(); void setTesting(bool enable); void setTheme(const QString &theme); void setImmediateLock(bool immediateLock); void lockImmediately(); void setGraceTime(int milliseconds); void setNoLock(bool noLock); void setKsldSocket(int socket); void setDefaultToSwitchUser(bool defaultToSwitchUser); void osdProgress(const QString &icon, int percent, const QString &additionalText); void osdText(const QString &icon, const QString &additionalText); void updateCanSuspend(bool set); void updateCanHibernate(bool set); bool supportsSeccomp() const { return m_supportsSeccomp; } public Q_SLOTS: void desktopResized(); protected: bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; private Q_SLOTS: void resetRequestIgnore(); void suspendToRam(); void suspendToDisk(); void getFocus(); void markViewsAsVisible(KQuickAddons::QuickViewSharedEngine *view); void setLockedPropertyOnViews(); private: void initialize(); void initializeWayland(); void shareEvent(QEvent *e, KQuickAddons::QuickViewSharedEngine *from); void loadWallpaperPlugin(KQuickAddons::QuickViewSharedEngine *view); Authenticator *createAuthenticator(); QString m_packageName; QUrl m_mainQmlPath; QList m_views; QTimer *m_resetRequestIgnoreTimer; QTimer *m_delayedLockTimer; KPackage::Package m_package; bool m_testing; bool m_ignoreRequests; bool m_immediateLock; bool m_runtimeInitialized; Authenticator *m_authenticator; int m_graceTime; bool m_noLock; bool m_defaultToSwitchUser; bool m_canSuspend = false; bool m_canHibernate = false; KWayland::Client::ConnectionThread *m_ksldConnection = nullptr; KWayland::Client::Registry *m_ksldRegistry = nullptr; QThread *m_ksldConnectionThread = nullptr; org_kde_ksld *m_ksldInterface = nullptr; KWayland::Client::PlasmaShell *m_plasmaShell = nullptr; WallpaperIntegration *m_wallpaperIntegration; + LnFIntegration *m_lnfIntegration; + bool m_supportsSeccomp = false; }; } // namespace #endif // SCREENLOCKER_GREETERAPP_H diff --git a/greeter/lnf_integration.cpp b/greeter/lnf_integration.cpp new file mode 100644 index 0000000..a6b9cde --- /dev/null +++ b/greeter/lnf_integration.cpp @@ -0,0 +1,74 @@ +/******************************************************************** + KSld - the KDE Screenlocker Daemon + This file is part of the KDE project. + +Copyright (C) 2016 Martin Gräßlin +Copyright (C) 2017 David Edmundson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License or (at your option) version 3 or any later version +accepted by the membership of KDE e.V. (or its successor approved +by the membership of KDE e.V.), which shall act as a proxy +defined in Section 14 of version 3 of the license. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "lnf_integration.h" + +#include +#include +#include +#include +#include + +#include +#include + +namespace ScreenLocker +{ + +LnFIntegration::LnFIntegration(QObject *parent) + : QObject(parent) +{ + qRegisterMetaType(); +} + +LnFIntegration::~LnFIntegration() = default; + +void LnFIntegration::init() +{ + if (!m_package.isValid()) { + return; + } + if (auto config = configScheme()) { + m_configuration = new KDeclarative::ConfigPropertyMap(config, this); + } +} + + +KConfigLoader *LnFIntegration::configScheme() +{ + if (!m_configLoader) { + const QString xmlPath = m_package.filePath(QByteArrayLiteral("lockscreen"), QStringLiteral("config.xml")); + + const KConfigGroup cfg = m_config->group("Greeter").group("LnF"); + + if (xmlPath.isEmpty()) { + m_configLoader = new KConfigLoader(cfg, 0, this); + } else { + QFile file(xmlPath); + m_configLoader = new KConfigLoader(cfg, &file, this); + } + } + return m_configLoader; +} + +} diff --git a/greeter/lnf_integration.h b/greeter/lnf_integration.h new file mode 100644 index 0000000..838ca4f --- /dev/null +++ b/greeter/lnf_integration.h @@ -0,0 +1,83 @@ +/******************************************************************** + KSld - the KDE Screenlocker Daemon + This file is part of the KDE project. + +Copyright (C) 2016 Martin Gräßlin +Copyright (C) 2017 David Edmundson + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License or (at your option) version 3 or any later version +accepted by the membership of KDE e.V. (or its successor approved +by the membership of KDE e.V.), which shall act as a proxy +defined in Section 14 of version 3 of the license. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KSCREENLOCKER_LNF_INTEGRATION_H +#define KSCREENLOCKER_LNF_INTEGRATION_H + +#include +#include + +class KConfigLoader; + +namespace KDeclarative +{ +class ConfigPropertyMap; +class QmlObject; +} + +namespace ScreenLocker +{ + +class LnFIntegration : public QObject +{ + Q_OBJECT + + Q_PROPERTY(KDeclarative::ConfigPropertyMap *configuration READ configuration NOTIFY configurationChanged) + +public: + LnFIntegration(QObject *parent); + virtual ~LnFIntegration(); + + void init(); + + void setConfig(const KSharedConfig::Ptr &config) { + m_config = config; + } + + void setPackage(const KPackage::Package &package) { + m_package = package; + } + + KPackage::Package package() const { + return m_package; + } + + KDeclarative::ConfigPropertyMap *configuration() const { + return m_configuration; + } + +Q_SIGNALS: + void packageChanged(); + void configurationChanged(); + +private: + KConfigLoader *configScheme(); + KPackage::Package m_package; + KSharedConfig::Ptr m_config; + KConfigLoader *m_configLoader = nullptr; + KDeclarative::ConfigPropertyMap *m_configuration = nullptr; +}; + +} + +#endif diff --git a/kcm/CMakeLists.txt b/kcm/CMakeLists.txt index 58d3ec0..4e035dc 100644 --- a/kcm/CMakeLists.txt +++ b/kcm/CMakeLists.txt @@ -1,43 +1,44 @@ # KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"screenlocker_kcm\") set(screenlocker_kcm_SRCS kcm.cpp ../greeter/wallpaper_integration.cpp + ../greeter/lnf_integration.cpp ) include_directories(${CMAKE_CURRENT_BINARY_DIR}/../) ki18n_wrap_ui(screenlocker_kcm_SRCS kcm.ui) kconfig_add_kcfg_files(screenlocker_kcm_SRCS ../kcfg/kscreensaversettings.kcfgc) qt5_add_dbus_interface(screenlocker_kcm_SRCS ../dbus/org.kde.screensaver.xml screenlocker_interface) qt5_add_resources( screenlocker_kcm_SRCS resources.qrc ) add_library(screenlocker_kcm MODULE ${screenlocker_kcm_SRCS}) target_link_libraries(screenlocker_kcm Qt5::DBus Qt5::QuickWidgets KF5::ConfigWidgets KF5::I18n KF5::TextWidgets KF5::GlobalAccel KF5::Package KF5::XmlGui KF5::Declarative ) kcoreaddons_desktop_to_json(screenlocker_kcm screenlocker.desktop SERVICE_TYPES kcmodule.desktop) install( TARGETS screenlocker_kcm DESTINATION ${KDE_INSTALL_PLUGINDIR} ) install( FILES screenlocker.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp index 99db3cc..d51f62c 100644 --- a/kcm/kcm.cpp +++ b/kcm/kcm.cpp @@ -1,275 +1,316 @@ /******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "kcm.h" #include "kscreensaversettings.h" #include "ui_kcm.h" #include "screenlocker_interface.h" #include "../greeter/wallpaper_integration.h" +#include "../greeter/lnf_integration.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include static const QString s_lockActionName = QStringLiteral("Lock Session"); static const QString s_defaultWallpaperPackage = QStringLiteral("org.kde.color"); class ScreenLockerKcmForm : public QWidget, public Ui::ScreenLockerKcmForm { Q_OBJECT public: explicit ScreenLockerKcmForm(QWidget *parent); }; ScreenLockerKcmForm::ScreenLockerKcmForm(QWidget *parent) : QWidget(parent) { setupUi(this); kcfg_Timeout->setSuffix(ki18ncp("Spinbox suffix. Short for minutes"," min"," mins")); kcfg_LockGrace->setSuffix(ki18ncp("Spinbox suffix. Short for seconds"," sec"," secs")); } ScreenLockerKcm::ScreenLockerKcm(QWidget *parent, const QVariantList &args) : KCModule(parent, args) , m_actionCollection(new KActionCollection(this, QStringLiteral("ksmserver"))) , m_ui(new ScreenLockerKcmForm(this)) { QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); addConfig(KScreenSaverSettings::self(), m_ui); m_actionCollection->setConfigGlobal(true); QAction *a = m_actionCollection->addAction(s_lockActionName); a->setProperty("isConfigurationAction", true); m_ui->lockscreenShortcut->setCheckForConflictsAgainst(KKeySequenceWidget::None); a->setText(i18n("Lock Session")); KGlobalAccel::self()->setShortcut(a, QList{Qt::ALT+Qt::CTRL+Qt::Key_L, Qt::Key_ScreenSaver}); connect(m_ui->lockscreenShortcut, &KKeySequenceWidget::keySequenceChanged, this, &ScreenLockerKcm::shortcutChanged); loadWallpapers(); auto wallpaperChangedSignal = static_cast(&QComboBox::currentIndexChanged); connect(m_ui->wallpaperCombo, wallpaperChangedSignal, this, static_cast(&ScreenLockerKcm::changed)); connect(m_ui->wallpaperCombo, wallpaperChangedSignal, this, &ScreenLockerKcm::loadWallpaperConfig); m_ui->wallpaperCombo->installEventFilter(this); - m_ui->wallpaperConfigWidget->setClearColor(m_ui->palette().color(QPalette::Active, QPalette::Window)); auto proxy = new ScreenLockerProxy(this); + m_ui->wallpaperConfigWidget->setClearColor(m_ui->palette().color(QPalette::Active, QPalette::Window)); m_ui->wallpaperConfigWidget->rootContext()->setContextProperty("configDialog", proxy); + m_ui->lnfConfigWidget->setClearColor(m_ui->palette().color(QPalette::Active, QPalette::Window)); + m_ui->lnfConfigWidget->rootContext()->setContextProperty("configDialog", proxy); + + + connect(this, &ScreenLockerKcm::wallpaperConfigurationChanged, proxy, &ScreenLockerProxy::wallpaperConfigurationChanged); connect(this, &ScreenLockerKcm::currentWallpaperChanged, proxy, &ScreenLockerProxy::currentWallpaperChanged); - m_ui->wallpaperConfigWidget->setSource(QUrl(QStringLiteral("qrc:/kscreenlocker-kcm-resources/config.qml"))); + m_ui->wallpaperConfigWidget->setSource(QUrl(QStringLiteral("qrc:/kscreenlocker-kcm-resources/wallpaperconfig.qml"))); connect(m_ui->wallpaperConfigWidget->rootObject(), SIGNAL(configurationChanged()), this, SLOT(changed())); + m_ui->lnfConfigWidget->setSource(QUrl(QStringLiteral("qrc:/kscreenlocker-kcm-resources/lnfconfig.qml"))); + connect(m_ui->lnfConfigWidget->rootObject(), SIGNAL(configurationChanged()), this, SLOT(changed())); + m_ui->installEventFilter(this); } void ScreenLockerKcm::shortcutChanged(const QKeySequence &key) { if (QAction *a = m_actionCollection->action(s_lockActionName)) { auto shortcuts = KGlobalAccel::self()->shortcut(a); m_ui->lockscreenShortcut->setProperty("changed", !shortcuts.contains(key)); } changed(); } void ScreenLockerKcm::load() { KCModule::load(); m_package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { m_package.setPath(packageName); } if (QAction *a = m_actionCollection->action(s_lockActionName)) { auto shortcuts = KGlobalAccel::self()->shortcut(a); if (!shortcuts.isEmpty()) { m_ui->lockscreenShortcut->setKeySequence(shortcuts.first()); } } + m_lnfIntegration = new ScreenLocker::LnFIntegration(this); + m_lnfIntegration->setPackage(m_package); + m_lnfIntegration->setConfig(KScreenSaverSettings::self()->sharedConfig()); + m_lnfIntegration->init(); + selectWallpaper(KScreenSaverSettings::self()->wallpaperPlugin()); loadWallpaperConfig(); + loadLnfConfig(); } void ScreenLockerKcm::test(const QString &plugin) { if (plugin.isEmpty() || plugin == QLatin1String("none")) { return; } QProcess proc; QStringList arguments; arguments << plugin << QStringLiteral("--testing"); if (proc.execute(KSCREENLOCKER_GREET_BIN, arguments)) { QMessageBox::critical(this, i18n("Error"), i18n("Failed to successfully test the screen locker.")); } } void ScreenLockerKcm::save() { if (!shouldSaveShortcut()) { QMetaObject::invokeMethod(this, "changed", Qt::QueuedConnection); return; } KCModule::save(); QMetaObject::invokeMethod(m_ui->wallpaperConfigWidget->rootObject(), "saveConfig"); + QMetaObject::invokeMethod(m_ui->lnfConfigWidget->rootObject(), "saveConfig"); // set the wallpaper config KScreenSaverSettings::self()->setWallpaperPlugin(m_ui->wallpaperCombo->currentData().toString()); KScreenSaverSettings::self()->save(); if (m_ui->lockscreenShortcut->property("changed").toBool()) { if (QAction *a = m_actionCollection->action(s_lockActionName)) { KGlobalAccel::self()->setShortcut(a, QList{m_ui->lockscreenShortcut->keySequence()}, KGlobalAccel::NoAutoloading); m_actionCollection->writeSettings(); } m_ui->lockscreenShortcut->setProperty("changed", false); } // reconfigure through DBus OrgKdeScreensaverInterface interface(QStringLiteral("org.kde.screensaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus()); if (interface.isValid()) { interface.configure(); } } bool ScreenLockerKcm::shouldSaveShortcut() { if (m_ui->lockscreenShortcut->property("changed").toBool()) { const QKeySequence &sequence = m_ui->lockscreenShortcut->keySequence(); auto conflicting = KGlobalAccel::getGlobalShortcutsByKey(sequence); if (!conflicting.isEmpty()) { // Inform and ask the user about the conflict and reassigning // the keys sequence if (!KGlobalAccel::promptStealShortcutSystemwide(this, conflicting, sequence)) { return false; } KGlobalAccel::stealShortcutSystemwide(sequence); } } return true; } void ScreenLockerKcm::defaults() { KCModule::defaults(); m_ui->lockscreenShortcut->setKeySequence(Qt::ALT+Qt::CTRL+Qt::Key_L); selectWallpaper(s_defaultWallpaperPackage); } void ScreenLockerKcm::loadWallpapers() { const auto wallpaperPackages = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/Wallpaper")); for (auto &package : wallpaperPackages) { m_ui->wallpaperCombo->addItem(package.name(), package.pluginId()); } } void ScreenLockerKcm::selectWallpaper(const QString &pluginId) { const auto index = m_ui->wallpaperCombo->findData(pluginId); if (index != -1) { m_ui->wallpaperCombo->setCurrentIndex(index); } else if (pluginId != s_defaultWallpaperPackage) { // fall back to default plugin selectWallpaper(s_defaultWallpaperPackage); } } void ScreenLockerKcm::loadWallpaperConfig() { if (m_wallpaperIntegration) { if (m_wallpaperIntegration->pluginName() == m_ui->wallpaperCombo->currentData().toString()) { // nothing changed return; } delete m_wallpaperIntegration; } emit currentWallpaperChanged(); m_wallpaperIntegration = new ScreenLocker::WallpaperIntegration(this); m_wallpaperIntegration->setConfig(KScreenSaverSettings::self()->sharedConfig()); m_wallpaperIntegration->setPluginName(m_ui->wallpaperCombo->currentData().toString()); m_wallpaperIntegration->init(); m_ui->wallpaperConfigWidget->rootContext()->setContextProperty(QStringLiteral("wallpaper"), m_wallpaperIntegration); emit wallpaperConfigurationChanged(); m_ui->wallpaperConfigWidget->rootObject()->setProperty("sourceFile", m_wallpaperIntegration->package().filePath(QByteArrayLiteral("ui"), QStringLiteral("config.qml"))); } +void ScreenLockerKcm::loadLnfConfig() +{ + auto sourceFile = m_package.fileUrl(QByteArrayLiteral("lockscreen"), QStringLiteral("config.qml")); + if (sourceFile.isEmpty()) { + m_ui->lnfConfigWidget->hide(); + return; + } + m_ui->lnfConfigWidget->rootObject()->setProperty("sourceFile", sourceFile); +} + KDeclarative::ConfigPropertyMap * ScreenLockerKcm::wallpaperConfiguration() const { if (!m_wallpaperIntegration) { return nullptr; } return m_wallpaperIntegration->configuration(); } +KDeclarative::ConfigPropertyMap * ScreenLockerKcm::lnfConfiguration() const +{ + if (!m_lnfIntegration) { + return nullptr; + } + return m_lnfIntegration->configuration(); +} + + QString ScreenLockerKcm::currentWallpaper() const { return m_ui->wallpaperCombo->currentData().toString(); } bool ScreenLockerKcm::eventFilter(QObject *watched, QEvent *event) { if (watched == m_ui) { if (event->type() == QEvent::PaletteChange) { m_ui->wallpaperConfigWidget->setClearColor(m_ui->palette().color(QPalette::Active, QPalette::Window)); } return false; } if (watched != m_ui->wallpaperCombo) { return false; } if (event->type() == QEvent::Move) { if (auto object = m_ui->wallpaperConfigWidget->rootObject()) { // QtQuick Layouts have a hardcoded 5 px spacing by default object->setProperty("formAlignment", m_ui->wallpaperCombo->x() + 5); } + if (auto object = m_ui->lnfConfigWidget->rootObject()) { + // QtQuick Layouts have a hardcoded 5 px spacing by default + object->setProperty("formAlignment", m_ui->wallpaperCombo->x() + 5); + } + } return false; } K_PLUGIN_FACTORY_WITH_JSON(ScreenLockerKcmFactory, "screenlocker.json", registerPlugin();) #include "kcm.moc" diff --git a/kcm/kcm.h b/kcm/kcm.h index 096d17b..5d0709b 100644 --- a/kcm/kcm.h +++ b/kcm/kcm.h @@ -1,105 +1,115 @@ /******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin Copyright (C) 2014 Marco Martin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include #include class QQuickView; class QStandardItemModel; class KActionCollection; class ScreenLockerKcmForm; namespace ScreenLocker { class WallpaperIntegration; +class LnFIntegration; } namespace KDeclarative { class ConfigPropertyMap; } class ScreenLockerKcm : public KCModule { Q_OBJECT public: enum Roles { PluginNameRole = Qt::UserRole +1, ScreenhotRole }; explicit ScreenLockerKcm(QWidget *parent = nullptr, const QVariantList& args = QVariantList()); KDeclarative::ConfigPropertyMap *wallpaperConfiguration() const; + KDeclarative::ConfigPropertyMap *lnfConfiguration() const; + QString currentWallpaper() const; bool eventFilter(QObject *watched, QEvent *event) override; public Q_SLOTS: void load() Q_DECL_OVERRIDE; void save() Q_DECL_OVERRIDE; void defaults() Q_DECL_OVERRIDE; void test(const QString &plugin); Q_SIGNALS: void wallpaperConfigurationChanged(); void currentWallpaperChanged(); private: void shortcutChanged(const QKeySequence &key); bool shouldSaveShortcut(); void loadWallpapers(); void selectWallpaper(const QString &pluginId); void loadWallpaperConfig(); + void loadLnfConfig(); KPackage::Package m_package; KActionCollection *m_actionCollection; ScreenLockerKcmForm *m_ui; ScreenLocker::WallpaperIntegration *m_wallpaperIntegration = nullptr; + ScreenLocker::LnFIntegration* m_lnfIntegration = nullptr; }; //see https://bugreports.qt.io/browse/QTBUG-57714, don't expose a QWidget as a context property class ScreenLockerProxy : public QObject { Q_OBJECT Q_PROPERTY(KDeclarative::ConfigPropertyMap *wallpaperConfiguration READ wallpaperConfiguration NOTIFY wallpaperConfigurationChanged) + Q_PROPERTY(KDeclarative::ConfigPropertyMap *lnfConfiguration READ lnfConfiguration CONSTANT) + Q_PROPERTY(QString currentWallpaper READ currentWallpaper NOTIFY currentWallpaperChanged) public: ScreenLockerProxy(ScreenLockerKcm *parent) : QObject(parent), q(parent) { } KDeclarative::ConfigPropertyMap *wallpaperConfiguration() const { return q->wallpaperConfiguration(); } + KDeclarative::ConfigPropertyMap *lnfConfiguration() const { + return q->lnfConfiguration(); + } QString currentWallpaper() const { return q->currentWallpaper(); } signals: void wallpaperConfigurationChanged(); void currentWallpaperChanged(); private: ScreenLockerKcm* q; }; diff --git a/kcm/kcm.ui b/kcm/kcm.ui index 7a86767..98d7121 100644 --- a/kcm/kcm.ui +++ b/kcm/kcm.ui @@ -1,209 +1,228 @@ ScreenLockerKcmForm 0 0 577 544 0 0 0 true 0 0 Activation 0 0 Lock screen automatically after: 0 0 1 Re&quire password after locking: kcfg_LockGrace 0 0 Immediately 300 10 Lock screen when waking up from suspension &Lock screen on resume: kcfg_LockOnResume Lock screen when waking up from suspension - + The global keyboard shortcut to lock the screen. Keyboard shortcut: - Wallpaper + Appearance + + + + + 0 + 0 + + + + + 0 + 0 + + + + QQuickWidget::SizeRootObjectToView + + + QFormLayout::FieldsStayAtSizeHint Qt::AlignHCenter|Qt::AlignTop - + Wallpaper &Type: wallpaperCombo - + 0 0 QQuickWidget::SizeRootObjectToView - - QQuickWidget - QWidget -
QtQuickWidgets/QQuickWidget
-
KKeySequenceWidget QWidget
kkeysequencewidget.h
KPluralHandlingSpinBox QSpinBox
kpluralhandlingspinbox.h
+ + QQuickWidget + QWidget +
QtQuickWidgets/QQuickWidget
+
diff --git a/kcm/config.qml b/kcm/lnfconfig.qml similarity index 85% copy from kcm/config.qml copy to kcm/lnfconfig.qml index 07c9071..08ff817 100644 --- a/kcm/config.qml +++ b/kcm/lnfconfig.qml @@ -1,84 +1,83 @@ /******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 2016 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ import QtQuick 2.0 import QtQuick.Controls 1.0 as QtControls import QtQuick.Layouts 1.1 ColumnLayout { id: root property int formAlignment: 0 property alias sourceFile: main.sourceFile signal configurationChanged //BEGIN functions function saveConfig() { if (main.currentItem.saveConfig) { main.currentItem.saveConfig() } - for (var key in configDialog.wallpaperConfiguration) { + for (var key in configDialog.lnfConfiguration) { if (main.currentItem["cfg_"+key] !== undefined) { - configDialog.wallpaperConfiguration[key] = main.currentItem["cfg_"+key] + configDialog.lnfConfiguration[key] = main.currentItem["cfg_"+key] } } } //END functions Item { id: emptyConfig } QtControls.StackView { id: main Layout.fillHeight: true anchors { left: parent.left; right: parent.right; } property string sourceFile onSourceFileChanged: { if (sourceFile) { var props = {} - - var wallpaperConfig = configDialog.wallpaperConfiguration - for (var key in wallpaperConfig) { - props["cfg_" + key] = wallpaperConfig[key] + var lnfConfiguration = configDialog.lnfConfiguration + for (var key in lnfConfiguration) { + props["cfg_" + key] = lnfConfiguration[key] } var newItem = push({ item: sourceFile, replace: true, properties: props }) - for (var key in wallpaperConfig) { + for (var key in lnfConfiguration) { var changedSignal = newItem["cfg_" + key + "Changed"] if (changedSignal) { changedSignal.connect(root.configurationChanged) } } } else { replace(emptyConfig) } } } } diff --git a/kcm/resources.qrc b/kcm/resources.qrc index ebe8583..d7f0970 100644 --- a/kcm/resources.qrc +++ b/kcm/resources.qrc @@ -1,6 +1,7 @@ - config.qml + wallpaperconfig.qml + lnfconfig.qml diff --git a/kcm/config.qml b/kcm/wallpaperconfig.qml similarity index 100% rename from kcm/config.qml rename to kcm/wallpaperconfig.qml