diff --git a/CMakeLists.txt b/CMakeLists.txt index ea39816..0bf9d3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,193 +1,194 @@ set(PROJECT_VERSION "5.5.90") set(PROJECT_VERSION_MAJOR 5) cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) set(QT_MIN_VERSION "5.5.0") set(KF5_MIN_VERSION "5.15.0") find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Widgets Quick QuickWidgets Test) find_package(ECM 1.8.0 REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings) include(ECMSetupVersion) include(ECMGenerateHeaders) include(CheckIncludeFiles) include(ECMMarkNonGuiExecutable) include(ECMPackageConfigHelpers) include(FeatureSummary) 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) add_feature_info("prctl-dumpable" HAVE_PR_SET_DUMPABLE "Required for disallow ptrace on greeter and kcheckpass process") find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS KCMUtils Declarative IdleTime KDELibs4Support Crash GlobalAccel) 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) 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") 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) # 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 ${KDE4_DBUS_INTERFACES_DIR}/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 ) 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/kscreensaversettings.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 KF5::I18n KF5::IdleTime KF5::GlobalAccel KF5::Notifications KF5::CoreAddons KF5::ConfigGui KF5::Crash KF5::WindowSystem ${X11_LIBRARIES} ${X11_Xcursor_LIB} 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) generate_export_header(KScreenLocker BASE_NAME KScreenLocker EXPORT_MACRO_NAME KSCREENLOCKER_EXPORT EXPORT_FILE_NAME KScreenLocker/kscreenlocker_export.h ) ecm_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") ecm_configure_package_config_file("${CMAKE_CURRENT_SOURCE_DIR}/KScreenLockerConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/KScreenLockerConfig.cmake" INSTALL_DESTINATION "${CMAKECONFIG_INSTALL_PREFIX}/KScreenLocker") 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 updaters/kscreenlocker.upd DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) install(PROGRAMS updaters/ksreenlocker_5_3_separate_autologin.pl DESTINATION ${KDE_INSTALL_DATADIR}/kconf_update) add_subdirectory(autotests) add_subdirectory(tests) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/greeter/CMakeLists.txt b/greeter/CMakeLists.txt index c12ec68..8d6a504 100644 --- a/greeter/CMakeLists.txt +++ b/greeter/CMakeLists.txt @@ -1,48 +1,47 @@ 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 ) 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::Solid KF5::Package KF5::Crash KF5::I18n KF5::ConfigGui KF5::Declarative KF5::QuickAddons + KF5::WindowSystem Qt5::Quick Qt5::Qml Qt5::X11Extras ${X11_LIBRARIES} - KF5::KDELibs4Support KF5::WaylandClient Wayland::Client ) 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 970ae75..6cb49d6 100644 --- a/greeter/greeterapp.cpp +++ b/greeter/greeterapp.cpp @@ -1,602 +1,636 @@ /******************************************************************** 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" // KDE #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 // Wayland #include #include // X11 #include #include #include // #include // 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_authenticator(new Authenticator(this)) , m_graceTime(0) , m_noLock(false) { connect(m_authenticator, &Authenticator::succeeded, this, &QCoreApplication::quit); initialize(); connect(this, &UnlockApp::screenAdded, this, &UnlockApp::desktopResized); if (QX11Info::isPlatformX11()) { installNativeEventFilter(new FocusOutEventFilter); } } UnlockApp::~UnlockApp() { 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(); } } 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"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { package.setPath(packageName); } if (!KScreenSaverSettings::theme().isEmpty()) { package.setPath(KScreenSaverSettings::theme()); } m_mainQmlPath = QUrl::fromLocalFile(package.filePath("lockscreenmainscript")); 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::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 const bool canLogout = KAuthorized::authorizeKAction(QStringLiteral("logout")) && KAuthorized::authorize(QStringLiteral("logout")); - const QSet spdMethods = Solid::PowerManagement::supportedSleepStates(); for (int i = m_views.count(); i < nScreens; ++i) { connect(QGuiApplication::screens()[i], &QObject::destroyed, this, &UnlockApp::desktopResized); // 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(); // overwrite the factory set by kdeclarative auto oldFactory = view->engine()->networkAccessManagerFactory(); view->engine()->setNetworkAccessManagerFactory(nullptr); delete oldFactory; view->engine()->setNetworkAccessManagerFactory(new NoAccessNetworkAccessManagerFactory); 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("backgroundPath"), KScreenSaverSettings::themeBackground()); context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_interfaceVersion"), 2); context->setContextProperty(QStringLiteral("org_kde_plasma_screenlocker_greeter_view"), view); 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); QQmlProperty lockProperty(view->rootObject(), QStringLiteral("locked")); lockProperty.write(m_immediateLock || (!m_noLock && !m_delayedLockTimer)); QQmlProperty sleepProperty(view->rootObject(), QStringLiteral("suspendToRamSupported")); - sleepProperty.write(spdMethods.contains(Solid::PowerManagement::SuspendState)); - if (spdMethods.contains(Solid::PowerManagement::SuspendState) && - view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToRam()").constData()) != -1) { + sleepProperty.write(m_canSuspend); + if (view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToRam()").constData()) != -1) { connect(view->rootObject(), SIGNAL(suspendToRam()), SLOT(suspendToRam())); } QQmlProperty hibernateProperty(view->rootObject(), QStringLiteral("suspendToDiskSupported")); - hibernateProperty.write(spdMethods.contains(Solid::PowerManagement::HibernateState)); - if (spdMethods.contains(Solid::PowerManagement::HibernateState) && - view->rootObject()->metaObject()->indexOfSignal(QMetaObject::normalizedSignature("suspendToDisk()").constData()) != -1) { + hibernateProperty.write(m_canHibernate); + if (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()); } 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); } 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) { view->requestActivate(); 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(); - Solid::PowerManagement::requestSleep(Solid::PowerManagement::SuspendState, 0, 0); + org_kde_ksld_suspendSystem(m_ksldInterface); } void UnlockApp::suspendToDisk() { if (m_ignoreRequests) { return; } m_ignoreRequests = true; m_resetRequestIgnoreTimer->start(); - Solid::PowerManagement::requestSleep(Solid::PowerManagement::HibernateState, 0, 0); + 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::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; } 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 + 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 8a9ebe0..380bea8 100644 --- a/greeter/greeterapp.h +++ b/greeter/greeterapp.h @@ -1,106 +1,111 @@ /******************************************************************** 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 UnlockApp : public QGuiApplication { Q_OBJECT public: explicit UnlockApp(int &argc, char **argv); virtual ~UnlockApp(); void setTesting(bool enable); void setImmediateLock(bool immediateLock); void lockImmediately(); void setGraceTime(int milliseconds); void setNoLock(bool noLock); void setKsldSocket(int socket); 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); public Q_SLOTS: void desktopResized(); protected: virtual bool eventFilter(QObject *obj, QEvent *event); 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); 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_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; }; } // namespace #endif // SCREENLOCKER_GREETERAPP_H diff --git a/powermanagement.cpp b/powermanagement.cpp new file mode 100644 index 0000000..b0ef92f --- /dev/null +++ b/powermanagement.cpp @@ -0,0 +1,193 @@ +/******************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 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 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#include "powermanagement.h" + +#include +#include +#include + +static const QString s_fdoPowerService = QStringLiteral("org.freedesktop.PowerManagement"); +static const QString s_fdoPowerPath = QStringLiteral("/org/freedesktop/PowerManagement"); + +class PowerManagementInstance : public PowerManagement +{ + Q_OBJECT +public: + explicit PowerManagementInstance() : PowerManagement() {} +}; +Q_GLOBAL_STATIC(PowerManagementInstance, s_instance) + +class PowerManagement::Private +{ +public: + Private(PowerManagement *q); + void update(); + void setCanSuspend(bool set); + void setCanHibernate(bool set); + + bool serviceRegistered; + bool canSuspend; + bool canHibernate; + QScopedPointer fdoPowerServiceWatcher; + +private: + void updateProperty(const QString &dbusName, void (Private::*setter)(bool)); + PowerManagement *q; +}; + +PowerManagement::Private::Private(PowerManagement *q) + : serviceRegistered(false) + , canSuspend(false) + , canHibernate(false) + , fdoPowerServiceWatcher(new QDBusServiceWatcher(s_fdoPowerService, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration | QDBusServiceWatcher::WatchForRegistration)) + , q(q) +{ +} + +void PowerManagement::Private::update() +{ + serviceRegistered = true; + updateProperty(QStringLiteral("CanSuspend"), &Private::setCanSuspend); + updateProperty(QStringLiteral("CanHibernate"), &Private::setCanHibernate); +} + +void PowerManagement::Private::updateProperty(const QString &dbusName, void (Private::*setter)(bool)) +{ + QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService, + s_fdoPowerPath, + s_fdoPowerService, + dbusName); + QDBusPendingReply reply = QDBusConnection::sessionBus().asyncCall(message); + QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(reply, q); + QObject::connect(callWatcher, &QDBusPendingCallWatcher::finished, q, + [this, setter](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + self->deleteLater(); + if (!reply.isValid()) { + return; + } + ((this)->*setter)(reply.value()); + } + ); + +} + +void PowerManagement::Private::setCanHibernate(bool set) +{ + if (canHibernate == set) { + return; + } + canHibernate = set; + emit q->canHibernateChanged(); +} + +void PowerManagement::Private::setCanSuspend(bool set) +{ + if (canSuspend == set) { + return; + } + canSuspend = set; + emit q->canSuspendChanged(); +} + +PowerManagement *PowerManagement::instance() +{ + return s_instance; +} + +PowerManagement::PowerManagement() + : QObject() + , d(new Private(this)) +{ + connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceRegistered, this, [this] { d->update(); }); + connect(d->fdoPowerServiceWatcher.data(), &QDBusServiceWatcher::serviceUnregistered, this, + [this] { + d->serviceRegistered = false; + d->setCanSuspend(false); + d->setCanHibernate(false); + } + ); + + // check whether the service is registered + QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("/"), + QStringLiteral("org.freedesktop.DBus"), + QStringLiteral("ListNames")); + QDBusPendingReply async = QDBusConnection::sessionBus().asyncCall(message); + QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this); + connect(callWatcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply reply = *self; + self->deleteLater(); + if (!reply.isValid()) { + return; + } + if (reply.value().contains(s_fdoPowerService)) { + d->update(); + } + } + ); +} + +PowerManagement::~PowerManagement() +{ +} + +void PowerManagement::suspend() +{ + if (!d->serviceRegistered) { + return; + } + if (!d->canSuspend) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService, + s_fdoPowerPath, + s_fdoPowerService, + QStringLiteral("Suspend")); + QDBusConnection::sessionBus().asyncCall(message); +} + +void PowerManagement::hibernate() +{ + if (!d->serviceRegistered) { + return; + } + if (!d->canHibernate) { + return; + } + QDBusMessage message = QDBusMessage::createMethodCall(s_fdoPowerService, + s_fdoPowerPath, + s_fdoPowerService, + QStringLiteral("Hibernate")); + QDBusConnection::sessionBus().asyncCall(message); +} + +bool PowerManagement::canSuspend() const +{ + return d->canSuspend; +} + +bool PowerManagement::canHibernate() const +{ + return d->canHibernate; +} + +#include "powermanagement.moc" diff --git a/powermanagement.h b/powermanagement.h new file mode 100644 index 0000000..6fc030c --- /dev/null +++ b/powermanagement.h @@ -0,0 +1,54 @@ +/******************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 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 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#ifndef POWERMANAGEMENT_H +#define POWERMANAGEMENT_H + +#include + +class PowerManagement : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool canSuspend READ canSuspend NOTIFY canSuspendChanged) + Q_PROPERTY(bool canHibernate READ canHibernate NOTIFY canHibernateChanged) +public: + virtual ~PowerManagement(); + + bool canSuspend() const; + bool canHibernate() const; + + static PowerManagement *instance(); + +public Q_SLOTS: + void suspend(); + void hibernate(); + +Q_SIGNALS: + void canSuspendChanged(); + void canHibernateChanged(); + +protected: + explicit PowerManagement(); + +private: + class Private; + QScopedPointer d; +}; + +#endif diff --git a/protocols/ksld.xml b/protocols/ksld.xml index bd4e783..289f28e 100644 --- a/protocols/ksld.xml +++ b/protocols/ksld.xml @@ -1,18 +1,26 @@ - + + + + + + + + + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fb64b2c..3e32156 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,3 +1,8 @@ add_executable(kscreenlocker_test kscreenlocker_main.cpp) target_link_libraries(kscreenlocker_test KScreenLocker Qt5::Widgets KF5::I18n) ecm_mark_as_test(kscreenlocker_test) + +add_definitions(-DQML_PATH="${CMAKE_CURRENT_SOURCE_DIR}/powermanagement.qml") +add_executable(powermanagment_test powermanagementtest.cpp ../powermanagement.cpp) +target_link_libraries(powermanagment_test Qt5::Gui Qt5::DBus Qt5::Quick) +ecm_mark_as_test(powermanagment_test) diff --git a/tests/powermanagement.qml b/tests/powermanagement.qml new file mode 100644 index 0000000..0659d2b --- /dev/null +++ b/tests/powermanagement.qml @@ -0,0 +1,39 @@ +/******************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 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 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +import QtQuick 2.0 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 1.4 + +Rectangle { + color: "white" + + RowLayout { + Button { + enabled: powerManagement.canSuspend + text: "Suspend" + onClicked: powerManagement.suspend() + } + Button { + enabled: powerManagement.canHibernate + text: "Hibernate" + onClicked: powerManagement.hibernate() + } + } +} diff --git a/tests/powermanagementtest.cpp b/tests/powermanagementtest.cpp new file mode 100644 index 0000000..5f8cc60 --- /dev/null +++ b/tests/powermanagementtest.cpp @@ -0,0 +1,36 @@ +/******************************************************************** +Copyright 2016 Martin Gräßlin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 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 6 of version 3 of the license. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +#include "../powermanagement.h" +#include +#include +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + QQuickView view; + view.rootContext()->setContextProperty("powerManagement", PowerManagement::instance()); + view.setSource(QUrl::fromLocalFile(QStringLiteral(QML_PATH))); + + view.show(); + + return app.exec(); +} diff --git a/waylandserver.cpp b/waylandserver.cpp index 98b8bd7..f827ee6 100644 --- a/waylandserver.cpp +++ b/waylandserver.cpp @@ -1,179 +1,228 @@ /******************************************************************** 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 "waylandserver.h" +#include "powermanagement.h" // ksld #include // Wayland #include #include // KWayland #include // Qt #include // system #include #include #include #include namespace ScreenLocker { static const QString s_plasmaShellService = QStringLiteral("org.kde.plasmashell"); static const QString s_osdServicePath = QStringLiteral("/org/kde/osdService"); static const QString s_osdServiceInterface = QStringLiteral("org.kde.osdService"); WaylandServer::WaylandServer(QObject *parent) : QObject(parent) { // connect to osd service QDBusConnection::sessionBus().connect(s_plasmaShellService, s_osdServicePath, s_osdServiceInterface, QStringLiteral("osdProgress"), this, SLOT(osdProgress(QString,int,QString))); QDBusConnection::sessionBus().connect(s_plasmaShellService, s_osdServicePath, s_osdServiceInterface, QStringLiteral("osdText"), this, SLOT(osdText(QString,QString))); + connect(PowerManagement::instance(), &PowerManagement::canSuspendChanged, this, &WaylandServer::sendCanSuspend); + connect(PowerManagement::instance(), &PowerManagement::canHibernateChanged, this, &WaylandServer::sendCanHibernate); } WaylandServer::~WaylandServer() { stop(); } int WaylandServer::start() { stop(); m_display.reset(new KWayland::Server::Display); m_display->start(KWayland::Server::Display::StartMode::ConnectClientsOnly); if (!m_display->isRunning()) { // failed to start the Wayland server return -1; } int socketPair[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, socketPair) == -1) { // failed creating socket return -1; } fcntl(socketPair[0], F_SETFD, FD_CLOEXEC); m_allowedClient = m_display->createClient(socketPair[0]); if (!m_allowedClient) { // failed creating the Wayland client stop(); close(socketPair[0]); close(socketPair[1]); return -1; } connect(m_allowedClient, &KWayland::Server::ClientConnection::disconnected, this, [this] { m_allowedClient = nullptr; }); - m_interface = wl_global_create(*m_display.data(), &org_kde_ksld_interface, 2, this, bind); + m_interface = wl_global_create(*m_display.data(), &org_kde_ksld_interface, 3, this, bind); return socketPair[1]; } void WaylandServer::stop() { if (m_allowedClient) { m_allowedClient->destroy(); } if (m_interface) { wl_global_destroy(m_interface); m_interface = nullptr; } m_display.reset(); m_allowedClient = nullptr; } void WaylandServer::bind(wl_client *client, void *data, uint32_t version, uint32_t id) { auto s = reinterpret_cast(data); if (client != s->m_allowedClient->client()) { // a proper error would be better wl_client_post_no_memory(client); return; } - wl_resource *r = s->m_allowedClient->createResource(&org_kde_ksld_interface, qMin(version, 2u), id); + wl_resource *r = s->m_allowedClient->createResource(&org_kde_ksld_interface, qMin(version, 3u), id); if (!r) { wl_client_post_no_memory(client); return; } static const struct org_kde_ksld_interface s_interface = { - x11WindowCallback + x11WindowCallback, + suspendSystemCallback, + hibernateSystemCallback }; wl_resource_set_implementation(r, &s_interface, s, unbind); s->addResource(r); + s->sendCanSuspend(); + s->sendCanHibernate(); s->m_allowedClient->flush(); } void WaylandServer::unbind(wl_resource *resource) { reinterpret_cast(wl_resource_get_user_data(resource))->removeResource(resource); } void WaylandServer::x11WindowCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = reinterpret_cast(wl_resource_get_user_data(resource)); if (s->m_allowedClient->client() != client) { return; } emit s->x11WindowAdded(id); } +void WaylandServer::suspendSystemCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + PowerManagement::instance()->suspend(); +} + +void WaylandServer::hibernateSystemCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + PowerManagement::instance()->hibernate(); +} + void WaylandServer::addResource(wl_resource *r) { m_resources.append(r); } void WaylandServer::removeResource(wl_resource *r) { m_resources.removeAll(r); } void WaylandServer::osdProgress(const QString &icon, int percent, const QString &additionalText) { if (!m_allowedClient) { return; } for (auto r : m_resources) { if (wl_resource_get_version(r) < 2) { continue; } org_kde_ksld_send_osdProgress(r, icon.toUtf8().constData(), percent, additionalText.toUtf8().constData()); m_allowedClient->flush(); } } void WaylandServer::osdText(const QString &icon, const QString &additionalText) { if (!m_allowedClient) { return; } for (auto r : m_resources) { if (wl_resource_get_version(r) < 2) { continue; } org_kde_ksld_send_osdText(r, icon.toUtf8().constData(), additionalText.toUtf8().constData()); m_allowedClient->flush(); } } +void WaylandServer::sendCanSuspend() +{ + if (!m_allowedClient) { + return; + } + Q_FOREACH (auto r, m_resources) { + if (wl_resource_get_version(r) < ORG_KDE_KSLD_CANSUSPENDSYSTEM_SINCE_VERSION) { + continue; + } + org_kde_ksld_send_canSuspendSystem(r, PowerManagement::instance()->canSuspend() ? 1 : 0); + } + m_allowedClient->flush(); +} + +void WaylandServer::sendCanHibernate() +{ + if (!m_allowedClient) { + return; + } + Q_FOREACH (auto r, m_resources) { + if (wl_resource_get_version(r) < ORG_KDE_KSLD_CANHIBERNATESYSTEM_SINCE_VERSION) { + continue; + } + org_kde_ksld_send_canHibernateSystem(r, PowerManagement::instance()->canHibernate() ? 1 : 0); + } + m_allowedClient->flush(); +} + } diff --git a/waylandserver.h b/waylandserver.h index 65d97e3..24bc3e4 100644 --- a/waylandserver.h +++ b/waylandserver.h @@ -1,71 +1,75 @@ /******************************************************************** 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 . *********************************************************************/ #ifndef SCREENLOCKER_WAYLANDSERVER_H #define SCREENLOCKER_WAYLANDSERVER_H #include struct wl_client; struct wl_global; struct wl_resource; namespace KWayland { namespace Server { class ClientConnection; class Display; } } namespace ScreenLocker { class WaylandServer : public QObject { Q_OBJECT public: explicit WaylandServer(QObject *parent = nullptr); virtual ~WaylandServer(); int start(); void stop(); Q_SIGNALS: void x11WindowAdded(quint32 window); private Q_SLOTS: void osdProgress(const QString &icon, int percent, const QString &additionalText); void osdText(const QString &icon, const QString &additionalText); private: static void bind(wl_client *client, void *data, uint32_t version, uint32_t id); static void unbind(wl_resource *resource); static void x11WindowCallback(wl_client *client, wl_resource *resource, uint32_t id); + static void suspendSystemCallback(wl_client *client, wl_resource *resource); + static void hibernateSystemCallback(wl_client *client, wl_resource *resource); void addResource(wl_resource *r); void removeResource(wl_resource *r); + void sendCanSuspend(); + void sendCanHibernate(); QScopedPointer m_display; KWayland::Server::ClientConnection *m_allowedClient = nullptr; wl_global *m_interface = nullptr; QList m_resources; }; } #endif