diff --git a/CMakeLists.txt b/CMakeLists.txt
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -548,6 +548,7 @@
qt5_add_dbus_adaptor(kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl)
qt5_add_dbus_adaptor(kwin_KDEINIT_SRCS org.kde.kwin.OrientationSensor.xml orientation_sensor.h KWin::OrientationSensor)
qt5_add_dbus_adaptor(kwin_KDEINIT_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface)
+qt5_add_dbus_adaptor(kwin_KDEINIT_SRCS org.kde.KWin.Session.xml sm.h KWin::SessionManager)
qt5_add_dbus_interface(kwin_KDEINIT_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/kf5_org.freedesktop.ScreenSaver.xml screenlocker_interface)
qt5_add_dbus_interface(kwin_KDEINIT_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/org.kde.screensaver.xml kscreenlocker_interface)
diff --git a/activation.cpp b/activation.cpp
--- a/activation.cpp
+++ b/activation.cpp
@@ -558,7 +558,7 @@
if (time == -1U)
time = c->userTime();
int level = c->rules()->checkFSP(options->focusStealingPreventionLevel());
- if (session_saving && level <= FSP::Medium) { // <= normal
+ if (sessionSaving() && level <= FSP::Medium) { // <= normal
return true;
}
AbstractClient* ac = mostRecentlyActivatedClient();
@@ -635,7 +635,7 @@
bool Workspace::allowFullClientRaising(const KWin::AbstractClient *c, xcb_timestamp_t time)
{
int level = c->rules()->checkFSP(options->focusStealingPreventionLevel());
- if (session_saving && level <= 2) { // <= normal
+ if (sessionSaving() && level <= 2) { // <= normal
return true;
}
AbstractClient* ac = mostRecentlyActivatedClient();
diff --git a/libkwineffects/kwinglobals.h b/libkwineffects/kwinglobals.h
--- a/libkwineffects/kwinglobals.h
+++ b/libkwineffects/kwinglobals.h
@@ -138,6 +138,16 @@
Right
};
+/**
+ * Represents the state of the session running outside kwin
+ * Under Plasma this is managed by ksmserver
+ */
+enum class SessionState {
+ Normal,
+ Saving,
+ Quitting
+};
+
inline
KWIN_EXPORT xcb_connection_t *connection()
{
diff --git a/main_x11.cpp b/main_x11.cpp
--- a/main_x11.cpp
+++ b/main_x11.cpp
@@ -480,9 +480,6 @@
a.start();
- KWin::SessionSaveDoneHelper helper;
- Q_UNUSED(helper); // The sessionsavedonehelper opens a side channel to the smserver,
- // listens for events and talks to it, so it needs to be created.
return a.exec();
}
diff --git a/org.kde.KWin.Session.xml b/org.kde.KWin.Session.xml
new file mode 100644
--- /dev/null
+++ b/org.kde.KWin.Session.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
diff --git a/sm.h b/sm.h
--- a/sm.h
+++ b/sm.h
@@ -28,9 +28,6 @@
#include
#include
-#include
-#include
-
class QSocketNotifier;
namespace KWin
@@ -77,23 +74,14 @@
SMSavePhase2Full // complete saving in phase2, there was no phase 0
};
-class KWIN_EXPORT SessionSaveDoneHelper
- : public QObject
+class SessionManager: public QObject
{
Q_OBJECT
public:
- SessionSaveDoneHelper();
- ~SessionSaveDoneHelper() override;
- SmcConn connection() const {
- return conn;
- }
- void saveDone();
- void close();
-private Q_SLOTS:
- void processData();
-private:
- QSocketNotifier* notifier;
- SmcConn conn;
+ SessionManager(QObject *parent);
+ ~SessionManager();
+public Q_SLOTS:
+ void setState(const QString &state);
};
} // namespace
diff --git a/sm.cpp b/sm.cpp
--- a/sm.cpp
+++ b/sm.cpp
@@ -29,10 +29,11 @@
#include "workspace.h"
#include "x11client.h"
#include
-#include
-#include
#include
+#include
+#include "sessionadaptor.h"
+
namespace KWin
{
@@ -93,7 +94,6 @@
if (!sm.isPhase2()) {
KConfigGroup cg(config, "Session");
cg.writeEntry("AllowsInteraction", sm.allowsInteraction());
- sessionSaveStarted();
if (gs_sessionManagerIsKSMServer) // save stacking order etc. before "save file?" etc. dialogs change it
storeSession(config, SMSavePhase0);
config->markAsClean(); // don't write Phase #1 data to disk
@@ -111,13 +111,6 @@
}
}
-// I bet this is broken, just like everywhere else in KDE
-void Workspace::commitData(QSessionManager &sm)
-{
- if (!sm.isPhase2())
- sessionSaveStarted();
-}
-
// Workspace
/**
@@ -361,158 +354,24 @@
return realInfo;
}
-// KWin's focus stealing prevention causes problems with user interaction
-// during session save, as it prevents possible dialogs from getting focus.
-// Therefore it's temporarily disabled during session saving. Start of
-// session saving can be detected in SessionManager::saveState() above,
-// but Qt doesn't have API for saying when session saved finished (either
-// successfully, or was canceled). Therefore, create another connection
-// to session manager, that will provide this information.
-// Similarly the remember feature of window-specific settings should be disabled
-// during KDE shutdown when windows may move e.g. because of Kicker going away
-// (struts changing). When session saving starts, it can be cancelled, in which
-// case the shutdown_cancelled callback is invoked, or it's a checkpoint that
-// is immediatelly followed by save_complete, or finally it's a shutdown that
-// is immediatelly followed by die callback. So getting save_yourself with shutdown
-// set disables window-specific settings remembering, getting shutdown_cancelled
-// re-enables, otherwise KWin will go away after die.
-static void save_yourself(SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool)
-{
- SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
- if (conn_P != session->connection())
- return;
- if (shutdown)
- RuleBook::self()->setUpdatesDisabled(true);
- SmcSaveYourselfDone(conn_P, True);
-}
-
-static void die(SmcConn conn_P, SmPointer ptr)
+SessionManager::SessionManager(QObject *parent)
+ :QObject(parent)
{
- SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
- if (conn_P != session->connection())
- return;
- // session->saveDone(); we will quit anyway
- session->close();
-}
-
-static void save_complete(SmcConn conn_P, SmPointer ptr)
-{
- SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
- if (conn_P != session->connection())
- return;
- session->saveDone();
+ new SessionAdaptor(this);
+ QDBusConnection::sessionBus().registerObject("/Session", this);
}
-static void shutdown_cancelled(SmcConn conn_P, SmPointer ptr)
+SessionManager::~SessionManager()
{
- SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
- if (conn_P != session->connection())
- return;
- RuleBook::self()->setUpdatesDisabled(false); // re-enable
- // no need to differentiate between successful finish and cancel
- session->saveDone();
}
-void SessionSaveDoneHelper::saveDone()
+void SessionManager::setState(const QString &state)
{
- if (Workspace::self())
- Workspace::self()->sessionSaveDone();
-}
-
-SessionSaveDoneHelper::SessionSaveDoneHelper()
-{
- SmcCallbacks calls;
- calls.save_yourself.callback = save_yourself;
- calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this);
- calls.die.callback = die;
- calls.die.client_data = reinterpret_cast< SmPointer >(this);
- calls.save_complete.callback = save_complete;
- calls.save_complete.client_data = reinterpret_cast< SmPointer >(this);
- calls.shutdown_cancelled.callback = shutdown_cancelled;
- calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this);
- char* id = nullptr;
- char err[ 11 ];
- conn = SmcOpenConnection(nullptr, nullptr, 1, 0,
- SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
- | SmcShutdownCancelledProcMask, &calls, nullptr, &id, 10, err);
- if (id != nullptr)
- free(id);
- if (conn == nullptr)
- return; // no SM
-
- // detect ksmserver
- char* vendor = SmcVendor(conn);
- gs_sessionManagerIsKSMServer = qstrcmp(vendor, "KDE") == 0;
- free(vendor);
-
- // set the required properties, mostly dummy values
- SmPropValue propvalue[ 5 ];
- SmProp props[ 5 ];
- propvalue[ 0 ].length = sizeof(unsigned char);
- unsigned char value0 = SmRestartNever; // so that this extra SM connection doesn't interfere
- propvalue[ 0 ].value = &value0;
- props[ 0 ].name = const_cast< char* >(SmRestartStyleHint);
- props[ 0 ].type = const_cast< char* >(SmCARD8);
- props[ 0 ].num_vals = 1;
- props[ 0 ].vals = &propvalue[ 0 ];
- struct passwd* entry = getpwuid(geteuid());
- propvalue[ 1 ].length = entry != nullptr ? strlen(entry->pw_name) : 0;
- propvalue[ 1 ].value = (SmPointer)(entry != nullptr ? entry->pw_name : "");
- props[ 1 ].name = const_cast< char* >(SmUserID);
- props[ 1 ].type = const_cast< char* >(SmARRAY8);
- props[ 1 ].num_vals = 1;
- props[ 1 ].vals = &propvalue[ 1 ];
- propvalue[ 2 ].length = 0;
- propvalue[ 2 ].value = (SmPointer)("");
- props[ 2 ].name = const_cast< char* >(SmRestartCommand);
- props[ 2 ].type = const_cast< char* >(SmLISTofARRAY8);
- props[ 2 ].num_vals = 1;
- props[ 2 ].vals = &propvalue[ 2 ];
- propvalue[ 3 ].length = strlen("kwinsmhelper");
- propvalue[ 3 ].value = (SmPointer)"kwinsmhelper";
- props[ 3 ].name = const_cast< char* >(SmProgram);
- props[ 3 ].type = const_cast< char* >(SmARRAY8);
- props[ 3 ].num_vals = 1;
- props[ 3 ].vals = &propvalue[ 3 ];
- propvalue[ 4 ].length = 0;
- propvalue[ 4 ].value = (SmPointer)("");
- props[ 4 ].name = const_cast< char* >(SmCloneCommand);
- props[ 4 ].type = const_cast< char* >(SmLISTofARRAY8);
- props[ 4 ].num_vals = 1;
- props[ 4 ].vals = &propvalue[ 4 ];
- SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] };
- SmcSetProperties(conn, 5, p);
- notifier = new QSocketNotifier(IceConnectionNumber(SmcGetIceConnection(conn)),
- QSocketNotifier::Read, this);
- connect(notifier, SIGNAL(activated(int)), SLOT(processData()));
-}
-
-SessionSaveDoneHelper::~SessionSaveDoneHelper()
-{
- close();
-}
-
-void SessionSaveDoneHelper::close()
-{
- if (conn != nullptr) {
- delete notifier;
- SmcCloseConnection(conn, 0, nullptr);
- }
- conn = nullptr;
-}
-
-void SessionSaveDoneHelper::processData()
-{
- if (conn != nullptr)
- IceProcessMessages(SmcGetIceConnection(conn), nullptr, nullptr);
-}
-
-void Workspace::sessionSaveDone()
-{
- session_saving = false;
- foreach (X11Client *c, clients) {
- c->setSessionActivityOverride(false);
- }
+ static QHash s_stateMap = {
+ {"saving", SessionState::Saving},
+ {"quitting", SessionState::Quitting}
+ };
+ Workspace::self()->setSessionState(s_stateMap.value(state, SessionState::Normal));
}
} // namespace
diff --git a/workspace.h b/workspace.h
--- a/workspace.h
+++ b/workspace.h
@@ -339,8 +339,6 @@
bool globalShortcutsDisabled() const;
void disableGlobalShortcutsForClient(bool disable);
- void sessionSaveStarted();
- void sessionSaveDone();
void setWasUserInteraction();
bool wasUserInteraction() const;
bool sessionSaving() const;
@@ -479,6 +477,13 @@
void updateClientArea();
+ /**
+ * Updates kwin's internal knowledge of the state of the
+ * session outside.
+ */
+ void setSessionState(SessionState state);
+ SessionState sessionState() const;
+
private Q_SLOTS:
void desktopResized();
void selectWmInputEventMask();
@@ -492,7 +497,6 @@
// session management
void saveState(QSessionManager &sm);
- void commitData(QSessionManager &sm);
Q_SIGNALS:
/**
@@ -531,6 +535,8 @@
*/
void internalClientRemoved(KWin::InternalClient *client);
+ void sessionStateChanged();
+
private:
void init();
void initWithX11();
@@ -616,7 +622,8 @@
bool was_user_interaction;
QScopedPointer m_wasUserInteractionFilter;
- bool session_saving;
+
+ SessionState m_sessionState = SessionState::Normal;
int session_active_client;
int session_desktop;
@@ -742,14 +749,9 @@
return was_user_interaction;
}
-inline void Workspace::sessionSaveStarted()
-{
- session_saving = true;
-}
-
inline bool Workspace::sessionSaving() const
{
- return session_saving;
+ return m_sessionState == SessionState::Saving;
}
inline bool Workspace::showingDesktop() const
diff --git a/workspace.cpp b/workspace.cpp
--- a/workspace.cpp
+++ b/workspace.cpp
@@ -118,7 +118,6 @@
, force_restacking(false)
, showing_desktop(false)
, was_user_interaction(false)
- , session_saving(false)
, block_focus(0)
, m_userActionsMenu(new UserActionsMenu(this))
, client_keys_dialog(nullptr)
@@ -156,7 +155,6 @@
if (!sessionKey.isEmpty())
loadSessionInfo(sessionKey);
- connect(qApp, &QGuiApplication::commitDataRequest, this, &Workspace::commitData);
connect(qApp, &QGuiApplication::saveStateRequest, this, &Workspace::saveState);
RuleBook::create(this)->load();
@@ -189,6 +187,7 @@
connect(this, &Workspace::configChanged, decorationBridge, &Decoration::DecorationBridge::reconfigure);
new DBusInterface(this);
+ new SessionManager(this);
Outline::create(this);
@@ -1827,4 +1826,29 @@
emit internalClientRemoved(client);
}
+void Workspace::setSessionState(SessionState state)
+{
+ if (state == m_sessionState) {
+ return;
+ }
+ //starting session save
+ if (state == SessionState::Saving) {
+ RuleBook::self()->setUpdatesDisabled(true);
+ }
+ //Session save is done
+ if (m_sessionState == SessionState::Saving) {
+ RuleBook::self()->setUpdatesDisabled(false);
+ foreach (X11Client *c, clients) {
+ c->setSessionActivityOverride(false);
+ }
+ }
+ m_sessionState = state;
+ emit sessionStateChanged();
+}
+
+SessionState Workspace::sessionState() const
+{
+ return m_sessionState;
+}
+
} // namespace