Commit 3558eca7 authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇

Split out KWin startup handling from KSMServer

This makes us start KWin independently of KSMServer and has it talk to KWin over
DBus rather than session restore protocol.
parent 17542416
......@@ -254,29 +254,6 @@ void KSMServer::restoreLegacySession( KConfig* config )
if( config->hasGroup( QStringLiteral( "Legacy" ) + sessionGroup )) {
KConfigGroup group( config, QStringLiteral( "Legacy" ) + sessionGroup );
restoreLegacySessionInternal( &group );
} else if( wm == QLatin1String( "kwin" ) ) { // backwards comp. - get it from kwinrc
KConfigGroup group( config, sessionGroup );
int count = group.readEntry( "count", 0 );
for ( int i = 1; i <= count; i++ ) {
QString n = QString::number(i);
if ( !isWM( group.readEntry( QStringLiteral("program")+n, QString())))
continue;
QStringList restartCommand =
group.readEntry( QStringLiteral("restartCommand")+n, QStringList() );
for( QStringList::ConstIterator it = restartCommand.constBegin();
it != restartCommand.constEnd();
++it ) {
if( (*it) == QLatin1String( "-session" ) ) {
++it;
if( it != restartCommand.constEnd()) {
KConfig cfg( QStringLiteral( "session/" ) + wm +
QLatin1Char( '_' ) + (*it) );
KConfigGroup group(&cfg, "LegacySession");
restoreLegacySessionInternal( &group, ' ' );
}
}
}
}
}
}
......@@ -285,13 +262,11 @@ void KSMServer::restoreLegacySessionInternal( KConfigGroup* config, char sep )
int count = config->readEntry( "count",0 );
for ( int i = 1; i <= count; i++ ) {
QString n = QString::number(i);
QStringList wmCommand = (sep == ',') ?
QStringList wmCommand = (sep == ',') ? // why is this named "wmCommand"?
config->readEntry( QStringLiteral("command")+n, QStringList() ) :
KShell::splitArgs( config->readEntry( QStringLiteral("command")+n, QString() ) ); // close enough(?)
if( wmCommand.isEmpty())
continue;
if( isWM( wmCommand.first()))
continue;
startApplication( wmCommand,
config->readEntry( QStringLiteral("clientMachine")+n, QString() ),
config->readEntry( QStringLiteral("userId")+n, QString() ));
......
......@@ -191,9 +191,7 @@ void KSMServer::performLogout()
QTimer::singleShot(1000, this, &KSMServer::performLogout);
}
auto reply = m_kwinInterface->setState(KWinSessionState::Saving);
// we don't need to block as we wait for kwin to handle it's session 1
// before messaging the clients
auto setStateReply = m_kwinInterface->setState(KWinSessionState::Saving);
state = Shutdown;
......@@ -219,43 +217,35 @@ void KSMServer::performLogout()
performLegacySessionSave();
#endif
startProtection();
foreach( KSMClient* c, clients ) {
c->resetState();
// Whoever came with the idea of phase 2 got it backwards
// unfortunately. Window manager should be the very first
// one saving session data, not the last one, as possible
// user interaction during session save may alter
// window positions etc.
// Moreover, KWin's focus stealing prevention would lead
// to undesired effects while session saving (dialogs
// wouldn't be activated), so it needs be assured that
// KWin will turn it off temporarily before any other
// user interaction takes place.
// Therefore, make sure the WM finishes its phase 1
// before others a chance to change anything.
// KWin will check if the session manager is ksmserver,
// and if yes it will save in phase 1 instead of phase 2.
if( isWM( c ) )
++wmPhase1WaitingCount;
// Tell KWin to start saving before we start tearing down clients
// as any "Save changes?" prompt might meddle with the state
if (saveSession) {
setStateReply.waitForFinished(); // do we have to wait for this to finish?
qCDebug(KSMSERVER) << "Telling KWin we're about to save session" << currentSession();
auto saveSessionCall = m_kwinInterface->aboutToSaveSession(currentSession());
saveSessionCall.waitForFinished(); // hehe
}
if (wmPhase1WaitingCount > 0) {
foreach( KSMClient* c, clients ) {
if( isWM( c ) )
SmsSaveYourself( c->connection(), saveType,
true, SmInteractStyleAny, false );
}
} else { // no WM, simply start them all
foreach( KSMClient* c, clients )
SmsSaveYourself( c->connection(), saveType,
true, SmInteractStyleAny, false );
const auto pendingClients = clients;
for (KSMClient *c : pendingClients) {
c->resetState();
SmsSaveYourself(c->connection(), saveType, true, SmInteractStyleAny, false);
}
qCDebug(KSMSERVER) << "clients should be empty, " << clients.isEmpty();
qCDebug(KSMSERVER) << "clients should be empty, " << clients.count();
if ( clients.isEmpty() )
completeShutdownOrCheckpoint();
}
void KSMServer::saveCurrentSession()
{
abort(); // FIXME IMPLEMENT NEW SAVING ALSO HERE!
if ( state != Idle )
return;
......@@ -270,19 +260,10 @@ void KSMServer::saveCurrentSession()
#ifndef NO_LEGACY_SESSION_MANAGEMENT
performLegacySessionSave();
#endif
foreach( KSMClient* c, clients ) {
c->resetState();
if( isWM( c ) )
++wmPhase1WaitingCount;
}
if (wmPhase1WaitingCount > 0) {
foreach( KSMClient* c, clients ) {
if( isWM( c ) )
SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false );
}
} else {
foreach( KSMClient* c, clients )
SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false );
const auto pendingClients = clients;
for (KSMClient *c : pendingClients) {
SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false );
}
if ( clients.isEmpty() )
completeShutdownOrCheckpoint();
......@@ -320,17 +301,6 @@ void KSMServer::saveYourselfDone( KSMClient* client, bool success )
completeShutdownOrCheckpoint();
}
startProtection();
if( isWM( client ) && !client->wasPhase2 && wmPhase1WaitingCount > 0 ) {
--wmPhase1WaitingCount;
// WM finished its phase1, save the rest
if( wmPhase1WaitingCount == 0 ) {
foreach( KSMClient* c, clients )
if( !isWM( c ))
SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal,
saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone,
false );
}
}
}
void KSMServer::interactRequest( KSMClient* client, int /*dialogType*/ )
......@@ -360,17 +330,6 @@ void KSMServer::phase2Request( KSMClient* client )
client->waitForPhase2 = true;
client->wasPhase2 = true;
completeShutdownOrCheckpoint();
if( isWM( client ) && wmPhase1WaitingCount > 0 ) {
--wmPhase1WaitingCount;
// WM finished its phase1 and requests phase2, save the rest
if( wmPhase1WaitingCount == 0 ) {
foreach( KSMClient* c, clients )
if( !isWM( c ))
SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal,
saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone,
false );
}
}
}
void KSMServer::handlePendingInteractions()
......@@ -539,8 +498,6 @@ void KSMServer::startKilling()
m_kwinInterface->setState(KWinSessionState::Quitting);
foreach( KSMClient* c, clients ) {
if( isWM( c )) // kill the WM as the last one in order to reduce flicker
continue;
qCDebug(KSMSERVER) << "startKilling: client " << c->program() << "(" << c->clientId() << ")";
SmsDie( c->connection() );
}
......@@ -558,46 +515,11 @@ void KSMServer::completeKilling()
if( state == Killing ) {
bool wait = false;
foreach( KSMClient* c, clients ) {
if( isWM( c ))
continue;
wait = true; // still waiting for clients to go away
}
if( wait )
return;
killWM();
}
}
void KSMServer::killWM()
{
if( state != Killing )
return;
qCDebug(KSMSERVER) << "Starting killing WM";
state = KillingWM;
bool iswm = false;
foreach( KSMClient* c, clients ) {
if( isWM( c )) {
iswm = true;
qCDebug(KSMSERVER) << "killWM: client " << c->program() << "(" << c->clientId() << ")";
SmsDie( c->connection() );
}
}
if( iswm ) {
completeKillingWM();
QTimer::singleShot( 5000, this, &KSMServer::timeoutWMQuit );
}
else
killingCompleted();
}
void KSMServer::completeKillingWM()
{
qCDebug(KSMSERVER) << "KSMServer::completeKillingWM clients.count()=" <<
clients.count() << endl;
if( state == KillingWM ) {
if( clients.isEmpty())
killingCompleted();
}
}
......@@ -617,14 +539,6 @@ void KSMServer::timeoutQuit()
foreach( KSMClient* c, clients ) {
qCWarning(KSMSERVER) << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")" ;
}
killWM();
}
void KSMServer::timeoutWMQuit()
{
if( state == KillingWM ) {
qCWarning(KSMSERVER) << "SmsDie WM timeout" ;
}
killingCompleted();
}
......@@ -683,14 +597,9 @@ void KSMServer::completeKillingSubSession()
qCDebug(KSMSERVER) << "KSMServer::completeKillingSubSession clients.count()=" <<
clients.count() << endl;
if( state == KillingSubSession ) {
bool wait = false;
foreach( KSMClient* c, clientsToKill ) {
if( isWM( c ))
continue;
wait = true; // still waiting for clients to go away
if (!clientsToKill.isEmpty()) {
return; // still waiting for clients to go away
}
if( wait )
return;
signalSubSessionClosed();
}
}
......
......@@ -266,11 +266,6 @@ extern "C" Q_DECL_EXPORT int kdemain( int argc, char* argv[] )
i18n("Restores the saved user session if available"));
parser.addOption(restoreOption);
QCommandLineOption wmOption(QStringList() << QStringLiteral("w") << QStringLiteral("windowmanager"),
i18n("Starts <wm> in case no other window manager is \nparticipating in the session. Default is 'kwin'"),
i18n("wm"));
parser.addOption(wmOption);
QCommandLineOption nolocalOption(QStringLiteral("nolocal"),
i18n("Also allow remote connections"));
parser.addOption(nolocalOption);
......@@ -285,8 +280,6 @@ extern "C" Q_DECL_EXPORT int kdemain( int argc, char* argv[] )
parser.process(*a);
QString wm = parser.value(wmOption);
bool only_local = !parser.isSet(nolocalOption);
#ifndef HAVE__ICETRANSNOLISTEN
/* this seems strange, but the default is only_local, so if !only_local
......@@ -310,7 +303,7 @@ extern "C" Q_DECL_EXPORT int kdemain( int argc, char* argv[] )
flags |= KSMServer::InitFlag::NoLockScreen;
}
KSMServer *server = new KSMServer( wm, flags);
KSMServer *server = new KSMServer(flags);
// for the KDE-already-running check in startkde
KSelectionOwner kde_running( "_KDE_RUNNING", 0 );
......
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<?xml version="1.0" encoding="UTF-8"?>
<!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.KWin.Session">
<interface name="org.kde.KWin.Session">
<method name="setState">
<!-- Sets state of the session should be one of:
<!-- Sets state of the session should be one of:
0 = normal
1 = saving
2= quitting
-->
<arg name="state" type="u" direction="in"/>
</method>
</interface>
<arg name="state" type="u" direction="in" />
</method>
<method name="loadSession">
<arg name="name" type="s" direction="in" />
</method>
<method name="aboutToSaveSession">
<arg name="name" type="s" direction="in" />
</method>
<method name="finishSaveSession">
<arg name="name" type="s" direction="in" />
</method>
</interface>
</node>
......@@ -279,8 +279,6 @@ void KSMSetPropertiesProc (
SmFreeProperty( p );
}
client->properties.append( props[i] );
if ( !qstrcmp( props[i]->name, SmProgram ) )
the_server->clientSetProgram( client );
}
if ( numProps )
......@@ -597,9 +595,8 @@ static Status KSMNewClientProc ( SmsConn conn, SmPointer manager_data,
extern "C" int _IceTransNoListen(const char * protocol);
#endif
KSMServer::KSMServer( const QString& windowManager, InitFlags flags )
: wmProcess( nullptr )
, sessionGroup( QStringLiteral( "" ) )
KSMServer::KSMServer(InitFlags flags)
: sessionGroup( QStringLiteral( "" ) )
, m_kwinInterface(new OrgKdeKWinSessionInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/Session"), QDBusConnection::sessionBus(), this))
, sockets{ -1, -1 }
{
......@@ -628,13 +625,6 @@ KSMServer::KSMServer( const QString& windowManager, InitFlags flags )
clientInteracting = nullptr;
xonCommand = config.readEntry( "xonCommand", "xon" );
if (windowManager.isEmpty()) {
wm = QStringLiteral(KWIN_BIN);
} else {
wm = windowManager;
}
wmCommands = QStringList({wm});
only_local = flags.testFlag(InitFlag::OnlyLocal);
#ifdef HAVE__ICETRANSNOLISTEN
if (only_local)
......@@ -832,8 +822,6 @@ void KSMServer::deleteClient( KSMClient* client )
completeKilling();
else if ( state == KillingSubSession )
completeKillingSubSession();
if ( state == KillingWM )
completeKillingWM();
}
void KSMServer::newConnection( int /*socket*/ )
......@@ -861,12 +849,11 @@ void KSMServer::newConnection( int /*socket*/ )
fcntl( IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC );
}
QString KSMServer::currentSession()
{
if ( sessionGroup.startsWith( QLatin1String( "Session: " ) ) )
return sessionGroup.mid( 9 );
return QStringLiteral( "" ); // empty, not null, since used for KConfig::setGroup
return QStringLiteral( "" ); // empty, not null, since used for KConfig::setGroup // TODO does this comment make any sense?
}
void KSMServer::discardSession()
......@@ -917,15 +904,9 @@ void KSMServer::storeSession()
KConfigGroup cg( config, sessionGroup);
count = 0;
if (state != ClosingSubSession) {
// put the wm first
foreach ( KSMClient *c, clients )
if ( c->program() == wm ) {
clients.removeAll( c );
clients.prepend( c );
break;
}
}
// Tell kwin to save its state
auto reply = m_kwinInterface->finishSaveSession(currentSession());
reply.waitForFinished(); // boo!
foreach ( KSMClient *c, clients ) {
int restartHint = c->restartStyleHint();
......@@ -956,7 +937,6 @@ void KSMServer::storeSession()
cg.writePathEntry( QStringLiteral("discardCommand")+n, c->discardCommand() );
cg.writeEntry( QStringLiteral("restartStyleHint")+n, restartHint );
cg.writeEntry( QStringLiteral("userId")+n, c->userId() );
cg.writeEntry( QStringLiteral("wasWm")+n, isWM( c ));
}
cg.writeEntry( "count", count );
......@@ -977,19 +957,6 @@ QStringList KSMServer::sessionList()
return sessions;
}
bool KSMServer::isWM( const KSMClient* client ) const
{
return isWM( client->program());
}
bool KSMServer::isWM( const QString& program ) const
{
// Strip possible paths, so that even /usr/bin/kwin is recognized as kwin.
QString wmName = wm.mid( wm.lastIndexOf( QDir::separator()) + 1 );
QString programName = program.mid( program.lastIndexOf( QDir::separator()) + 1 );
return programName == wmName;
}
bool KSMServer::defaultSession() const
{
return sessionGroup.isEmpty();
......@@ -1043,56 +1010,8 @@ void KSMServer::restoreSession( const QString &sessionName )
int count = configSessionGroup.readEntry( "count", 0 );
appsToStart = count;
// find all commands to launch the wm in the session
QList<QStringList> wmStartCommands;
if ( !wm.isEmpty() ) {
for ( int i = 1; i <= count; i++ ) {
QString n = QString::number(i);
if ( isWM( configSessionGroup.readEntry( QStringLiteral("program")+n, QString())) ) {
wmStartCommands << configSessionGroup.readEntry( QStringLiteral("restartCommand")+n, QStringList() );
}
}
}
if( wmStartCommands.isEmpty()) // otherwise use the configured default
wmStartCommands << wmCommands;
launchWM( wmStartCommands );
}
void KSMServer::launchWM( const QList< QStringList >& wmStartCommands )
{
assert( state == LaunchingWM );
if (!(qEnvironmentVariableIsSet("WAYLAND_DISPLAY") || qEnvironmentVariableIsSet("WAYLAND_SOCKET"))) {
// when we have a window manager, we start it first and give
// it some time before launching other processes. Results in a
// visually more appealing startup.
wmProcess = startApplication( wmStartCommands[ 0 ], QString(), QString(), true );
connect( wmProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(wmProcessChange()));
connect( wmProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(wmProcessChange()));
}
emit windowManagerLoaded();
}
void KSMServer::wmProcessChange()
{
if( state != LaunchingWM )
{ // don't care about the process when not in the wm-launching state anymore
wmProcess = nullptr;
return;
}
if( wmProcess->state() == QProcess::NotRunning )
{ // wm failed to launch for some reason, go with kwin instead
qCWarning(KSMSERVER) << "Window manager" << wm << "failed to launch";
if( wm == QLatin1String( KWIN_BIN ) )
return; // uhoh, kwin itself failed
qCDebug(KSMSERVER) << "Launching KWin";
wm = QStringLiteral( KWIN_BIN );
wmCommands = ( QStringList() << QStringLiteral( KWIN_BIN ) );
// launch it
launchWM( QList< QStringList >() << wmCommands );
return;
}
auto reply = m_kwinInterface->loadSession(sessionName);
reply.waitForFinished(); // boo!
}
/*!
......@@ -1109,7 +1028,6 @@ void KSMServer::startDefaultSession()
t.start();
#endif
sessionGroup = QString();
launchWM( QList< QStringList >() << wmCommands );
}
void KSMServer::restoreSession()
......@@ -1149,13 +1067,6 @@ void KSMServer::restoreSubSession( const QString& name )
tryRestoreNext();
}
void KSMServer::clientSetProgram( KSMClient* client )
{
if( client->program() == wm ) {
emit windowManagerLoaded();
}
}
void KSMServer::clientRegistered( const char* previousId )
{
if ( previousId && lastIdStarted == QString::fromLocal8Bit( previousId ) )
......@@ -1188,10 +1099,6 @@ void KSMServer::tryRestoreNext()
(config.readEntry( QStringLiteral("restartStyleHint")+n, 0 ) == SmRestartNever)) {
continue;
}
if ( isWM( config.readEntry( QStringLiteral("program")+n, QString())) )
continue; // wm already started
if( config.readEntry( QStringLiteral( "wasWm" )+n, false ))
continue; // it was wm before, but not now, don't run it (some have --replace in command :( )
startApplication( restartCommand,
config.readEntry( QStringLiteral("clientMachine")+n, QString() ),
config.readEntry( QStringLiteral("userId")+n, QString() ));
......
......@@ -86,7 +86,7 @@ public:
};
Q_DECLARE_FLAGS(InitFlags, InitFlag)
KSMServer( const QString& windowManager, InitFlags flags );
KSMServer(InitFlags flags );
~KSMServer() override;
static KSMServer* self();
......@@ -107,7 +107,6 @@ public:
void ioError( IceConn iceConn );
// notification
void clientSetProgram( KSMClient* client );
void clientRegistered( const char* previousId );
// public API
......@@ -122,7 +121,6 @@ public:
void setupShortcuts();
Q_SIGNALS:
void windowManagerLoaded();
void logoutCancelled();
public Q_SLOTS:
......@@ -135,9 +133,6 @@ private Q_SLOTS:
void protectionTimeout();
void timeoutQuit();
void timeoutWMQuit();
void wmProcessChange();
void defaultLogout();
void logoutWithoutConfirmation();
......@@ -152,9 +147,7 @@ private:
void performStandardKilling();
void completeKilling();
void completeKillingSubSession();
void killWM();
void signalSubSessionClosed();
void completeKillingWM();
void cancelShutdown( KSMClient* c );
void killingCompleted();
......@@ -164,16 +157,12 @@ private:
void startProtection();
void endProtection();
void launchWM( const QList< QStringList >& wmStartCommands );
KProcess* startApplication( const QStringList& command,
const QString& clientMachine = QString(),
const QString& userId = QString(),
bool wm = false );
void executeCommand( const QStringList& command );
bool isWM( const KSMClient* client ) const;
bool isWM( const QString& program ) const;
bool defaultSession() const; // empty session
void setupXIOErrorHandler();
......@@ -222,8 +211,8 @@ private:
enum State
{
Idle,
LaunchingWM, Restoring,
Shutdown, Checkpoint, Killing, KillingWM, WaitingForKNotify, // shutdown
LaunchingWM /* FIXME rename this*/, Restoring,
Shutdown, Checkpoint, Killing, KillingWMDONTUSETHIS, WaitingForKNotify, // shutdown
ClosingSubSession, KillingSubSession, RestoringSubSession
};
State state;
......@@ -233,11 +222,7 @@ private:
bool clean;
KSMClient* clientInteracting;
QString wm;
QStringList wmCommands;
KProcess* wmProcess;
QString sessionGroup;
QString sessionName;
QTimer protectionTimer;
QTimer restoreTimer;
QString xonCommand;
......
......@@ -7,4 +7,6 @@
#define CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "@CMAKE_INSTALL_FULL_LIBEXECDIR_KF5@"
#define KWIN_WAYLAND_BIN_PATH "@KWIN_WAYLAND_BIN_PATH@"
#define KWIN_BIN "${KWIN_BIN}"
#endif
......@@ -17,6 +17,8 @@
Boston, MA 02110-1301, USA.
*/
#include <config-startplasma.h>
#include <QDir>
#include <QProcess>
#include <QStandardPaths>
......@@ -321,6 +323,22 @@ QProcess* setupKSplash()
return p;
}
// FIXME Move this into plasma-session/startup!
void startWindowManager()
{
QProcess windowManager;
windowManager.setProcessChannelMode(QProcess::ForwardedChannels);
if (qEnvironmentVariableIsSet("KDEWM")) {
windowManager.setProgram(qEnvironmentVariable("KDEWM"));
}
if (windowManager.program().isEmpty()) {
windowManager.setProgram(QStringLiteral(KWIN_BIN));
}
windowManager.startDetached();
}
void setupGSLib()
// Get Ghostscript to look into user's KDE fonts dir for additional Fontmap
......@@ -355,9 +373,9 @@ bool startPlasmaSession(bool wayland)
if (wayland) {
plasmaSessionOptions << QStringLiteral("--no-lockscreen");
} else {
if (qEnvironmentVariableIsSet("KDEWM")) {
plasmaSessionOptions << QStringLiteral("--windowmanager") << qEnvironmentVariable("KDEWM");
}
// FIXME Move this into plasma-session/startup!
startWindowManager();
if (desktopLockedAtStart) {
plasmaSessionOptions << QStringLiteral("--lockscreen");
}
......
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