File indexing completed on 2024-04-28 16:54:46

0001 /*
0002     SPDX-FileCopyrightText: 2000 Matthias Ettrich <ettrich@kde.org>
0003     SPDX-FileCopyrightText: 2007 Urs Wolfer <uwolfer @ kde.org>
0004 
0005     SPDX-License-Identifier: MIT
0006 */
0007 
0008 #include "shutdowndlg.h"
0009 
0010 #include <QApplication>
0011 #include <QDBusConnection>
0012 #include <QDBusMessage>
0013 #include <QDBusPendingCall>
0014 #include <QDBusPendingCallWatcher>
0015 #include <QDBusPendingReply>
0016 #include <QDBusVariant>
0017 #include <QFile>
0018 #include <QPainter>
0019 #include <QQmlContext>
0020 #include <QQmlEngine>
0021 #include <QQmlPropertyMap>
0022 #include <QQuickItem>
0023 #include <QQuickView>
0024 #include <QStandardPaths>
0025 #include <QTimer>
0026 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0027 #include <private/qtx11extras_p.h>
0028 #else
0029 #include <QX11Info>
0030 #endif
0031 
0032 #include <KAuthorized>
0033 #include <KConfigGroup>
0034 #include <KLocalizedString>
0035 #include <KSharedConfig>
0036 #include <KUser>
0037 #include <KWindowEffects>
0038 #include <KWindowSystem>
0039 #include <KX11Extras>
0040 #include <LayerShellQt/Window>
0041 
0042 #include <netwm.h>
0043 #include <stdio.h>
0044 
0045 #include <X11/Xatom.h>
0046 #include <X11/Xutil.h>
0047 #include <fixx11h.h>
0048 
0049 #include <config-workspace.h>
0050 #include <debug.h>
0051 
0052 static const QString s_login1Service = QStringLiteral("org.freedesktop.login1");
0053 static const QString s_login1Path = QStringLiteral("/org/freedesktop/login1");
0054 static const QString s_dbusPropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties");
0055 static const QString s_login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager");
0056 static const QString s_login1RebootToFirmwareSetup = QStringLiteral("RebootToFirmwareSetup");
0057 
0058 KSMShutdownDlg::KSMShutdownDlg(QWindow *parent, KWorkSpace::ShutdownType sdtype, QScreen *screen)
0059     : QuickViewSharedEngine(parent)
0060     , m_result(false)
0061 // this is a WType_Popup on purpose. Do not change that! Not
0062 // having a popup here has severe side effects.
0063 {
0064     // window stuff
0065     setColor(QColor(Qt::transparent));
0066     setScreen(screen);
0067 
0068     if (KWindowSystem::isPlatformWayland() && !m_windowed) {
0069         if (auto w = LayerShellQt::Window::get(this)) {
0070             w->setKeyboardInteractivity(LayerShellQt::Window::KeyboardInteractivityExclusive);
0071             w->setExclusiveZone(-1);
0072             w->setLayer(LayerShellQt::Window::LayerOverlay);
0073             w->setDesiredOutput(screen);
0074         }
0075     }
0076 
0077     setResizeMode(KQuickAddons::QuickViewSharedEngine::SizeRootObjectToView);
0078 
0079     // Qt doesn't set this on unmanaged windows
0080     // FIXME: or does it?
0081     if (KWindowSystem::isPlatformX11()) {
0082         XChangeProperty(QX11Info::display(),
0083                         winId(),
0084                         XInternAtom(QX11Info::display(), "WM_WINDOW_ROLE", False),
0085                         XA_STRING,
0086                         8,
0087                         PropModeReplace,
0088                         (unsigned char *)"logoutdialog",
0089                         strlen("logoutdialog"));
0090 
0091         XClassHint classHint;
0092         classHint.res_name = const_cast<char *>("ksmserver-logout-greeter");
0093         classHint.res_class = const_cast<char *>("ksmserver-logout-greeter");
0094         XSetClassHint(QX11Info::display(), winId(), &classHint);
0095     }
0096 
0097     // QQuickView *windowContainer = QQuickView::createWindowContainer(m_view, this);
0098     // windowContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
0099     QQmlContext *context = rootContext();
0100     context->setContextProperty(QStringLiteral("maysd"), m_session.canShutdown());
0101     context->setContextProperty(QStringLiteral("sdtype"), sdtype);
0102 
0103     QQmlPropertyMap *mapShutdownType = new QQmlPropertyMap(this);
0104     mapShutdownType->insert(QStringLiteral("ShutdownTypeDefault"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeDefault));
0105     mapShutdownType->insert(QStringLiteral("ShutdownTypeNone"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeNone));
0106     mapShutdownType->insert(QStringLiteral("ShutdownTypeReboot"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeReboot));
0107     mapShutdownType->insert(QStringLiteral("ShutdownTypeHalt"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeHalt));
0108     mapShutdownType->insert(QStringLiteral("ShutdownTypeLogout"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeLogout));
0109     context->setContextProperty(QStringLiteral("ShutdownType"), mapShutdownType);
0110 
0111     QQmlPropertyMap *mapSpdMethods = new QQmlPropertyMap(this);
0112     mapSpdMethods->insert(QStringLiteral("StandbyState"), m_session.canSuspend());
0113     mapSpdMethods->insert(QStringLiteral("SuspendState"), m_session.canSuspend());
0114     mapSpdMethods->insert(QStringLiteral("HibernateState"), m_session.canHibernate());
0115     context->setContextProperty(QStringLiteral("spdMethods"), mapSpdMethods);
0116     context->setContextProperty(QStringLiteral("canLogout"), m_session.canLogout());
0117 
0118     // Trying to access a non-existent context property throws an error, always create the property and then update it later
0119     context->setContextProperty("rebootToFirmwareSetup", false);
0120 
0121     QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, s_login1Path, s_dbusPropertiesInterface, QStringLiteral("Get"));
0122     message.setArguments({s_login1ManagerInterface, s_login1RebootToFirmwareSetup});
0123     QDBusPendingReply<QVariant> call = QDBusConnection::systemBus().asyncCall(message);
0124     QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this);
0125     connect(callWatcher, &QDBusPendingCallWatcher::finished, context, [context](QDBusPendingCallWatcher *watcher) {
0126         QDBusPendingReply<QVariant> reply = *watcher;
0127         watcher->deleteLater();
0128 
0129         if (reply.value().toBool()) {
0130             context->setContextProperty("rebootToFirmwareSetup", true);
0131         }
0132     });
0133 
0134     // TODO KF6 remove, used to read "BootManager" from kdmrc
0135     context->setContextProperty(QStringLiteral("bootManager"), QStringLiteral("None"));
0136 
0137     // TODO KF6 remove. Unused
0138     context->setContextProperty(QStringLiteral("choose"), false);
0139 
0140     // TODO KF6 remove, used to call KDisplayManager::bootOptions
0141     QStringList rebootOptions;
0142     int def = 0;
0143     QQmlPropertyMap *rebootOptionsMap = new QQmlPropertyMap(this);
0144     rebootOptionsMap->insert(QStringLiteral("options"), QVariant::fromValue(rebootOptions));
0145     rebootOptionsMap->insert(QStringLiteral("default"), QVariant::fromValue(def));
0146     context->setContextProperty(QStringLiteral("rebootOptions"), rebootOptionsMap);
0147 
0148     // engine stuff
0149     engine()->rootContext()->setContextObject(new KLocalizedContext(engine()));
0150 }
0151 
0152 void KSMShutdownDlg::init(const KPackage::Package &package)
0153 {
0154     rootContext()->setContextProperty(QStringLiteral("screenGeometry"), screen()->geometry());
0155 
0156     const QString fileName = package.filePath("logoutmainscript");
0157 
0158     if (QFile::exists(fileName)) {
0159         setSource(package.fileUrl("logoutmainscript"));
0160     } else {
0161         qCWarning(LOGOUT_GREETER) << "Couldn't find a theme for the Shutdown dialog" << fileName;
0162         return;
0163     }
0164 
0165     if (!errors().isEmpty()) {
0166         qCWarning(LOGOUT_GREETER) << errors();
0167     }
0168 
0169     connect(rootObject(), SIGNAL(logoutRequested()), SLOT(slotLogout()));
0170     connect(rootObject(), SIGNAL(haltRequested()), SLOT(slotHalt()));
0171     connect(rootObject(), SIGNAL(suspendRequested(int)), SLOT(slotSuspend(int)));
0172     connect(rootObject(), SIGNAL(rebootRequested()), SLOT(slotReboot()));
0173     connect(rootObject(), SIGNAL(rebootRequested2(int)), SLOT(slotReboot(int)));
0174     connect(rootObject(), SIGNAL(cancelRequested()), SLOT(reject()));
0175     connect(rootObject(), SIGNAL(lockScreenRequested()), SLOT(slotLockScreen()));
0176 
0177     connect(screen(), &QScreen::geometryChanged, this, [this] {
0178         setGeometry(screen()->geometry());
0179     });
0180 
0181     // decide in backgroundcontrast whether doing things darker or lighter
0182     // set backgroundcontrast here, because in QEvent::PlatformSurface
0183     // is too early and we don't have the root object yet
0184     const QColor backgroundColor = rootObject() ? rootObject()->property("backgroundColor").value<QColor>() : QColor();
0185     KWindowEffects::enableBackgroundContrast(this, true, 0.4, (backgroundColor.value() > 128 ? 1.6 : 0.3), 1.7);
0186     if (m_windowed) {
0187         show();
0188     } else {
0189         showFullScreen();
0190         setFlag(Qt::FramelessWindowHint);
0191     }
0192     requestActivate();
0193 
0194     KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager);
0195 
0196     setKeyboardGrabEnabled(true);
0197     KWindowEffects::enableBlurBehind(this, true);
0198 }
0199 
0200 void KSMShutdownDlg::resizeEvent(QResizeEvent *e)
0201 {
0202     KQuickAddons::QuickViewSharedEngine::resizeEvent(e);
0203 
0204     if (KX11Extras::compositingActive()) {
0205         // TODO: reenable window mask when we are without composite?
0206         //        clearMask();
0207     } else {
0208         //        setMask(m_view->mask());
0209     }
0210 }
0211 
0212 void KSMShutdownDlg::slotLogout()
0213 {
0214     m_session.requestLogout(SessionManagement::ConfirmationMode::Skip);
0215     accept();
0216 }
0217 
0218 void KSMShutdownDlg::slotReboot()
0219 {
0220     // no boot option selected -> current
0221     m_bootOption.clear();
0222     m_session.requestReboot(SessionManagement::ConfirmationMode::Skip);
0223     accept();
0224 }
0225 
0226 void KSMShutdownDlg::slotReboot(int opt)
0227 {
0228     if (int(rebootOptions.size()) > opt)
0229         m_bootOption = rebootOptions[opt];
0230     m_session.requestReboot(SessionManagement::ConfirmationMode::Skip);
0231     accept();
0232 }
0233 
0234 void KSMShutdownDlg::slotLockScreen()
0235 {
0236     m_bootOption.clear();
0237     m_session.lock();
0238     reject();
0239 }
0240 
0241 void KSMShutdownDlg::slotHalt()
0242 {
0243     m_bootOption.clear();
0244     m_session.requestShutdown(SessionManagement::ConfirmationMode::Skip);
0245     accept();
0246 }
0247 
0248 void KSMShutdownDlg::slotSuspend(int spdMethod)
0249 {
0250     m_bootOption.clear();
0251     switch (spdMethod) {
0252     case 1: // Solid::PowerManagement::StandbyState:
0253     case 2: // Solid::PowerManagement::SuspendState:
0254         m_session.suspend();
0255         break;
0256     case 4: // Solid::PowerManagement::HibernateState:
0257         m_session.hibernate();
0258         break;
0259     }
0260     reject();
0261 }
0262 
0263 void KSMShutdownDlg::accept()
0264 {
0265     Q_EMIT accepted();
0266 }
0267 
0268 void KSMShutdownDlg::reject()
0269 {
0270     Q_EMIT rejected();
0271 }