Commit 68538b6d authored by David Edmundson's avatar David Edmundson

[ksmserver] Launch logout greeter with DBus methods

Summary:
Removing this is part of preparations for ksmserver to become just an X
session manager. See T9779.

Instead of ksmserver opening the greeter with a tonne of args and then
having a socket to watch for changes, the new mechanism is that we fire
and forget the prompt. Then the logout prompt calls back into ksmserver
with the request. Simplifies code greatly allows for moving.

KSMserver's shutdown DBus method remain on ksmserver for compatibility,
but ultimately spawning the prompt will move to libkworkspace and called
from the session directly.

QML API in the logout greeter remains mostly untouched.

Behavioural changes are minor:
There was a QML property "choose" exposed to the greeter if the default
shutdown option is set to None. Though we don't allow this in the KCM
and our default prompt doesn't use it.

We no longer block a user from saving a session whilst a logout prompt
is active.

Test Plan:
Logged out
Shutdown

Also ran with old ksmserver, still got a prompt

Reviewers: #plasma

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D16066
parent d2737d2b
......@@ -44,7 +44,7 @@ set(klauncher_xml ${KINIT_DBUS_INTERFACES_DIR}/kf5_org.kde.KLauncher.xml)
qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ${klauncher_xml} klauncher_interface )
qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/org.kde.screensaver.xml kscreenlocker_interface )
qt5_add_dbus_interface( ksmserver_KDEINIT_SRCS org.kde.LogoutPrompt.xml logoutprompt_interface)
qt5_add_dbus_adaptor( ksmserver_KDEINIT_SRCS org.kde.KSMServerInterface.xml server.h KSMServer )
......
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.LogoutPrompt">
<method name="promptLogout"/>
<method name="promptShutDown"/>
<method name="promptReboot"/>
</interface>
</node>
......@@ -624,7 +624,6 @@ KSMServer::KSMServer( const QString& windowManager, InitFlags flags )
shutdownType = KWorkSpace::ShutdownTypeNone;
state = Idle;
dialogActive = false;
saveSession = false;
wmPhase1WaitingCount = 0;
KConfigGroup config(KSharedConfig::openConfig(), "General");
......
......@@ -239,7 +239,6 @@ private:
ClosingSubSession, KillingSubSession, RestoringSubSession
};
State state;
bool dialogActive;
bool saveSession;
int wmPhase1WaitingCount;
int saveType;
......@@ -289,7 +288,6 @@ private:
QList<KSMClient*> clientsToSave;
int sockets[2];
friend bool readFromPipe(int pipe);
};
#endif
......@@ -77,6 +77,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "client.h"
#include <solid/powermanagement.h>
#include "logoutprompt_interface.h"
#include <QDesktopWidget>
#include <QX11Info>
......@@ -108,34 +109,12 @@ bool KSMServer::isShuttingDown() const
return state >= Shutdown;
}
bool readFromPipe(int pipe)
{
QFile readPipe;
if (!readPipe.open(pipe, QIODevice::ReadOnly)) {
return false;
}
QByteArray result = readPipe.readLine();
if (result.isEmpty()) {
return false;
}
bool ok = false;
const int number = result.toInt(&ok);
if (!ok) {
return false;
}
KSMServer::self()->shutdownType = KWorkSpace::ShutdownType(number);
return true;
}
void KSMServer::shutdown( KWorkSpace::ShutdownConfirm confirm,
KWorkSpace::ShutdownType sdtype, KWorkSpace::ShutdownMode sdmode )
{
qCDebug(KSMSERVER) << "Shutdown called with confirm " << confirm
<< " type " << sdtype << " and mode " << sdmode;
pendingShutdown.stop();
if( dialogActive )
return;
if( state >= Shutdown ) // already performing shutdown
return;
if( state != Idle ) // performing startup
......@@ -160,129 +139,27 @@ void KSMServer::shutdown( KWorkSpace::ShutdownConfirm confirm,
(confirm == KWorkSpace::ShutdownConfirmYes) ? false :
(confirm == KWorkSpace::ShutdownConfirmNo) ? true :
!cg.readEntry( "confirmLogout", true );
bool choose = false;
bool maysd = false;
if (cg.readEntry( "offerShutdown", true ) && KDisplayManager().canShutdown())
maysd = true;
if (!maysd) {
if (sdtype != KWorkSpace::ShutdownTypeNone &&
sdtype != KWorkSpace::ShutdownTypeDefault &&
logoutConfirmed)
return; /* unsupported fast shutdown */
sdtype = KWorkSpace::ShutdownTypeNone;
} else if (sdtype == KWorkSpace::ShutdownTypeDefault) {
sdtype = (KWorkSpace::ShutdownType)
cg.readEntry( "shutdownType", (int)KWorkSpace::ShutdownTypeNone );
choose = true;
}
if (sdmode == KWorkSpace::ShutdownModeDefault)
sdmode = KWorkSpace::ShutdownModeInteractive;
qCDebug(KSMSERVER) << "After modifications confirm is " << confirm
<< " type is " << sdtype << " and mode " << sdmode;
QString bopt;
if ( !logoutConfirmed ) {
int pipeFds[2];
if (pipe(pipeFds) != 0) {
return;
}
QProcess *p = new QProcess(this);
p->setProgram(QStringLiteral(LOGOUT_GREETER_BIN));
QStringList arguments;
if (maysd) {
arguments << QStringLiteral("--shutdown-allowed");
}
if (choose) {
arguments << QStringLiteral("--choose");
}
if (sdtype != KWorkSpace::ShutdownTypeDefault) {
arguments << QStringLiteral("--mode");
switch (sdtype) {
case KWorkSpace::ShutdownTypeHalt:
arguments << QStringLiteral("shutdown");
break;
case KWorkSpace::ShutdownTypeReboot:
arguments << QStringLiteral("reboot");
break;
case KWorkSpace::ShutdownTypeNone:
default:
// logout
arguments << QStringLiteral("logout");
break;
}
}
arguments << QStringLiteral("--mode-fd");
arguments << QString::number(pipeFds[1]);
p->setArguments(arguments);
const int resultPipe = pipeFds[0];
connect(p, static_cast<void (QProcess::*)(QProcess::ProcessError)>(&QProcess::error), this,
[this, resultPipe, sdmode, sdtype] {
close(resultPipe);
dialogActive = false;
auto fallbackPrompt = new QMessageBox;
fallbackPrompt->setAttribute(Qt::WA_DeleteOnClose, true);
fallbackPrompt->setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel);
switch (sdtype) {
case KWorkSpace::ShutdownTypeHalt:
//i18nd is used as this patch was backported to an LTS with stable translations
fallbackPrompt->setText(i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Shut Down"));
break;
case KWorkSpace::ShutdownTypeReboot:
fallbackPrompt->setText(i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Reboot"));
break;
case KWorkSpace::ShutdownTypeNone:
Q_FALLTHROUGH();
default:
fallbackPrompt->setText(i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Logout"));
break;
}
connect(fallbackPrompt, &QMessageBox::buttonClicked, this, [=](QAbstractButton *button) {
if (button != fallbackPrompt->button(QMessageBox::Ok)) {
return;
}
shutdownType = sdtype;
shutdownMode = sdmode;
bootOption = QString();
performLogout();
});
fallbackPrompt->show();
}
);
connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), this,
[this, resultPipe, sdmode, p] (int exitCode) {
p->deleteLater();
dialogActive = false;
if (exitCode != 0) {
close(resultPipe);
return;
}
QFutureWatcher<bool> *watcher = new QFutureWatcher<bool>();
QObject::connect(watcher, &QFutureWatcher<bool>::finished, this,
[this, sdmode, watcher] {
const bool result = watcher->result();
if (!result) {
// it failed to read, don't logout
return;
}
shutdownMode = sdmode;
bootOption = QString();
performLogout();
}, Qt::QueuedConnection);
QObject::connect(watcher, &QFutureWatcher<void>::finished, watcher, &QFutureWatcher<bool>::deleteLater, Qt::QueuedConnection);
watcher->setFuture(QtConcurrent::run(readFromPipe, resultPipe));
}
);
dialogActive = true;
p->start();
close(pipeFds[1]);
if (!logoutConfirmed) {
OrgKdeLogoutPromptInterface logoutPrompt(QStringLiteral("org.kde.LogoutPrompt"),
QStringLiteral("/LogoutPrompt"),
QDBusConnection::sessionBus());
switch (sdtype) {
case KWorkSpace::ShutdownTypeHalt:
logoutPrompt.promptShutDown();
break;
case KWorkSpace::ShutdownTypeReboot:
logoutPrompt.promptReboot();
break;
case KWorkSpace::ShutdownTypeNone:
Q_FALLTHROUGH();
default:
logoutPrompt.promptLogout();
break;
}
} else {
shutdownType = sdtype;
shutdownMode = sdmode;
bootOption = bopt;
performLogout();
}
}
......@@ -349,7 +226,6 @@ void KSMServer::performLogout()
qCDebug(KSMSERVER) << "clients should be empty, " << clients.isEmpty();
if ( clients.isEmpty() )
completeShutdownOrCheckpoint();
dialogActive = false;
}
void KSMServer::pendingShutdownTimeout()
......@@ -359,7 +235,7 @@ void KSMServer::pendingShutdownTimeout()
void KSMServer::saveCurrentSession()
{
if ( state != Idle || dialogActive )
if ( state != Idle )
return;
if ( currentSession().isEmpty() || currentSession() == QString::fromLocal8Bit( SESSION_PREVIOUS_LOGOUT ) )
......@@ -392,7 +268,7 @@ void KSMServer::saveCurrentSession()
void KSMServer::saveCurrentSessionAs( const QString &session )
{
if ( state != Idle || dialogActive )
if ( state != Idle )
return;
sessionGroup = QStringLiteral( "Session: " ) + session;
saveCurrentSession();
......
......@@ -4,6 +4,10 @@ ecm_qt_declare_logging_category(LOGOUT_GREETER_SRCS HEADER debug.h
CATEGORY_NAME kde.logout_greeter
DEFAULT_SEVERITY Info)
qt5_add_dbus_adaptor( LOGOUT_GREETER_SRCS ../ksmserver/org.kde.LogoutPrompt.xml greeter.h Greeter)
qt5_add_dbus_interface( LOGOUT_GREETER_SRCS ../ksmserver/org.kde.KSMServerInterface.xml ksmserveriface)
add_executable(ksmserver-logout-greeter ${LOGOUT_GREETER_SRCS})
target_link_libraries(ksmserver-logout-greeter
PW::KWorkspace
......@@ -20,6 +24,7 @@ target_link_libraries(ksmserver-logout-greeter
${X11_LIBRARIES}
)
install(TARGETS ksmserver-logout-greeter DESTINATION ${KDE_INSTALL_LIBEXECDIR})
kdbusaddons_generate_dbus_service_file(ksmserver-logout-greeter org.kde.LogoutPrompt ${KDE_INSTALL_FULL_LIBEXECDIR})
if(BUILD_TESTING)
add_subdirectory(tests)
......
......@@ -31,6 +31,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "shutdowndlg.h"
#include "logoutpromptadaptor.h"
#include "ksmserveriface.h"
#include <KQuickAddons/QtQuickSettings>
#include <KWindowSystem>
......@@ -38,16 +41,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <KWayland/Client/registry.h>
#include <KWayland/Client/plasmashell.h>
#include <unistd.h>
Greeter::Greeter(int fd, bool shutdownAllowed, bool choose, KWorkSpace::ShutdownType type)
Greeter::Greeter(bool shutdownAllowed)
: QObject()
, m_fd(fd)
, m_shutdownAllowed(shutdownAllowed)
, m_choose(choose)
, m_shutdownType(type)
, m_waylandPlasmaShell(nullptr)
{
new LogoutPromptAdaptor(this);
QDBusConnection::sessionBus().registerObject(QStringLiteral("/LogoutPrompt"), this);
QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.LogoutPrompt"));
}
Greeter::~Greeter()
......@@ -83,12 +84,13 @@ void Greeter::init()
adoptScreen(screen);
}
connect(qApp, &QGuiApplication::screenAdded, this, &Greeter::adoptScreen);
m_running = true;
}
void Greeter::adoptScreen(QScreen* screen)
{
// TODO: last argument is the theme, maybe add command line option for it?
KSMShutdownDlg *w = new KSMShutdownDlg(nullptr, m_shutdownAllowed, m_choose, m_shutdownType, m_waylandPlasmaShell);
KSMShutdownDlg *w = new KSMShutdownDlg(nullptr, m_shutdownAllowed, m_shutdownType, m_waylandPlasmaShell);
w->installEventFilter(this);
m_dialogs << w;
......@@ -97,18 +99,11 @@ void Greeter::adoptScreen(QScreen* screen)
w->deleteLater();
});
connect(w, &KSMShutdownDlg::rejected, this, &Greeter::rejected);
connect(w, &KSMShutdownDlg::accepted, this,
[w, this] {
if (m_fd != -1) {
QFile f;
if (f.open(m_fd, QFile::WriteOnly, QFile::AutoCloseHandle)) {
f.write(QByteArray::number(int(w->shutdownType())));
f.close();
}
}
QApplication::quit();
}
);
connect(w, &KSMShutdownDlg::accepted, this, [w]() {
OrgKdeKSMServerInterfaceInterface ksmserver(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus());
ksmserver.logout(KWorkSpace::ShutdownConfirmNo, w->shutdownType(), KWorkSpace::ShutdownModeDefault);
QApplication::exit(1);
});
w->setScreen(screen);
w->setGeometry(screen->geometry());
w->init();
......@@ -116,9 +111,6 @@ void Greeter::adoptScreen(QScreen* screen)
void Greeter::rejected()
{
if (m_fd != -1) {
close(m_fd);
}
QApplication::exit(1);
}
......@@ -139,3 +131,32 @@ bool Greeter::eventFilter(QObject *watched, QEvent *event)
}
return false;
}
void Greeter::promptLogout()
{
if (m_running) {
return;
}
m_shutdownType = KWorkSpace::ShutdownTypeLogout;
init();
}
void Greeter::promptShutDown()
{
if (m_running) {
return;
}
m_shutdownType = KWorkSpace::ShutdownTypeHalt;
init();
}
void Greeter::promptReboot()
{
if (m_running) {
return;
}
m_shutdownType = KWorkSpace::ShutdownTypeReboot;
init();
}
......@@ -43,22 +43,27 @@ class Greeter : public QObject
{
Q_OBJECT
public:
Greeter(int fd, bool shutdownAllowed, bool choose, KWorkSpace::ShutdownType type);
Greeter(bool shutdownAllowed);
~Greeter() override;
void init();
bool eventFilter(QObject *watched, QEvent *event) override;
public Q_SLOTS:
void promptLogout();
void promptShutDown();
void promptReboot();
private:
void adoptScreen(QScreen *screen);
void rejected();
void setupWaylandIntegration();
int m_fd;
bool m_shutdownAllowed;
bool m_choose;
KWorkSpace::ShutdownType m_shutdownType;
bool m_running = false;
KWorkSpace::ShutdownType m_shutdownType = KWorkSpace::ShutdownTypeHalt;
QVector<KSMShutdownDlg *> m_dialogs;
KWayland::Client::PlasmaShell *m_waylandPlasmaShell;
};
......@@ -28,7 +28,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <KQuickAddons/QtQuickSettings>
#include <unistd.h>
#include "ksmserveriface.h"
#include "greeter.h"
......@@ -38,6 +38,7 @@ int main(int argc, char *argv[])
// Before Qt 5.12, the xdg-shell v6 integration does not support fullscreen.
qputenv("QT_WAYLAND_SHELL_INTEGRATION", "wl-shell");
}
qunsetenv("SESSION_MANAGER");
KWorkSpace::detectPlatform(argc, argv);
QQuickWindow::setDefaultAlphaBuffer(true);
......@@ -45,57 +46,27 @@ int main(int argc, char *argv[])
KQuickAddons::QtQuickSettings::init();
QCommandLineParser parser;
parser.addHelpOption();
// TODO: should these things be translated? It's internal after all...
QCommandLineOption shutdownAllowedOption(QStringLiteral("shutdown-allowed"),
QStringLiteral("Whether the user is allowed to shut down the system."));
parser.addOption(shutdownAllowedOption);
QCommandLineOption chooseOption(QStringLiteral("choose"),
QStringLiteral("Whether the user is offered the choices between logout, shutdown, etc."));
parser.addOption(chooseOption);
QCommandLineOption modeOption(QStringLiteral("mode"),
QStringLiteral("The initial exit mode to offer to the user."),
QStringLiteral("logout|shutdown|reboot"),
QStringLiteral("logout"));
parser.addOption(modeOption);
QCommandLineOption fdOption(QStringLiteral("mode-fd"),
QStringLiteral("An optional file descriptor the selected mode is written to on accepted"),
QStringLiteral("fd"), QString::number(-1));
parser.addOption(fdOption);
parser.process(app);
KWorkSpace::ShutdownType type = KWorkSpace::ShutdownTypeDefault;
if (parser.isSet(modeOption)) {
const QString modeValue = parser.value(modeOption);
if (QString::compare(QLatin1String("logout"), modeValue, Qt::CaseInsensitive) == 0) {
type = KWorkSpace::ShutdownTypeNone;
} else if (QString::compare(QLatin1String("shutdown"), modeValue, Qt::CaseInsensitive) == 0) {
type = KWorkSpace::ShutdownTypeHalt;
} else if (QString::compare(QLatin1String("reboot"), modeValue, Qt::CaseInsensitive) == 0) {
type = KWorkSpace::ShutdownTypeReboot;
} else {
return 1;
}
OrgKdeKSMServerInterfaceInterface ksmserver(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QDBusConnection::sessionBus());
QDBusPendingReply<bool> isShuttingDownPending = ksmserver.isShuttingDown();
QDBusPendingReply<bool> canShutdownPending = ksmserver.canShutdown();
isShuttingDownPending.waitForFinished();
canShutdownPending.waitForFinished();
//if ksmserver is shutting us down already, we don't want another prompt
if (isShuttingDownPending.value()) {
return 0;
}
int fd = -1;
if (parser.isSet(fdOption)) {
bool ok = false;
const int passedFd = parser.value(fdOption).toInt(&ok);
if (ok) {
fd = dup(passedFd);
}
bool shutdownAllowed = canShutdownPending.value();
Greeter greeter(shutdownAllowed);
if (argc > 1) {
//special case, invoked from ksmserver from a former release which had a tonne of args
//shouldn't happen often
greeter.promptLogout();
}
Greeter greeter(fd, parser.isSet(shutdownAllowedOption), parser.isSet(chooseOption), type);
greeter.init();
return app.exec();
}
#include "main.moc"
......@@ -68,8 +68,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Q_DECLARE_METATYPE(Solid::PowerManagement::SleepState)
KSMShutdownDlg::KSMShutdownDlg( QWindow* parent,
bool maysd, bool choose, KWorkSpace::ShutdownType sdtype,
KSMShutdownDlg::KSMShutdownDlg(QWindow* parent,
bool maysd, KWorkSpace::ShutdownType sdtype,
KWayland::Client::PlasmaShell *plasmaShell)
: QuickViewSharedEngine(parent),
m_result(false),
......@@ -100,7 +100,6 @@ KSMShutdownDlg::KSMShutdownDlg( QWindow* parent,
//windowContainer->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
QQmlContext *context = rootContext();
context->setContextProperty(QStringLiteral("maysd"), maysd);
context->setContextProperty(QStringLiteral("choose"), choose);
context->setContextProperty(QStringLiteral("sdtype"), sdtype);
QQmlPropertyMap *mapShutdownType = new QQmlPropertyMap(this);
......@@ -122,6 +121,10 @@ KSMShutdownDlg::KSMShutdownDlg( QWindow* parent,
// TODO KF6 remove, used to read "BootManager" from kdmrc
context->setContextProperty(QStringLiteral("bootManager"), QStringLiteral("None"));
//TODO KF6 remove. Unused
context->setContextProperty(QStringLiteral("choose"), false);
// TODO KF6 remove, used to call KDisplayManager::bootOptions
QStringList rebootOptions;
int def = 0;
......
......@@ -43,7 +43,7 @@ class KSMShutdownDlg : public KQuickAddons::QuickViewSharedEngine
Q_OBJECT
public:
KSMShutdownDlg( QWindow* parent, bool maysd, bool choose, KWorkSpace::ShutdownType sdtype, KWayland::Client::PlasmaShell *plasmaShell = nullptr );
KSMShutdownDlg( QWindow* parent, bool maysd, KWorkSpace::ShutdownType sdtype, KWayland::Client::PlasmaShell *plasmaShell = nullptr );
void init();
bool result() const;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment