diff --git a/CMakeLists.txt b/CMakeLists.txt index 772e603..e55151d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,285 +1,286 @@ cmake_minimum_required(VERSION 3.0) project(kscreenlocker) set(PROJECT_VERSION "5.17.80") set(PROJECT_VERSION_MAJOR 5) set(QT_MIN_VERSION "5.12.0") set(KF5_MIN_VERSION "5.62.0") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS DBus Widgets Quick QuickWidgets Test) find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMSetupVersion) include(ECMGenerateHeaders) include(CheckIncludeFiles) include(ECMMarkNonGuiExecutable) include(CMakePackageConfigHelpers) include(FeatureSummary) include(GenerateExportHeader) include(KDEClangFormat) include(CheckIncludeFile) include(CheckSymbolExists) check_include_file("sys/prctl.h" HAVE_SYS_PRCTL_H) check_symbol_exists(PR_SET_DUMPABLE "sys/prctl.h" HAVE_PR_SET_DUMPABLE) check_include_file("sys/procctl.h" HAVE_SYS_PROCCTL_H) check_symbol_exists(PROC_TRACE_CTL "sys/procctl.h" HAVE_PROC_TRACE_CTL) if (HAVE_PR_SET_DUMPABLE OR HAVE_PROC_TRACE_CTL) set(CAN_DISABLE_PTRACE TRUE) endif () add_feature_info("prctl/procctl tracing control" CAN_DISABLE_PTRACE "Required for disallowing ptrace on greeter and kcheckpass process") check_include_file("sys/signalfd.h" HAVE_SIGNALFD_H) if (NOT HAVE_SIGNALFD_H) check_include_files("sys/types.h;sys/event.h" HAVE_EVENT_H) endif () if (NOT (HAVE_SIGNALFD_H OR HAVE_EVENT_H)) message(FATAL_ERROR "kcheckpass either needs signalfd() or kevent()&sigtimedwait() to work") endif () add_feature_info("sys/signalfd.h" HAVE_SIGNALFD_H "Use the signalfd() api for signalhandling") add_feature_info("sys/event.h" HAVE_EVENT_H "Use the kevent() and sigwaitinfo() api for signalhandling") find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS Crash Declarative GlobalAccel I18n IdleTime KCMUtils Notifications Solid TextWidgets WindowSystem XmlGui ) find_package(X11) set_package_properties(X11 PROPERTIES DESCRIPTION "X11 libraries" URL "http://www.x.org" TYPE REQUIRED PURPOSE "Required for building the X11 based workspace") find_package(XCB MODULE REQUIRED COMPONENTS XCB KEYSYMS XTEST) set_package_properties(XCB PROPERTIES TYPE REQUIRED) add_feature_info("XInput" X11_Xinput_FOUND "Required for grabbing XInput2 devices in the screen locker") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS X11Extras) find_package(KF5Wayland CONFIG REQUIRED) set_package_properties(KF5Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building screenlocker") find_package(WaylandScanner) find_package(Wayland 1.3 COMPONENTS Client Server) set_package_properties(Wayland PROPERTIES TYPE REQUIRED PURPOSE "Required for building screenlocker") find_package(loginctl) set_package_properties(loginctl PROPERTIES URL "https://www.freedesktop.org/software/systemd/man/loginctl.html" DESCRIPTION "Send control commands to the login manager" TYPE RUNTIME ) set(HAVE_LOGINCTL ${loginctl_FOUND}) if ( NOT HAVE_LOGINCTL ) find_package(ConsoleKit) set_package_properties(ConsoleKit PROPERTIES URL "https://github.com/ConsoleKit2/ConsoleKit2" DESCRIPTION "Framework for tracking user login sessions" TYPE RECOMMENDED ) set(HAVE_CONSOLEKIT ${ConsoleKit_FOUND}) endif () if (HAVE_LOGINCTL OR HAVE_CONSOLEKIT) set(HAVE_UNLOCK_CAPABILITY TRUE) endif () add_feature_info("Unlock broken screenlocker" HAVE_UNLOCK_CAPABILITY "Needed for emergency unlock in case that the greeter is broken. In case your distribution does not provide loginctl or consolekit please contact plasma-devel@kde.org to discuss alternatives.") option(PAM_REQUIRED "Require building with PAM" ON) find_package(Seccomp) set_package_properties(Seccomp PROPERTIES TYPE OPTIONAL PURPOSE "Used for putting the look'n'feel package in the greeter into a sandbox." ) set(HAVE_SECCOMP ${Seccomp_FOUND}) include(ConfigureChecks.cmake) configure_file(config-workspace.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-workspace.h) configure_file(config-unix.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-unix.h ) configure_file(config-X11.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-X11.h) if (HAVE_CONSOLEKIT) configure_file(ck-unlock-session.cmake ${CMAKE_CURRENT_BINARY_DIR}/ck-unlock-session) endif () # adjusting CMAKE_C_FLAGS to get wayland protocols to compile set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu90") ecm_setup_version(${PROJECT_VERSION} VARIABLE_PREFIX KSCREENLOCKER VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/kscreenlocker_version.h" PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfigVersion.cmake" SOVERSION 5) configure_file(config-kscreenlocker.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kscreenlocker.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) set(KSLD_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/KScreenLocker") add_subdirectory(kcheckpass) add_subdirectory(greeter) add_subdirectory(kcm) add_definitions(-DTRANSLATION_DOMAIN=\"kscreenlocker\") set(screensaver_dbusXML dbus/org.freedesktop.ScreenSaver.xml) set(kscreensaver_dbusXML dbus/org.kde.screensaver.xml) set(powerdevilpolicyagent_xml dbus/kf5_org.kde.Solid.PowerManagement.PolicyAgent.xml) set(ksld_SRCS abstractlocker.cpp ksldapp.cpp interface.cpp globalaccel.cpp x11locker.cpp waylandlocker.cpp logind.cpp waylandserver.cpp powermanagement.cpp powermanagement_inhibition.cpp kscreensaversettings.cpp ) qt5_add_dbus_adaptor(ksld_SRCS ${screensaver_dbusXML} interface.h ScreenLocker::Interface) qt5_add_dbus_adaptor(ksld_SRCS ${kscreensaver_dbusXML} interface.h ScreenLocker::Interface kscreensaveradaptor KScreenSaverAdaptor) kconfig_add_kcfg_files(ksld_SRCS kcfg/kscreensaversettingsbase.kcfgc) qt5_add_dbus_interface(ksld_SRCS ${powerdevilpolicyagent_xml} powerdevilpolicyagent) ecm_add_wayland_server_protocol(ksld_SRCS PROTOCOL protocols/ksld.xml BASENAME ksld ) add_library(KScreenLocker SHARED ${ksld_SRCS}) target_link_libraries(KScreenLocker PUBLIC Qt5::Core Qt5::X11Extras PRIVATE Qt5::DBus KF5::I18n KF5::IdleTime KF5::GlobalAccel KF5::Notifications KF5::CoreAddons KF5::ConfigGui + KF5::Package KF5::WindowSystem KF5::XmlGui ${X11_LIBRARIES} XCB::XCB XCB::KEYSYMS KF5::WaylandServer Wayland::Server ) if (X11_Xinput_FOUND) target_link_libraries(KScreenLocker PRIVATE ${X11_Xinput_LIB}) endif() target_include_directories(KScreenLocker INTERFACE "$") # Needed to compile on Arm target. set_target_properties(KScreenLocker PROPERTIES COMPILE_FLAGS "-fPIC") add_library(PW::KScreenLocker ALIAS KScreenLocker) # add clang-format target for all our real source files file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) generate_export_header(KScreenLocker BASE_NAME KScreenLocker EXPORT_MACRO_NAME KSCREENLOCKER_EXPORT EXPORT_FILE_NAME KScreenLocker/kscreenlocker_export.h ) configure_package_config_file(ScreenSaverDBusInterfaceConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/ScreenSaverDBusInterfaceConfig.cmake PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/ScreenSaverDBusInterface") set_target_properties(KScreenLocker PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${PROJECT_VERSION_MAJOR}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/ScreenSaverDBusInterfaceConfig.cmake DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/ScreenSaverDBusInterface") configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KScreenLockerConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfig.cmake" PATH_VARS KDE_INSTALL_DBUSINTERFACEDIR INSTALL_DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/KScreenLocker") if (HAVE_CONSOLEKIT) install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/ck-unlock-session DESTINATION ${KDE_INSTALL_BINDIR}) endif () install(TARGETS KScreenLocker EXPORT KScreenLockerTargets ${INSTALL_TARGETS_DEFAULT_ARGS} LIBRARY) install(EXPORT KScreenLockerTargets DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/KScreenLocker" FILE KScreenLockerTargets.cmake NAMESPACE PW::) ecm_generate_headers(KScreenLocker_CamelCase_HEADERS HEADER_NAMES KsldApp REQUIRED_HEADERS KScreenLocker_HEADERS) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/KScreenLocker/kscreenlocker_export.h ${KScreenLocker_CamelCase_HEADERS} ${KScreenLocker_HEADERS} DESTINATION ${KSLD_INCLUDEDIR}/KScreenLocker COMPONENT Devel) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfigVersion.cmake" DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/KScreenLocker" COMPONENT Devel) install(FILES kscreenlocker.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFY5RCDIR} RENAME ksmserver.notifyrc) install(FILES ${screensaver_dbusXML} DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.freedesktop.ScreenSaver.xml) install(FILES ${kscreensaver_dbusXML} DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR}) install(FILES updaters/kscreenlocker.upd DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) install(PROGRAMS updaters/ksreenlocker_5_3_separate_autologin.pl DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) if(BUILD_TESTING) add_subdirectory(autotests) add_subdirectory(tests) endif() feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/greeter/greeterapp.cpp b/greeter/greeterapp.cpp index c85b0b1..b137c85 100644 --- a/greeter/greeterapp.cpp +++ b/greeter/greeterapp.cpp @@ -1,769 +1,769 @@ /******************************************************************** 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 "kscreensaversettingsbase.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 #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 { // disable DrKonqi as the crash dialog blocks the restart of the locker void disableDrKonqi() { KCrash::setDrKonqiEnabled(false); } // run immediately, before Q_CORE_STARTUP functions // that would enable drkonqi Q_CONSTRUCTOR_FUNCTION(disableDrKonqi) 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(nullptr) , 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: qAsConst(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); KScreenSaverSettingsBase::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 (!KScreenSaverSettingsBase::theme().isEmpty()) { package.setPath(KScreenSaverSettingsBase::theme()); } m_mainQmlPath = package.fileUrl("lockscreenmainscript"); m_wallpaperIntegration->setConfig(KScreenSaverSettingsBase::self()->sharedConfig()); - m_wallpaperIntegration->setPluginName(KScreenSaverSettingsBase::self()->wallpaperPlugin()); + m_wallpaperIntegration->setPluginName(KScreenSaverSettingsBase::self()->wallpaperPluginId()); m_wallpaperIntegration->init(); m_lnfIntegration->setPackage(package); m_lnfIntegration->setConfig(KScreenSaverSettingsBase::self()->sharedConfig()); m_lnfIntegration->init(); installEventFilter(this); } QWindow *UnlockApp::getActiveScreen() { QWindow *activeScreen = nullptr; if (m_views.isEmpty()) { return activeScreen; } for (KQuickAddons::QuickViewSharedEngine * view : qAsConst(m_views)) { if (view->geometry().contains(QCursor::pos())) { activeScreen = view; break; } } if (!activeScreen) { activeScreen = m_views.first(); } return activeScreen; } 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); view->rootContext()->setContextProperty(QStringLiteral("wallpaperIntegration"), m_wallpaperIntegration); } ); } 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); auto screen = QGuiApplication::screens()[i]; view->setGeometry(screen->geometry()); // 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::Ready) { 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()); } } ); // 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(); connect(view, &QQuickWindow::frameSwapped, this, [this, view] { markViewsAsVisible(view); }, Qt::QueuedConnection); } } void UnlockApp::markViewsAsVisible(KQuickAddons::QuickViewSharedEngine *view) { disconnect(view, &QQuickWindow::frameSwapped, this, nullptr); QQmlProperty showProperty(view->rootObject(), QStringLiteral("viewVisible")); showProperty.write(true); // random state update, actually rather required on init only QMetaObject::invokeMethod(this, "getFocus", Qt::QueuedConnection); auto mime1 = new QMimeData; //Effectively we want to clear the clipboard //however some clipboard managers (like klipper with it's default settings) //will prevent an empty clipbard //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 //as the clipboard is cleared on close, klipper will then put the original text back when we exit mime1->setData(QStringLiteral("x-kde-lockscreen"), QByteArrayLiteral("empty")); //ownership is transferred QGuiApplication::clipboard()->setMimeData(mime1, QClipboard::Clipboard); auto mime2 = new QMimeData; mime2->setData(QStringLiteral("x-kde-lockscreen"), QByteArrayLiteral("empty")); QGuiApplication::clipboard()->setMimeData(mime2, QClipboard::Selection); } void UnlockApp::getFocus() { QWindow *activeScreen = getActiveScreen(); if (!activeScreen) { return; } // this loop is required to make the qml/graphicsscene properly handle the shared keyboard input // ie. "type something into the box of every greeter" for (KQuickAddons::QuickViewSharedEngine *view : qAsConst(m_views)) { if (!m_testing) { view->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master! } } // 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) { activeScreen->setKeyboardGrabEnabled(true); // TODO - check whether this still works in master! } activeScreen->requestActivate(); } void UnlockApp::setLockedPropertyOnViews() { delete m_delayedLockTimer; m_delayedLockTimer = nullptr; for (KQuickAddons::QuickViewSharedEngine *view : qAsConst(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 for (KQuickAddons::QuickViewSharedEngine * view : qAsConst(m_views)) { view->setFlags(view->flags() & ~Qt::X11BypassWindowManagerHint); } } else { for (KQuickAddons::QuickViewSharedEngine * view : qAsConst(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 = nullptr; for (KQuickAddons::QuickViewSharedEngine *v : qAsConst(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, nullptr, 0); } // no further processing return false; } if (event->type() == QEvent::MouseButtonPress && QX11Info::isPlatformX11()) { if (getActiveScreen()) { getActiveScreen()->requestActivate(); } 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 for (KQuickAddons::QuickViewSharedEngine * view : qAsConst(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 : qAsConst(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 : qAsConst(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 : qAsConst(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/kcfg/kscreenlockersettings.kcfg b/kcfg/kscreenlockersettings.kcfg index 52d646d..c17f6ab 100644 --- a/kcfg/kscreenlockersettings.kcfg +++ b/kcfg/kscreenlockersettings.kcfg @@ -1,47 +1,47 @@ true Sets whether the screen will be locked after the specified time. 5 1 Sets the minutes after which the screen is locked. true 5 0 300 true - + org.kde.image diff --git a/kcm/kcm.cpp b/kcm/kcm.cpp index 7880461..7962883 100644 --- a/kcm/kcm.cpp +++ b/kcm/kcm.cpp @@ -1,301 +1,271 @@ /******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin Copyright (C) 2019 Kevin Ottens 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 -static const QString s_defaultWallpaperPackage = QStringLiteral("org.kde.image"); - class ScreenLockerKcmForm : public QWidget, public Ui::ScreenLockerKcmForm { Q_OBJECT public: explicit ScreenLockerKcmForm(QWidget *parent); }; ScreenLockerKcmForm::ScreenLockerKcmForm(QWidget *parent) : QWidget(parent) { setupUi(this); layout()->setContentsMargins(0, 0, 0, 0); kcfg_Timeout->setSuffix(ki18ncp("Spinbox suffix"," minute"," minutes")); kcfg_LockGrace->setSuffix(ki18ncp("Spinbox suffix"," second"," seconds")); } ScreenLockerKcm::ScreenLockerKcm(QWidget *parent, const QVariantList &args) : KCModule(parent, args) , m_settings(new KScreenSaverSettings(this)) , m_ui(new ScreenLockerKcmForm(this)) { QVBoxLayout* layout = new QVBoxLayout(this); layout->addWidget(m_ui); - - addConfig(m_settings, m_ui); - - loadWallpapers(); - connect(m_ui->wallpaperCombo, static_cast(&QComboBox::currentIndexChanged), + for (const auto &pluginInfo : m_settings->availableWallpaperPlugins()) { + m_ui->kcfg_wallpaperPluginIndex->addItem(pluginInfo.name, pluginInfo.id); + } + connect(m_ui->kcfg_wallpaperPluginIndex, static_cast(&QComboBox::currentIndexChanged), this, &ScreenLockerKcm::loadWallpaperConfig); - - m_ui->wallpaperCombo->installEventFilter(this); + m_ui->kcfg_wallpaperPluginIndex->installEventFilter(this); + m_ui->installEventFilter(this); auto proxy = new ScreenLockerProxy(this); + m_ui->wallpaperConfigWidget->setClearColor(m_ui->palette().color(QPalette::Active, QPalette::Window)); m_ui->wallpaperConfigWidget->rootContext()->setContextProperty(QStringLiteral("configDialog"), proxy); - - m_ui->lnfConfigWidget->setClearColor(m_ui->palette().color(QPalette::Active, QPalette::Window)); - m_ui->lnfConfigWidget->rootContext()->setContextProperty(QStringLiteral("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/wallpaperconfig.qml"))); connect(m_ui->wallpaperConfigWidget->rootObject(), SIGNAL(configurationChanged()), this, SLOT(updateState())); + m_ui->lnfConfigWidget->setClearColor(m_ui->palette().color(QPalette::Active, QPalette::Window)); + m_ui->lnfConfigWidget->rootContext()->setContextProperty(QStringLiteral("configDialog"), proxy); m_ui->lnfConfigWidget->setSource(QUrl(QStringLiteral("qrc:/kscreenlocker-kcm-resources/lnfconfig.qml"))); connect(m_ui->lnfConfigWidget->rootObject(), SIGNAL(configurationChanged()), this, SLOT(updateState())); - m_ui->installEventFilter(this); + addConfig(m_settings, m_ui); } void ScreenLockerKcm::load() { KCModule::load(); - // Because the wallpaper plugin is currently handled wrongly - m_settings->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); - } - - m_lnfIntegration = new ScreenLocker::LnFIntegration(this); - m_lnfIntegration->setPackage(m_package); - m_lnfIntegration->setConfig(m_settings->sharedConfig()); - m_lnfIntegration->init(); - m_lnfSettings = m_lnfIntegration->configScheme(); - - selectWallpaper(m_settings->wallpaperPlugin()); loadWallpaperConfig(); loadLnfConfig(); if (m_lnfSettings) { m_lnfSettings->load(); emit m_lnfSettings->configChanged(); // To force the ConfigPropertyMap to reevaluate } if (m_wallpaperSettings) { m_wallpaperSettings->load(); emit m_wallpaperSettings->configChanged(); // To force the ConfigPropertyMap to reevaluate } updateState(); } void ScreenLockerKcm::save() { KCModule::save(); if (m_lnfSettings) { m_lnfSettings->save(); } if (m_wallpaperSettings) { m_wallpaperSettings->save(); } // reconfigure through DBus OrgKdeScreensaverInterface interface(QStringLiteral("org.kde.screensaver"), QStringLiteral("/ScreenSaver"), QDBusConnection::sessionBus()); if (interface.isValid()) { interface.configure(); } updateState(); } void ScreenLockerKcm::defaults() { KCModule::defaults(); - selectWallpaper(s_defaultWallpaperPackage); if (m_lnfSettings) { m_lnfSettings->setDefaults(); emit m_lnfSettings->configChanged(); // To force the ConfigPropertyMap to reevaluate } if (m_wallpaperSettings) { m_wallpaperSettings->setDefaults(); emit m_wallpaperSettings->configChanged(); // To force the ConfigPropertyMap to reevaluate } updateState(); } void ScreenLockerKcm::updateState() { bool isDefaults = m_settings->isDefaults(); bool isSaveNeeded = m_settings->isSaveNeeded(); if (m_lnfSettings) { isDefaults &= m_lnfSettings->isDefaults(); isSaveNeeded |= m_lnfSettings->isSaveNeeded(); } if (m_wallpaperSettings) { isDefaults &= m_wallpaperSettings->isDefaults(); isSaveNeeded |= m_wallpaperSettings->isSaveNeeded(); } emit changed(isSaveNeeded); emit defaulted(isDefaults); } -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() { - // set the wallpaper config - m_settings->setWallpaperPlugin(m_ui->wallpaperCombo->currentData().toString()); - updateState(); - if (m_wallpaperIntegration) { - if (m_wallpaperIntegration->pluginName() == m_ui->wallpaperCombo->currentData().toString()) { + if (m_wallpaperIntegration->pluginName() == m_ui->kcfg_wallpaperPluginIndex->currentData().toString()) { // nothing changed return; } delete m_wallpaperIntegration; } emit currentWallpaperChanged(); m_wallpaperIntegration = new ScreenLocker::WallpaperIntegration(this); m_wallpaperIntegration->setConfig(m_settings->sharedConfig()); - m_wallpaperIntegration->setPluginName(m_ui->wallpaperCombo->currentData().toString()); + m_wallpaperIntegration->setPluginName(m_ui->kcfg_wallpaperPluginIndex->currentData().toString()); m_wallpaperIntegration->init(); m_wallpaperSettings = m_wallpaperIntegration->configScheme(); 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() { + if (m_package.isValid() && m_lnfIntegration) { + return; + } + + Q_ASSERT(!m_package.isValid() && !m_lnfIntegration); + + 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); + } + + m_lnfIntegration = new ScreenLocker::LnFIntegration(this); + m_lnfIntegration->setPackage(m_package); + m_lnfIntegration->setConfig(m_settings->sharedConfig()); + m_lnfIntegration->init(); + m_lnfSettings = m_lnfIntegration->configScheme(); + 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(); + return m_ui->kcfg_wallpaperPluginIndex->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) { + if (watched != m_ui->kcfg_wallpaperPluginIndex) { 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); + object->setProperty("formAlignment", m_ui->kcfg_wallpaperPluginIndex->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); + object->setProperty("formAlignment", m_ui->kcfg_wallpaperPluginIndex->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 f237d45..555cf47 100644 --- a/kcm/kcm.h +++ b/kcm/kcm.h @@ -1,116 +1,117 @@ /******************************************************************** KSld - the KDE Screenlocker Daemon This file is part of the KDE project. Copyright (C) 2014 Martin Gräßlin Copyright (C) 2014 Marco Martin Copyright (C) 2019 Kevin Ottens 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 KScreenSaverSettings; 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() override; void save() override; void defaults() override; Q_SIGNALS: void wallpaperConfigurationChanged(); void currentWallpaperChanged(); private Q_SLOTS: void updateState(); private: - void loadWallpapers(); void selectWallpaper(const QString &pluginId); void loadWallpaperConfig(); void loadLnfConfig(); KPackage::Package m_package; KScreenSaverSettings *m_settings; ScreenLockerKcmForm *m_ui; ScreenLocker::WallpaperIntegration *m_wallpaperIntegration = nullptr; KCoreConfigSkeleton *m_wallpaperSettings = nullptr; ScreenLocker::LnFIntegration* m_lnfIntegration = nullptr; KCoreConfigSkeleton *m_lnfSettings = 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) { + connect(q, &ScreenLockerKcm::wallpaperConfigurationChanged, this, &ScreenLockerProxy::wallpaperConfigurationChanged); + connect(q, &ScreenLockerKcm::currentWallpaperChanged, this, &ScreenLockerProxy::currentWallpaperChanged); } KDeclarative::ConfigPropertyMap *wallpaperConfiguration() const { return q->wallpaperConfiguration(); } KDeclarative::ConfigPropertyMap *lnfConfiguration() const { return q->lnfConfiguration(); } QString currentWallpaper() const { return q->currentWallpaper(); } Q_SIGNALS: void wallpaperConfigurationChanged(); void currentWallpaperChanged(); private: ScreenLockerKcm* q; }; diff --git a/kcm/kcm.ui b/kcm/kcm.ui index d355abd..dd75fb7 100644 --- a/kcm/kcm.ui +++ b/kcm/kcm.ui @@ -1,200 +1,200 @@ ScreenLockerKcmForm 500 525 0 Activation Qt::AlignHCenter|Qt::AlignTop Lock screen: 0 Automatically after: 1 Lock screen when waking up from suspension After waking from sleep Allow unlocking without password for: kcfg_LockGrace Immediately 300 10 The global keyboard shortcut to lock the screen. Keyboard shortcut: Appearance 0 0 0 0 QQuickWidget::SizeViewToRootObject QFormLayout::FieldsStayAtSizeHint Qt::AlignHCenter|Qt::AlignTop Wallpaper &type: - wallpaperCombo + kcfg_wallpaperPluginIndex - + 0 0 QQuickWidget::SizeRootObjectToView QQuickWidget QWidget
QtQuickWidgets/QQuickWidget
KKeySequenceWidget QWidget
kkeysequencewidget.h
KPluralHandlingSpinBox QSpinBox
kpluralhandlingspinbox.h
kcfg_Autolock toggled(bool) kcfg_Timeout setEnabled(bool)
diff --git a/kscreensaversettings.cpp b/kscreensaversettings.cpp index a6fd913..e95f055 100644 --- a/kscreensaversettings.cpp +++ b/kscreensaversettings.cpp @@ -1,69 +1,117 @@ /******************************************************************** This file is part of the KDE project. Copyright 2019 Kevin Ottens 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 "kscreensaversettings.h" #include #include #include +#include +#include QList KScreenSaverSettings::defaultShortcuts() { return { Qt::META + Qt::Key_L, Qt::ALT + Qt::CTRL + Qt::Key_L, Qt::Key_ScreenSaver }; } +QString KScreenSaverSettings::defaultWallpaperPlugin() +{ + return QStringLiteral("org.kde.image"); +} + KScreenSaverSettings::KScreenSaverSettings(QObject *parent) : KScreenSaverSettingsBase() , m_actionCollection(new KActionCollection(this, QStringLiteral("ksmserver"))) , m_lockAction(nullptr) { setParent(parent); + const auto wallpaperPackages = KPackage::PackageLoader::self()->listPackages(QStringLiteral("Plasma/Wallpaper")); + for (auto &package : wallpaperPackages) { + m_availableWallpaperPlugins.append({package.name(), package.pluginId()}); + } + m_actionCollection->setConfigGlobal(true); m_lockAction = m_actionCollection->addAction(QStringLiteral("Lock Session")); m_lockAction->setProperty("isConfigurationAction", true); m_lockAction->setText(i18n("Lock Session")); KGlobalAccel::self()->setShortcut(m_lockAction, defaultShortcuts()); addItem(new KPropertySkeletonItem(this, "shortcut", defaultShortcuts().first()), QStringLiteral("lockscreenShortcut")); + addItem(new KPropertySkeletonItem(this, "wallpaperPluginIndex", indexFromWallpaperPluginId(defaultWallpaperPlugin())), + QStringLiteral("wallpaperPluginIndex")); } KScreenSaverSettings::~KScreenSaverSettings() { } +QVector KScreenSaverSettings::availableWallpaperPlugins() const +{ + return m_availableWallpaperPlugins; +} + +int KScreenSaverSettings::wallpaperPluginIndex() const +{ + return indexFromWallpaperPluginId(wallpaperPluginId()); +} + +void KScreenSaverSettings::setWallpaperPluginIndex(int index) +{ + Q_ASSERT(index >=0 && index < m_availableWallpaperPlugins.size()); + setWallpaperPluginId(m_availableWallpaperPlugins[index].id); + + // We get in this function during save, but wallpaperPluginId might + // have been written already, since we're tempering with its value here + // we make sure it really reaches the config object. + findItem(QStringLiteral("wallpaperPluginId"))->writeConfig(config()); +} + QKeySequence KScreenSaverSettings::shortcut() const { return KGlobalAccel::self()->shortcut(m_lockAction).first(); } void KScreenSaverSettings::setShortcut(const QKeySequence &sequence) { auto shortcuts = KGlobalAccel::self()->shortcut(m_lockAction); if (shortcuts.isEmpty()) { shortcuts << QKeySequence(); } shortcuts[0] = sequence; KGlobalAccel::self()->setShortcut(m_lockAction, shortcuts, KGlobalAccel::NoAutoloading); } + +int KScreenSaverSettings::indexFromWallpaperPluginId(const QString &id) const +{ + const auto it = std::find_if(m_availableWallpaperPlugins.cbegin(), m_availableWallpaperPlugins.cend(), + [id] (const WallpaperInfo &info) { return info.id == id; }); + if (it != m_availableWallpaperPlugins.cend()) { + return it - m_availableWallpaperPlugins.cbegin(); + } else if (id != defaultWallpaperPlugin()) { + return indexFromWallpaperPluginId(defaultWallpaperPlugin()); + } else { + return -1; + } +} diff --git a/kscreensaversettings.h b/kscreensaversettings.h index cb8bb97..b5f4146 100644 --- a/kscreensaversettings.h +++ b/kscreensaversettings.h @@ -1,45 +1,59 @@ /******************************************************************** This file is part of the KDE project. Copyright 2019 Kevin Ottens 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 KSCREENSAVERSETTINGS_H #define KSCREENSAVERSETTINGS_H #include "kscreensaversettingsbase.h" class QAction; class KActionCollection; class KScreenSaverSettings : public KScreenSaverSettingsBase { Q_OBJECT Q_PROPERTY(QKeySequence shortcut READ shortcut WRITE setShortcut) + Q_PROPERTY(int wallpaperPluginIndex READ wallpaperPluginIndex WRITE setWallpaperPluginIndex) public: + struct WallpaperInfo { + QString name; + QString id; + }; + static QList defaultShortcuts(); + static QString defaultWallpaperPlugin(); KScreenSaverSettings(QObject *parent = nullptr); ~KScreenSaverSettings() override; + QVector availableWallpaperPlugins() const; + int wallpaperPluginIndex() const; + void setWallpaperPluginIndex(int index); + QKeySequence shortcut() const; void setShortcut(const QKeySequence &sequence); private: + int indexFromWallpaperPluginId(const QString &id) const; + + QVector m_availableWallpaperPlugins; KActionCollection *m_actionCollection; QAction *m_lockAction; }; #endif // KSCREENSAVERSETTINGS_H