File indexing completed on 2024-05-05 05:38:40
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 #include <private/qtx11extras_p.h> 0027 0028 #include <KAuthorized> 0029 #include <KConfigGroup> 0030 #include <KLocalizedString> 0031 #include <KSharedConfig> 0032 #include <KUser> 0033 #include <KWindowEffects> 0034 #include <KWindowSystem> 0035 #include <KX11Extras> 0036 #include <LayerShellQt/Window> 0037 0038 #include <netwm.h> 0039 #include <stdio.h> 0040 0041 #include <X11/Xatom.h> 0042 #include <X11/Xutil.h> 0043 #include <fixx11h.h> 0044 0045 #include <config-workspace.h> 0046 #include <debug.h> 0047 0048 static const QString s_login1Service = QStringLiteral("org.freedesktop.login1"); 0049 static const QString s_login1Path = QStringLiteral("/org/freedesktop/login1"); 0050 static const QString s_dbusPropertiesInterface = QStringLiteral("org.freedesktop.DBus.Properties"); 0051 static const QString s_login1ManagerInterface = QStringLiteral("org.freedesktop.login1.Manager"); 0052 static const QString s_login1RebootToFirmwareSetup = QStringLiteral("RebootToFirmwareSetup"); 0053 0054 static const QString s_packageKitService = QStringLiteral("org.freedesktop.PackageKit"); 0055 static const QString s_packageKitPath = QStringLiteral("/org/freedesktop/PackageKit"); 0056 static const QString s_packageKitOfflineInterface = QStringLiteral("org.freedesktop.PackageKit.Offline"); 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 } 0074 } 0075 0076 setResizeMode(PlasmaQuick::QuickViewSharedEngine::SizeRootObjectToView); 0077 0078 // Qt doesn't set this on unmanaged windows 0079 // FIXME: or does it? 0080 if (KWindowSystem::isPlatformX11()) { 0081 XChangeProperty(QX11Info::display(), 0082 winId(), 0083 XInternAtom(QX11Info::display(), "WM_WINDOW_ROLE", False), 0084 XA_STRING, 0085 8, 0086 PropModeReplace, 0087 (unsigned char *)"logoutdialog", 0088 strlen("logoutdialog")); 0089 0090 XClassHint classHint; 0091 classHint.res_name = const_cast<char *>("ksmserver-logout-greeter"); 0092 classHint.res_class = const_cast<char *>("ksmserver-logout-greeter"); 0093 XSetClassHint(QX11Info::display(), winId(), &classHint); 0094 } 0095 0096 // QQuickView *windowContainer = QQuickView::createWindowContainer(m_view, this); 0097 // windowContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); 0098 QQmlContext *context = rootContext(); 0099 context->setContextProperty(QStringLiteral("maysd"), m_session.canShutdown()); 0100 context->setContextProperty(QStringLiteral("sdtype"), sdtype); 0101 0102 QQmlPropertyMap *mapShutdownType = new QQmlPropertyMap(this); 0103 mapShutdownType->insert(QStringLiteral("ShutdownTypeDefault"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeDefault)); 0104 mapShutdownType->insert(QStringLiteral("ShutdownTypeNone"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeNone)); 0105 mapShutdownType->insert(QStringLiteral("ShutdownTypeReboot"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeReboot)); 0106 mapShutdownType->insert(QStringLiteral("ShutdownTypeHalt"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeHalt)); 0107 mapShutdownType->insert(QStringLiteral("ShutdownTypeLogout"), QVariant::fromValue<int>(KWorkSpace::ShutdownTypeLogout)); 0108 context->setContextProperty(QStringLiteral("ShutdownType"), mapShutdownType); 0109 0110 QQmlPropertyMap *mapSpdMethods = new QQmlPropertyMap(this); 0111 mapSpdMethods->insert(QStringLiteral("StandbyState"), m_session.canSuspend()); 0112 mapSpdMethods->insert(QStringLiteral("SuspendState"), m_session.canSuspend()); 0113 mapSpdMethods->insert(QStringLiteral("HibernateState"), m_session.canHibernate()); 0114 context->setContextProperty(QStringLiteral("spdMethods"), mapSpdMethods); 0115 context->setContextProperty(QStringLiteral("canLogout"), m_session.canLogout()); 0116 0117 // Trying to access a non-existent context property throws an error, always create the property and then update it later 0118 context->setContextProperty("rebootToFirmwareSetup", false); 0119 0120 QDBusMessage message = QDBusMessage::createMethodCall(s_login1Service, s_login1Path, s_dbusPropertiesInterface, QStringLiteral("Get")); 0121 message.setArguments({s_login1ManagerInterface, s_login1RebootToFirmwareSetup}); 0122 QDBusPendingReply<QVariant> call = QDBusConnection::systemBus().asyncCall(message); 0123 QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(call, this); 0124 connect(callWatcher, &QDBusPendingCallWatcher::finished, context, [context](QDBusPendingCallWatcher *watcher) { 0125 QDBusPendingReply<QVariant> reply = *watcher; 0126 watcher->deleteLater(); 0127 0128 if (reply.value().toBool()) { 0129 context->setContextProperty("rebootToFirmwareSetup", true); 0130 } 0131 }); 0132 0133 context->setContextProperty("softwareUpdatePending", false); 0134 checkSoftwareUpdatePending(); 0135 0136 // TODO KF6 remove, used to read "BootManager" from kdmrc 0137 context->setContextProperty(QStringLiteral("bootManager"), QStringLiteral("None")); 0138 0139 // TODO KF6 remove. Unused 0140 context->setContextProperty(QStringLiteral("choose"), false); 0141 0142 // TODO KF6 remove, used to call KDisplayManager::bootOptions 0143 QStringList rebootOptions; 0144 int def = 0; 0145 QQmlPropertyMap *rebootOptionsMap = new QQmlPropertyMap(this); 0146 rebootOptionsMap->insert(QStringLiteral("options"), QVariant::fromValue(rebootOptions)); 0147 rebootOptionsMap->insert(QStringLiteral("default"), QVariant::fromValue(def)); 0148 context->setContextProperty(QStringLiteral("rebootOptions"), rebootOptionsMap); 0149 0150 // engine stuff 0151 engine()->rootContext()->setContextObject(new KLocalizedContext(engine().get())); 0152 engine()->setProperty("_kirigamiTheme", QStringLiteral("KirigamiPlasmaStyle")); 0153 } 0154 0155 void KSMShutdownDlg::init(const KPackage::Package &package) 0156 { 0157 rootContext()->setContextProperty(QStringLiteral("screenGeometry"), screen()->geometry()); 0158 0159 const QString fileName = package.filePath("logoutmainscript"); 0160 0161 if (QFile::exists(fileName)) { 0162 setSource(package.fileUrl("logoutmainscript")); 0163 } else { 0164 qCWarning(LOGOUT_GREETER) << "Couldn't find a theme for the Shutdown dialog" << fileName; 0165 return; 0166 } 0167 0168 if (!errors().isEmpty()) { 0169 qCWarning(LOGOUT_GREETER) << errors(); 0170 } 0171 0172 connect(rootObject(), SIGNAL(logoutRequested()), SLOT(slotLogout())); 0173 connect(rootObject(), SIGNAL(haltRequested()), SLOT(slotHalt())); 0174 connect(rootObject(), SIGNAL(suspendRequested(int)), SLOT(slotSuspend(int))); 0175 connect(rootObject(), SIGNAL(rebootRequested()), SLOT(slotReboot())); 0176 connect(rootObject(), SIGNAL(rebootRequested2(int)), SLOT(slotReboot(int))); 0177 connect(rootObject(), SIGNAL(cancelRequested()), SLOT(reject())); 0178 connect(rootObject(), SIGNAL(lockScreenRequested()), SLOT(slotLockScreen())); 0179 connect(rootObject(), SIGNAL(cancelSoftwareUpdateRequested()), SLOT(slotCancelSoftwareUpdate())); 0180 0181 connect(screen(), &QScreen::geometryChanged, this, [this] { 0182 setGeometry(screen()->geometry()); 0183 }); 0184 0185 // decide in backgroundcontrast whether doing things darker or lighter 0186 // set backgroundcontrast here, because in QEvent::PlatformSurface 0187 // is too early and we don't have the root object yet 0188 const QColor backgroundColor = rootObject() ? rootObject()->property("backgroundColor").value<QColor>() : QColor(); 0189 KWindowEffects::enableBackgroundContrast(this, true, 0.4, (backgroundColor.value() > 128 ? 1.6 : 0.3), 1.7); 0190 if (m_windowed) { 0191 show(); 0192 } else { 0193 showFullScreen(); 0194 setFlag(Qt::FramelessWindowHint); 0195 } 0196 requestActivate(); 0197 0198 if (KWindowSystem::isPlatformX11()) { 0199 KX11Extras::setState(winId(), NET::SkipTaskbar | NET::SkipPager); 0200 } 0201 0202 setKeyboardGrabEnabled(true); 0203 KWindowEffects::enableBlurBehind(this, true); 0204 } 0205 0206 void KSMShutdownDlg::resizeEvent(QResizeEvent *e) 0207 { 0208 PlasmaQuick::QuickViewSharedEngine::resizeEvent(e); 0209 0210 if (KX11Extras::compositingActive()) { 0211 // TODO: reenable window mask when we are without composite? 0212 // clearMask(); 0213 } else { 0214 // setMask(m_view->mask()); 0215 } 0216 } 0217 0218 void KSMShutdownDlg::slotLogout() 0219 { 0220 m_session.requestLogout(SessionManagement::ConfirmationMode::Skip); 0221 accept(); 0222 } 0223 0224 void KSMShutdownDlg::slotReboot() 0225 { 0226 // no boot option selected -> current 0227 m_bootOption.clear(); 0228 m_session.requestReboot(SessionManagement::ConfirmationMode::Skip); 0229 accept(); 0230 } 0231 0232 void KSMShutdownDlg::slotReboot(int opt) 0233 { 0234 if (int(rebootOptions.size()) > opt) 0235 m_bootOption = rebootOptions[opt]; 0236 m_session.requestReboot(SessionManagement::ConfirmationMode::Skip); 0237 accept(); 0238 } 0239 0240 void KSMShutdownDlg::slotLockScreen() 0241 { 0242 m_bootOption.clear(); 0243 m_session.lock(); 0244 reject(); 0245 } 0246 0247 void KSMShutdownDlg::slotHalt() 0248 { 0249 m_bootOption.clear(); 0250 m_session.requestShutdown(SessionManagement::ConfirmationMode::Skip); 0251 accept(); 0252 } 0253 0254 void KSMShutdownDlg::slotSuspend(int spdMethod) 0255 { 0256 m_bootOption.clear(); 0257 switch (spdMethod) { 0258 case 1: // Solid::PowerManagement::StandbyState: 0259 case 2: // Solid::PowerManagement::SuspendState: 0260 m_session.suspend(); 0261 break; 0262 case 4: // Solid::PowerManagement::HibernateState: 0263 m_session.hibernate(); 0264 break; 0265 } 0266 reject(); 0267 } 0268 0269 void KSMShutdownDlg::slotCancelSoftwareUpdate() 0270 { 0271 QDBusMessage packageKitMessage = 0272 QDBusMessage::createMethodCall(s_packageKitService, s_packageKitPath, s_packageKitOfflineInterface, QStringLiteral("Cancel")); 0273 QDBusPendingReply<> packageKitCall = QDBusConnection::systemBus().asyncCall(packageKitMessage); 0274 QDBusPendingCallWatcher *packageKitCallWatcher = new QDBusPendingCallWatcher(packageKitCall, this); 0275 connect(packageKitCallWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0276 QDBusPendingReply<> reply = *watcher; 0277 watcher->deleteLater(); 0278 0279 if (reply.isError()) { 0280 qWarning() << "Failed to cancel pending software update" << reply.error().message(); 0281 } else { 0282 checkSoftwareUpdatePending(); 0283 } 0284 }); 0285 } 0286 0287 void KSMShutdownDlg::checkSoftwareUpdatePending() 0288 { 0289 QDBusMessage packageKitMessage = QDBusMessage::createMethodCall(s_packageKitService, s_packageKitPath, s_dbusPropertiesInterface, QStringLiteral("GetAll")); 0290 packageKitMessage.setArguments({s_packageKitOfflineInterface}); 0291 QDBusPendingReply<QVariantMap> packageKitCall = QDBusConnection::systemBus().asyncCall(packageKitMessage); 0292 QDBusPendingCallWatcher *packageKitCallWatcher = new QDBusPendingCallWatcher(packageKitCall, this); 0293 connect(packageKitCallWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0294 QDBusPendingReply<QVariantMap> reply = *watcher; 0295 watcher->deleteLater(); 0296 0297 if (reply.isError() || !rootContext()) { 0298 return; 0299 } 0300 0301 const QVariantMap properties = reply.value(); 0302 if (properties.value(QStringLiteral("UpdateTriggered")).toBool() || properties.value(QStringLiteral("UpgradeTriggered")).toBool()) { 0303 rootContext()->setContextProperty("softwareUpdatePending", true); 0304 } 0305 }); 0306 } 0307 0308 void KSMShutdownDlg::accept() 0309 { 0310 Q_EMIT accepted(); 0311 } 0312 0313 void KSMShutdownDlg::reject() 0314 { 0315 Q_EMIT rejected(); 0316 }