From 409442c1ea8f167485642fbdb3494ff58900966f Mon Sep 17 00:00:00 2001
From: Michele Calgaro <michele.calgaro@yahoo.it>
Date: Sat, 21 Jun 2025 21:49:09 +0900
Subject: kdesktop: fix deadlock condition between kdesktop and kdesktop lock.

The logic to handle communication with kdesktop_lock is now running
completely in a separate thread and event loop, meaning the main GUI
thread remains responsive all the time and can handle interaction with
X11, DCOP and DBUS calls. This resolves issue #589.
The commit also solves the first problem reported in issue #640 and
loosely related to PR #526.

Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
---
 kdesktop/DESIGN              |   13 +
 kdesktop/KScreensaverIface.h |   18 +-
 kdesktop/lock/lockprocess.h  |    3 -
 kdesktop/lockeng.cpp         | 1144 ++++++++++++++++++++++--------------------
 kdesktop/lockeng.h           |  162 +++---
 kdesktop/main.cpp            |   18 +-
 6 files changed, 721 insertions(+), 637 deletions(-)

diff --git a/kdesktop/DESIGN b/kdesktop/DESIGN
index 7e195378f..170a86a75 100644
--- a/kdesktop/DESIGN
+++ b/kdesktop/DESIGN
@@ -124,3 +124,16 @@ kdesktop_lock to kdesktop communication:
    - TTIN: the lock process is ready. This is sent after the process has been created/respawned
    - USR2: the lock/screensaver has been activated
    - USR1: the lock/screensaver has been unlocked/stopped
+
+Communication is handled by the screen saver engine defined in 'lockeng.{h,cpp}'.
+The engine is split into two parts, the 'SaverEngine' running in the GUI thread and
+the 'SaverEngineEventHandler' running in a separate thread and eventloop.
+The 'SaverEngine' handles communication with X11, DCOP and DBUS while the
+'SaverEngineEventHandler' handles communication with the actual lock process.
+Several actions require cooperation of the two parts, so in various methods
+there will be inter-thread calls (using timers or by emitting signals) to
+trigger the other side remaining logic.
+This complex design is necessary to avoid blocking the main GUI application event loop,
+which has several tasks to manage and therefore can't affort to wait in a suspended state.
+This was previously leading to deadlock when DCOP calls where executed on the secondary
+thread/eventloop, for example when changing desktop while the lock process was restarting.
diff --git a/kdesktop/KScreensaverIface.h b/kdesktop/KScreensaverIface.h
index de5c19f2b..b6434738e 100644
--- a/kdesktop/KScreensaverIface.h
+++ b/kdesktop/KScreensaverIface.h
@@ -12,11 +12,10 @@ public:
   KScreensaverIface() : DCOPObject("KScreensaverIface") {}
 
 k_dcop:
-  /** Lock the screen now even if the screensaver does not lock by default. */
+  /** Lock the screen now even if the screensaver does not lock by default */
   virtual void lock() = 0;
 
-  /** Save the screen now.  If the user has locking enabled, the screen is
-   * locked also. */
+  /** Start the screensaver now. If the user has locking enabled, the screen is locked also */
   virtual void save() = 0;
 
   /** Quit the screensaver if it is running */
@@ -29,22 +28,23 @@ k_dcop:
    * Enable/disable the screensaver
    * returns true if the action succeeded
    */
-  virtual bool enable( bool e ) = 0;
+  virtual bool enable(bool e) = 0;
 
   /** Is the screen currently blanked? */
   virtual bool isBlanked() = 0;
 
-  /** Reload the screensaver configuration. */
+  /** Reload the screensaver configuration */
   virtual void configure() = 0;
 
-  /** Only blank the screen (and possibly lock).  Do not use a custom
-   * screen saver in the interest of saving battery.
+  /**
+   * Set the screensaver to blank (and possibly lock).
+   * This method does not actually start the screensaver.
    */
-  virtual void setBlankOnly( bool blankOnly ) = 0;
+  virtual void setBlankOnly(bool blankOnly) = 0;
 
   /***
    * @internal
-  */
+   */
   virtual void saverLockReady() = 0;
 };
 
diff --git a/kdesktop/lock/lockprocess.h b/kdesktop/lock/lockprocess.h
index 5a6a3b4d4..1afc9bb46 100644
--- a/kdesktop/lock/lockprocess.h
+++ b/kdesktop/lock/lockprocess.h
@@ -106,9 +106,6 @@ class LockProcess : public TQWidget
 
 		TDECryptographicCardDevice* cryptographicCardDevice();
 
-	signals:
-		void terminateHelperThread();
-
 	public slots:
 		void quitSaver();
 		void preparePopup();
diff --git a/kdesktop/lockeng.cpp b/kdesktop/lockeng.cpp
index c4b781c2a..7216acc4f 100644
--- a/kdesktop/lockeng.cpp
+++ b/kdesktop/lockeng.cpp
@@ -6,7 +6,6 @@
 // Copyright (c) 2012 Timothy Pearson <kb9vqf@pearsoncomputing.net>
 //
 
-
 #include <config.h>
 
 #include <stdlib.h>
@@ -45,52 +44,49 @@
 #include "lockeng.moc"
 #include "kdesktopsettings.h"
 
-#define SYSTEMD_LOGIN1_SERVICE		"org.freedesktop.login1"
-#define SYSTEMD_LOGIN1_PATH		"/org/freedesktop/login1"
-#define SYSTEMD_LOGIN1_MANAGER_IFACE	"org.freedesktop.login1.Manager"
-#define SYSTEMD_LOGIN1_SESSION_IFACE	"org.freedesktop.login1.Session"
-#define SYSTEMD_LOGIN1_SEAT_IFACE	"org.freedesktop.login1.Seat"
+#include "xautolock_c.h"
+
+#define SYSTEMD_LOGIN1_SERVICE       "org.freedesktop.login1"
+#define SYSTEMD_LOGIN1_MANAGER_IFACE "org.freedesktop.login1.Manager"
+#define SYSTEMD_LOGIN1_SESSION_IFACE "org.freedesktop.login1.Session"
+#define SYSTEMD_LOGIN1_SEAT_IFACE    "org.freedesktop.login1.Seat"
+#define SYSTEMD_LOGIN1_PATH          "/org/freedesktop/login1"
 
 #define DBUS_CONN_NAME			"kdesktop_lock"
 
-#include "xautolock_c.h"
 extern xautolock_corner_t xautolock_corners[ 4 ];
-
 bool trinity_lockeng_sak_available = true;
 
-SaverEngine* m_masterSaverEngine = NULL;
+SaverEngineEventHandler *gbl_saverEngineEventHandler = nullptr;
+
 static void sigusr1_handler(int)
 {
-	if (m_masterSaverEngine) {
-		m_masterSaverEngine->m_threadHelperObject->slotLockProcessWaiting();
+	if (gbl_saverEngineEventHandler)
+	{
+		gbl_saverEngineEventHandler->lockProcessExited();
 	}
 }
+
 static void sigusr2_handler(int)
 {
-	if (m_masterSaverEngine) {
-		m_masterSaverEngine->m_threadHelperObject->slotLockProcessFullyActivated();
+	if (gbl_saverEngineEventHandler)
+	{
+		gbl_saverEngineEventHandler->lockProcessFullyActivated();
 	}
 }
+
 static void sigttin_handler(int)
 {
-	if (m_masterSaverEngine) {
-		m_masterSaverEngine->slotLockProcessReady();
+	if (gbl_saverEngineEventHandler)
+	{
+		gbl_saverEngineEventHandler->lockProcessReady();
 	}
 }
 
-//===========================================================================
-//
-// Screen saver engine. Doesn't handle the actual screensaver window,
-// starting screensaver hacks, or password entry. That's done by
-// a newly started process.
-//
 SaverEngine::SaverEngine()
-	: TQWidget(),
+	: TQObject(),
 	  KScreensaverIface(),
 	  mBlankOnly(false),
-	  mSAKProcess(NULL),
-	  mTerminationRequested(false),
-	  mSaverProcessReady(false),
 	  mNewVTAfterLockEngage(false),
 	  mValidCryptoCardInserted(false),
 	  mSwitchVTAfterLockEngage(-1),
@@ -99,7 +95,6 @@ SaverEngine::SaverEngine()
 	  systemdSession(0)
 {
 	// handle SIGUSR1
-	m_masterSaverEngine = this;
 	mSignalAction.sa_handler= sigusr1_handler;
 	sigemptyset(&(mSignalAction.sa_mask));
 	sigaddset(&(mSignalAction.sa_mask), SIGUSR1);
@@ -107,7 +102,6 @@ SaverEngine::SaverEngine()
 	sigaction(SIGUSR1, &mSignalAction, 0L);
 
 	// handle SIGUSR2
-	m_masterSaverEngine = this;
 	mSignalAction.sa_handler= sigusr2_handler;
 	sigemptyset(&(mSignalAction.sa_mask));
 	sigaddset(&(mSignalAction.sa_mask), SIGUSR2);
@@ -115,7 +109,6 @@ SaverEngine::SaverEngine()
 	sigaction(SIGUSR2, &mSignalAction, 0L);
 
 	// handle SIGTTIN
-	m_masterSaverEngine = this;
 	mSignalAction.sa_handler= sigttin_handler;
 	sigemptyset(&(mSignalAction.sa_mask));
 	sigaddset(&(mSignalAction.sa_mask), SIGTTIN);
@@ -123,92 +116,52 @@ SaverEngine::SaverEngine()
 	sigaction(SIGTTIN, &mSignalAction, 0L);
 
 	// Save X screensaver parameters
-	XGetScreenSaver(tqt_xdisplay(), &mXTimeout, &mXInterval,
-					&mXBlanking, &mXExposures);
-
-	mState = Waiting;
-	mXAutoLock = 0;
+	XGetScreenSaver(tqt_xdisplay(), &mXTimeout, &mXInterval, &mXBlanking, &mXExposures);
+
+	// Create event handler thread, event loop and object
+	m_eventHandlerThread = new TQEventLoopThread;
+	m_eventHandlerThread->start();
+	m_saverEngineEventHandler = new SaverEngineEventHandler(this);
+	gbl_saverEngineEventHandler = m_saverEngineEventHandler;
+	m_saverEngineEventHandler->moveToThread(m_eventHandlerThread);
+	connect(this, TQ_SIGNAL(terminateEventHandlerThread()), m_saverEngineEventHandler, TQ_SLOT(terminateThread()));
+	connect(this, TQ_SIGNAL(lockScreenSignal(bool)), m_saverEngineEventHandler, TQ_SLOT(lockScreen(bool)));
+	connect(this, TQ_SIGNAL(activateSaverOrLockSignal(LockType)),
+	        m_saverEngineEventHandler, TQ_SLOT(activateSaverOrLock(LockType)));
+
+	mXAutoLock = nullptr;
 	mEnabled = false;
 
-	m_helperThread = new TQEventLoopThread;
-	m_helperThread->start();
-	m_threadHelperObject = new SaverEngineThreadHelperObject;
-	m_threadHelperObject->moveToThread(m_helperThread);
-	connect(this, TQ_SIGNAL(terminateHelperThread()), m_threadHelperObject, TQ_SLOT(terminateThread()));
-	connect(m_threadHelperObject, TQ_SIGNAL(lockProcessWaiting()), this, TQ_SLOT(lockProcessWaiting()));
-	connect(m_threadHelperObject, TQ_SIGNAL(lockProcessFullyActivated()), this, TQ_SLOT(lockProcessFullyActivated()));
-
-	connect(&mLockProcess, TQ_SIGNAL(processExited(TDEProcess *)),
-						TQ_SLOT(lockProcessExited()));
-
 	configure();
 
-	// Create SAK process only if SAK is enabled
-	KSimpleConfig *config;
-  struct stat st;
-  if (stat( KDE_CONFDIR "/tdm/tdmdistrc" , &st) == 0) {
-    config = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/tdm/tdmdistrc" ));
-  }
-  else {
-    config = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/tdm/tdmrc" ));
-  }
-	config->setGroup("X-:*-Greeter");
-	bool useSAKProcess = false;
-#ifdef BUILD_TSAK
-	useSAKProcess = config->readBoolEntry("UseSAK", false) && KDesktopSettings::useTDESAK();
-#endif
-	if (useSAKProcess) {
-		mSAKProcess = new TDEProcess;
-		*mSAKProcess << "tdmtsak";
-		connect(mSAKProcess, TQ_SIGNAL(processExited(TDEProcess*)), this, TQ_SLOT(slotSAKProcessExited()));
-		TQTimer::singleShot( 0, this, TQ_SLOT(handleSecureDialog()) );
-	}
-
-	mLockProcess.clearArguments();
-	TQString path = TDEStandardDirs::findExe( "kdesktop_lock" );
-	if( path.isEmpty())
-	{
-		kdDebug( 1204 ) << "Can't find kdesktop_lock!" << endl;
-	}
-	mLockProcess << path;
-	mLockProcess << TQString( "--internal" ) << TQString( "%1" ).arg(getpid());
-	if (!mLockProcess.start())
-	{
-		kdDebug( 1204 ) << "Failed to start kdesktop_lock!" << endl;
-	}
-
-	// Prevent kdesktop_lock signals from being handled by the wrong (GUI) thread
-	sigemptyset(&mThreadBlockSet);
-	sigaddset(&mThreadBlockSet, SIGUSR1);
-	sigaddset(&mThreadBlockSet, SIGUSR2);
-	sigaddset(&mThreadBlockSet, SIGTTIN);
-	pthread_sigmask(SIG_BLOCK, &mThreadBlockSet, NULL);
-
-	// Wait for the saver process to signal ready...
-	if (!waitForLockProcessStart()) {
-		kdDebug( 1204 ) << "Failed to initialize kdesktop_lock (unexpected termination)!" << endl;
-	}
-
-	// lock the desktop if required
-  config->setGroup("X-:0-Core");
-  bool autoLoginEnable = config->readBoolEntry("AutoLoginEnable", false);
-  bool autoLoginLocked = config->readBoolEntry("AutoLoginLocked", false);
-  if (autoLoginEnable && autoLoginLocked) {
-		mLockProcess.kill(SIGTTOU);
-		mLockProcess.kill(SIGUSR1);
-	}
-	delete config;
-	config = NULL;
+	// Prevent kdesktop_lock signals from being handled by the main GUI thread.
+	// Those signals will be handled by m_eventHandlerThread instead
+	//
+	// Make sure to keep this code after the constructor of `m_eventHandlerThread`, so that
+	// the new thread starts with the signals unblocked.
+	sigset_t sigBlockMask;
+	sigemptyset(&sigBlockMask);
+	sigaddset(&sigBlockMask, SIGUSR1);
+	sigaddset(&sigBlockMask, SIGUSR2);
+	sigaddset(&sigBlockMask, SIGTTIN);
+	sigaddset(&sigBlockMask, SIGCHLD);
+	pthread_sigmask(SIG_BLOCK, &sigBlockMask, NULL);
+
+	// Start SAK and lock processes
+	TQTimer::singleShot(0, m_saverEngineEventHandler, TQ_SLOT(restartLockProcess()));
 
 #ifdef WITH_TDEHWLIB
 	// Initialize SmartCard readers
 	TDEGenericDevice *hwdevice;
 	TDEHardwareDevices *hwdevices = TDEGlobal::hardwareDevices();
 	TDEGenericHardwareList cardReaderList = hwdevices->listByDeviceClass(TDEGenericDeviceType::CryptographicCard);
-	for (hwdevice = cardReaderList.first(); hwdevice; hwdevice = cardReaderList.next()) {
-		TDECryptographicCardDevice* cdevice = static_cast<TDECryptographicCardDevice*>(hwdevice);
-		connect(cdevice, TQ_SIGNAL(certificateListAvailable(TDECryptographicCardDevice*)), this, TQ_SLOT(cryptographicCardInserted(TDECryptographicCardDevice*)));
-		connect(cdevice, TQ_SIGNAL(cardRemoved(TDECryptographicCardDevice*)), this, TQ_SLOT(cryptographicCardRemoved(TDECryptographicCardDevice*)));
+	for (hwdevice = cardReaderList.first(); hwdevice; hwdevice = cardReaderList.next())
+	{
+		TDECryptographicCardDevice *cdevice = static_cast<TDECryptographicCardDevice*>(hwdevice);
+		connect(cdevice, TQ_SIGNAL(certificateListAvailable(TDECryptographicCardDevice*)),
+		        this, TQ_SLOT(cryptographicCardInserted(TDECryptographicCardDevice*)));
+		connect(cdevice, TQ_SIGNAL(cardRemoved(TDECryptographicCardDevice*)),
+		        this, TQ_SLOT(cryptographicCardRemoved(TDECryptographicCardDevice*)));
 		cdevice->enableCardMonitoring(true);
 	}
 
@@ -216,9 +169,11 @@ SaverEngine::SaverEngine()
 	KUser userinfo;
 	TQString fileName = userinfo.homeDir() + "/.tde_card_login_state";
 	TQFile flagFile(fileName);
-	if (flagFile.open(IO_ReadOnly)) {
+	if (flagFile.open(IO_ReadOnly))
+	{
 		TQTextStream stream(&flagFile);
-		if (stream.readLine().startsWith("1")) {
+		if (stream.readLine().startsWith("1"))
+		{
 			// Card was likely used to log in
 			TQTimer::singleShot(5000, this, TQ_SLOT(cardStartupTimeout()));
 		}
@@ -229,70 +184,66 @@ SaverEngine::SaverEngine()
 	dBusConnect();
 }
 
-//---------------------------------------------------------------------------
-//
-// Destructor - usual cleanups.
-//
 SaverEngine::~SaverEngine()
 {
-	if (mState == Waiting) {
-		kill(mLockProcess.pid(), SIGKILL);
-	}
-
-	mLockProcess.detach(); // don't kill it if we crash
+	m_saverEngineEventHandler->terminateLockProcess();
 	delete mXAutoLock;
-
 	dBusClose();
 
 	// Restore X screensaver parameters
-	XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking,
-					mXExposures);
-
-	terminateHelperThread();
-	m_helperThread->wait();
-	delete m_threadHelperObject;
-	delete m_helperThread;
+	XSetScreenSaver(tqt_xdisplay(), mXTimeout, mXInterval, mXBlanking, mXExposures);
+	emit terminateEventHandlerThread();
+	m_eventHandlerThread->wait();
+	delete m_saverEngineEventHandler;
+	delete m_eventHandlerThread;
 }
 
-void SaverEngine::cardStartupTimeout() {
-	if (!mValidCryptoCardInserted) {
-		// Restore saver timeout
-		configure();
-
-		// Force lock
-		lockScreen();
+void SaverEngine::cardStartupTimeout()
+{
+	if (!mValidCryptoCardInserted)
+	{
+		configure();  // Restore saver timeout
+		lockScreen(); // Force lock
 	}
 }
 
-void SaverEngine::cryptographicCardInserted(TDECryptographicCardDevice* cdevice) {
+void SaverEngine::cryptographicCardInserted(TDECryptographicCardDevice* cdevice)
+{
 #ifdef WITH_TDEHWLIB
 	TQString login_name = TQString::null;
 	X509CertificatePtrList certList = cdevice->cardX509Certificates();
-	if (certList.count() > 0) {
+	if (certList.count() > 0)
+	{
 		KSSLCertificate* card_cert = NULL;
 		card_cert = KSSLCertificate::fromX509(certList[0]);
 		TQStringList cert_subject_parts = TQStringList::split("/", card_cert->getSubject(), false);
-		for (TQStringList::Iterator it = cert_subject_parts.begin(); it != cert_subject_parts.end(); ++it ) {
+		for (TQStringList::Iterator it = cert_subject_parts.begin(); it != cert_subject_parts.end(); ++it)
+		{
 			TQString lcpart = (*it).lower();
-			if (lcpart.startsWith("cn=")) {
+			if (lcpart.startsWith("cn="))
+			{
 				login_name = lcpart.right(lcpart.length() - strlen("cn="));
 			}
 		}
 		delete card_cert;
 	}
 
-	if (login_name != "") {
+	if (login_name != "")
+	{
 		KUser user;
-		if (login_name == user.loginName()) {
+		if (login_name == user.loginName())
+		{
 			mValidCryptoCardInserted = true;
 		}
 	}
 #endif
 }
 
-void SaverEngine::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice) {
+void SaverEngine::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice)
+{
 #ifdef WITH_TDEHWLIB
-	if (mValidCryptoCardInserted) {
+	if (mValidCryptoCardInserted)
+	{
 		mValidCryptoCardInserted = false;
 
 		// Restore saver timeout
@@ -304,226 +255,166 @@ void SaverEngine::cryptographicCardRemoved(TDECryptographicCardDevice* cdevice)
 #endif
 }
 
-//---------------------------------------------------------------------------
-//
-// This should be called only using DCOP.
-//
+// DCOP interface method
 void SaverEngine::lock()
 {
 	lockScreen(true);
 }
 
-//---------------------------------------------------------------------------
-//
-// Lock the screen
-//
-void SaverEngine::lockScreen(bool DCOP)
+void SaverEngine::lockScreen(bool dcop)
 {
-	if (mValidCryptoCardInserted) {
+	if (mValidCryptoCardInserted)
+	{
+		kdDebug(1204) << "SaverEngine: crypto card inserted, ignore lock request" << endl;
 		return;
 	}
+	emit lockScreenSignal(dcop);
+}
 
-	bool ok = true;
-	if (mState == Waiting)
-	{
-		ok = startLockProcess( ForceLock );
-		// It takes a while for kdesktop_lock to start and lock the screen.
-		// Therefore delay the DCOP call until it tells kdesktop that the locking is in effect.
-		// This is done only for --forcelock .
-		if( ok && mState != Saving )
-		{
-			if (DCOP) {
-				DCOPClientTransaction* trans = tdeApp->dcopClient()->beginTransaction();
-				if (trans) {
-					mLockTransactions.append( trans );
-				}
-			}
-		}
-	}
-	else
+void SaverEngine::lockScreenGUI()
+{
+	DCOPClientTransaction *trans = tdeApp->dcopClient()->beginTransaction();
+	if (trans)
 	{
-		mLockProcess.kill( SIGHUP );
+		mLockTransactions.append(trans);
 	}
 }
 
 void SaverEngine::processLockTransactions()
 {
-	for( TQValueVector< DCOPClientTransaction* >::ConstIterator it = mLockTransactions.begin();
-		 it != mLockTransactions.end();
-		 ++it )
+	TQValueVector<DCOPClientTransaction*>::ConstIterator it = mLockTransactions.begin();
+	for (; it != mLockTransactions.end(); ++it)
 	{
 		TQCString replyType = "void";
 		TQByteArray arr;
-		tdeApp->dcopClient()->endTransaction( *it, replyType, arr );
+		tdeApp->dcopClient()->endTransaction(*it, replyType, arr);
 	}
 	mLockTransactions.clear();
 }
 
 void SaverEngine::saverLockReady()
 {
-	if( mState != Engaging )
+	if (m_saverEngineEventHandler->getState() != SaverState::Engaging)
 	{
-		kdDebug( 1204 ) << "Got unexpected saverReady()" << endl;
+		kdDebug(1204) << "Got unexpected saverLockReady()" << endl;
 	}
-	kdDebug( 1204 ) << "Saver Lock Ready" << endl;
+
+	kdDebug(1204) << "Saver Lock Ready" << endl;
 	processLockTransactions();
 }
 
-//---------------------------------------------------------------------------
+// DCOP interface method
 void SaverEngine::save()
 {
-	if (!mValidCryptoCardInserted) {
-		if (mState == Waiting) {
-			startLockProcess( DefaultLock );
-		}
+	if (mValidCryptoCardInserted)
+	{
+		kdDebug(1204) << "SaverEngine: crypto card inserted, ignore save request" << endl;
+		return;
 	}
+	TQTimer::singleShot(0, m_saverEngineEventHandler, TQ_SLOT(saveScreen()));
 }
 
-//---------------------------------------------------------------------------
+// DCOP interface method
 void SaverEngine::quit()
 {
-	if (mState == Saving || mState == Engaging)
-	{
-		stopLockProcess();
-	}
+	TQTimer::singleShot(0, m_saverEngineEventHandler, TQ_SLOT(stopLockProcess()));
 }
 
-//---------------------------------------------------------------------------
+// DCOP interface method
 bool SaverEngine::isEnabled()
 {
 	return mEnabled;
 }
 
-//---------------------------------------------------------------------------
-bool SaverEngine::enable( bool e )
+// DCOP interface method
+bool SaverEngine::enable(bool e)
 {
-	if ( e == mEnabled )
-	return true;
+	if (e == mEnabled)
+		return true;
 
 	// If we aren't in a suitable state, we will not reconfigure.
-	if (mState != Waiting)
+	if (m_saverEngineEventHandler->getState() != SaverState::Waiting)
 		return false;
 
 	mEnabled = e;
 
-	if (mEnabled) {
-		if ( !mXAutoLock ) {
+	if (mEnabled)
+	{
+		if (!mXAutoLock)
+		{
 			mXAutoLock = new XAutoLock();
 			connect(mXAutoLock, TQ_SIGNAL(timeout()), TQ_SLOT(idleTimeout()));
 		}
 		mXAutoLock->setTimeout(mTimeout);
 		mXAutoLock->setDPMS(true);
-		//mXAutoLock->changeCornerLockStatus( mLockCornerTopLeft, mLockCornerTopRight, mLockCornerBottomLeft, mLockCornerBottomRight);
 	
 		// We'll handle blanking
 		XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
-		kdDebug() << "XSetScreenSaver " << mTimeout + 10 << endl;
-
 		mXAutoLock->start();
-
-		kdDebug(1204) << "Saver Engine started, timeout: " << mTimeout << endl;
+		kdDebug(1204) << "Saver engine started, timeout: " << mTimeout << endl;
 	}
-	else {
-		if (mXAutoLock) {
+	else
+	{
+		if (mXAutoLock)
+		{
 			delete mXAutoLock;
-			mXAutoLock = 0;
+			mXAutoLock = nullptr;
 		}
 	
-		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
+		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset);
 		XSetScreenSaver(tqt_xdisplay(), 0, mXInterval,  PreferBlanking, DontAllowExposures);
-		kdDebug(1204) << "Saver Engine disabled" << endl;
+		kdDebug(1204) << "Saver engine disabled" << endl;
 	}
 
 	return true;
 }
 
-//---------------------------------------------------------------------------
+// DCOP interface method
 bool SaverEngine::isBlanked()
 {
-	return (mState != Waiting);
+	return (m_saverEngineEventHandler->getState() != SaverState::Waiting);
 }
 
 void SaverEngine::enableExports()
 {
 #ifdef TQ_WS_X11
-	kdDebug(270) << k_lineinfo << "activating background exports.\n";
+	kdDebug(270) << k_lineinfo << "activating background exports" << endl;
 	DCOPClient *client = tdeApp->dcopClient();
-	if (!client->isAttached()) {
+	if (!client->isAttached())
+	{
 		client->attach();
 	}
 	TQByteArray data;
-	TQDataStream args( data, IO_WriteOnly );
+	TQDataStream args(data, IO_WriteOnly);
 	args << 1;
 	
-	TQCString appname( "kdesktop" );
+	TQCString appname("kdesktop");
 	int screen_number = DefaultScreen(tqt_xdisplay());
-	if ( screen_number ) {
-		appname.sprintf("kdesktop-screen-%d", screen_number );
+	if (screen_number)
+	{
+		appname.sprintf("kdesktop-screen-%d", screen_number);
 	}
 	
-	client->send( appname, "KBackgroundIface", "setExport(int)", data );
+	client->send(appname, "KBackgroundIface", "setExport(int)", data);
 #endif
 }
 
-//---------------------------------------------------------------------------
-void SaverEngine::handleSecureDialog()
-{
-	// Wait for SAK press
-	if (mSAKProcess && !mSAKProcess->isRunning()) {
-		mSAKProcess->start();
-	}
-}
-
-void SaverEngine::slotSAKProcessExited()
-{
-	if (!mSAKProcess) {
-		printf("[kdesktop] SAK process does not exist. Something went wrong. Ignoring...\n"); fflush(stdout);
-		return;
-	}
-	int retcode = mSAKProcess->exitStatus();
-	if ((retcode != 0) && (mSAKProcess->normalExit())) {
-		trinity_lockeng_sak_available = false;
-		printf("[kdesktop] SAK driven secure dialog is not available for use (retcode %d).  Check tdmtsak for proper functionality.\n", retcode); fflush(stdout);
-	}
-
-	if (mState == Preparing) {
-		return;
-	}
-
-	if (mSAKProcess->normalExit() && trinity_lockeng_sak_available) {
-		bool ok = true;
-		if (mState == Waiting)
-		{
-			ok = startLockProcess( SecureDialog );
-			if( ok && mState != Saving )
-			{
-			}
-		}
-		else
-		{
-			mLockProcess.kill( SIGHUP );
-		}
-	}
-}
-
-//---------------------------------------------------------------------------
-//
 // Read and apply configuration.
-//
 void SaverEngine::configure()
 {
 	// If we aren't in a suitable state, we will not reconfigure.
-	if (mState != Waiting) {
+	if (m_saverEngineEventHandler->getState() != SaverState::Waiting)
+	{
 		return;
 	}
 
 	// create a new config obj to ensure we read the latest options
 	KDesktopSettings::self()->readConfig();
 
-	bool e = KDesktopSettings::screenSaverEnabled();
 	mTimeout = KDesktopSettings::timeout();
-
-	mEnabled = !e;   // force the enable()
+	bool e = KDesktopSettings::screenSaverEnabled();
+	mEnabled = !e;   // enable the screensaver by forcibly toggling it
+	enable(e);
 
 	int action;
 	action = KDesktopSettings::actionTopLeft();
@@ -534,320 +425,183 @@ void SaverEngine::configure()
 	xautolock_corners[2] = applyManualSettings(action);
 	action = KDesktopSettings::actionBottomRight();
 	xautolock_corners[3] = applyManualSettings(action);
-
-	enable( e );
 }
 
-//---------------------------------------------------------------------------
-//
-//  Set a variable to indicate only using the blanker and not the saver.
-//
-void SaverEngine::setBlankOnly( bool blankOnly )
+// DCOP interface method
+// Set a variable to indicate only to blank the screen and not use the saver
+void SaverEngine::setBlankOnly(bool blankOnly)
 {
 	mBlankOnly = blankOnly;
-	// FIXME: if running, stop and restart?  What about security
-	// implications of this?
 }
 
-bool SaverEngine::restartDesktopLockProcess()
+void SaverEngine::activateSaverOrLockGUI()
 {
-	if (!mLockProcess.isRunning()) {
-		mSaverProcessReady = false;
-		mLockProcess.clearArguments();
-		TQString path = TDEStandardDirs::findExe( "kdesktop_lock" );
-		if (path.isEmpty()) {
-			kdDebug( 1204 ) << "Can't find kdesktop_lock!" << endl;
-			return false;
-		}
-		mLockProcess << path;
-		mLockProcess << TQString( "--internal" ) << TQString( "%1" ).arg(getpid());
-		if (!mLockProcess.start()) {
-			kdDebug( 1204 ) << "Failed to start kdesktop_lock!" << endl;
-			return false;
-		}
-		// Wait for the saver process to signal ready...
-		if (!waitForLockProcessStart()) {
-			kdDebug( 1204 ) << "Failed to initialize kdesktop_lock (unexpected termination)!" << endl;
-			return false;
-		}
-	}
-	return true;
-}
-
-//---------------------------------------------------------------------------
-//
-// Start the screen saver.
-//
-bool SaverEngine::startLockProcess( LockType lock_type )
-{
-	int ret;
-
-	if (mState == Saving) {
-		return true;
-	}
-
-	mState = Preparing;
-	if (mSAKProcess) {
-		mSAKProcess->kill(SIGTERM);
-	}
-
-	enableExports();
-
-	kdDebug(1204) << "SaverEngine: starting saver" << endl;
-	emitDCOPSignal("KDE_start_screensaver()", TQByteArray());
-
-	if (!restartDesktopLockProcess()) {
-		mState = Waiting;
-		return false;
-	}
-
-	switch( lock_type )
-	{
-		case ForceLock:
-			mLockProcess.kill(SIGUSR1);		// Request forcelock
-			break;
-		case DontLock:
-			mLockProcess.kill(SIGUSR2);		// Request dontlock
-			break;
-		case SecureDialog:
-			mLockProcess.kill(SIGWINCH);	// Request secure dialog
-			break;
-		default:
-			break;
-	}
-	if (mBlankOnly) {
-		mLockProcess.kill(SIGTTIN);		// Request blanking
-	}
-
-	ret = mLockProcess.kill(SIGTTOU);			// Start lock
-	if (!ret) {
-		mState = Waiting;
-		return false;
-	}
 	XSetScreenSaver(tqt_xdisplay(), 0, mXInterval,  PreferBlanking, mXExposures);
-
-	mState = Engaging;
-	if (mXAutoLock) {
+	if (mXAutoLock)
+	{
 		mXAutoLock->stop();
 	}
-	return true;
+	emitDCOPSignal("KDE_start_screensaver()", TQByteArray());
 }
 
-//---------------------------------------------------------------------------
-//
-// Stop the screen saver.
-//
-void SaverEngine::stopLockProcess()
+void SaverEngine::stopLockProcessGUI()
 {
-	if (mState == Waiting) {
-		kdWarning(1204) << "SaverEngine::stopSaver() saver not active" << endl;
-		return;
-	}
-	kdDebug(1204) << "SaverEngine: stopping lock" << endl;
 	emitDCOPSignal("KDE_stop_screensaver()", TQByteArray());
 
-	mTerminationRequested = true;
-	mLockProcess.kill();
-
-	if (mEnabled) {
-		if (mXAutoLock) {
+	if (mEnabled)
+	{
+		if (mXAutoLock)
+		{
 			mXAutoLock->start();
 		}
-		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
+		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset);
 		XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
 	}
 	processLockTransactions();
-	mState = Waiting;
 
-	if( systemdSession && systemdSession->canSend() ) {
+	if (systemdSession && systemdSession->canSend())
+	{
 		TQValueList<TQT_DBusData> params;
 		params << TQT_DBusData::fromBool(false);
 		TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params);
 	}
 }
 
-void SaverEngine::recoverFromHackingAttempt()
+void SaverEngine::terminateTDESession()
 {
-	// Try to relaunch saver with forcelock
-	if (!startLockProcess(ForceLock)) {
-		// Terminate the TDE session ASAP!
-		// Values are explained at http://lists.kde.org/?l=kde-linux&m=115770988603387
-		TQByteArray data;
-		TQDataStream arg(data, IO_WriteOnly);
-		arg << (int)0 << (int)0 << (int)2;
-		if (!tdeApp->dcopClient()->send("ksmserver", "default", "logout(int,int,int)", data)) {
-			// Someone got to DCOP before we did
-			// Try an emergency system logout
-			system("logout");
-		}
-	}
-}
-
-void SaverEngine::lockProcessExited()
-{
-	bool abnormalExit = false;
-	if (!mLockProcess.normalExit()) {
-		abnormalExit = true;
-	}
-	else {
-		if (mLockProcess.exitStatus() != 0) {
-			abnormalExit = true;
-		}
-	}
-	if (mTerminationRequested) {
-		abnormalExit = false;
-		mTerminationRequested = false;
-	}
-	if (abnormalExit) {
-		// PROBABLE HACKING ATTEMPT DETECTED
-		restartDesktopLockProcess();
-		mState = Waiting;
-		TQTimer::singleShot( 100, this, TQ_SLOT(recoverFromHackingAttempt()) );
-	}
-	else {
-		// Restart the lock process
-		restartDesktopLockProcess();
+	// Terminate the TDE session ASAP!
+	// Values are explained at http://lists.kde.org/?l=kde-linux&m=115770988603387
+	TQByteArray data;
+	TQDataStream arg(data, IO_WriteOnly);
+	arg << (int)0 << (int)0 << (int)2;
+	if (!tdeApp->dcopClient()->send("ksmserver", "default", "logout(int,int,int)", data))
+	{
+		// Someone got to DCOP before we did. Try an emergency system logout
+		system("logout");
 	}
 }
 
-void SaverEngineThreadHelperObject::slotLockProcessWaiting()
+void SaverEngine::lockProcessFullyActivatedGUI()
 {
-	// lockProcessWaiting cannot be called directly from a signal handler, as it will hang in certain obscure circumstances
-	// Instead we use a single-shot timer to immediately call lockProcessWaiting once control has returned to the Qt main loop
-	lockProcessWaiting();
-}
-
-void SaverEngineThreadHelperObject::slotLockProcessFullyActivated()
-{
-	lockProcessFullyActivated();
-}
-
-void SaverEngine::lockProcessFullyActivated()
-{
-	mState = Saving;
-
-	if( systemdSession && systemdSession->canSend() ) {
+	if (systemdSession && systemdSession->canSend())
+	{
 		TQValueList<TQT_DBusData> params;
 		params << TQT_DBusData::fromBool(true);
 		TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params);
 	}
 
-	if (mNewVTAfterLockEngage) {
+	if (mNewVTAfterLockEngage)
+	{
 		DM().startReserve();
 		mNewVTAfterLockEngage = false;
 	}
-	else if (mSwitchVTAfterLockEngage != -1) {
+	else if (mSwitchVTAfterLockEngage != -1)
+	{
 		DM().switchVT(mSwitchVTAfterLockEngage);
 		mSwitchVTAfterLockEngage = -1;
 	}
 }
 
-void SaverEngine::slotLockProcessReady()
-{
-	mSaverProcessReady = true;
-}
-
-void SaverEngine::lockProcessWaiting()
+void SaverEngine::lockProcessWaitingGUI()
 {
-	kdDebug(1204) << "SaverEngine: lock exited" << endl;
-	if (trinity_lockeng_sak_available) {
-		handleSecureDialog();
-	}
-	if( mState == Waiting ) {
-		return;
-	}
 	emitDCOPSignal("KDE_stop_screensaver()", TQByteArray());
-	if (mEnabled) {
-		if (mXAutoLock) {
+	if (mEnabled)
+	{
+		if (mXAutoLock)
+		{
 			mXAutoLock->start();
 		}
-		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
+		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset);
 		XSetScreenSaver(tqt_xdisplay(), mTimeout + 10, mXInterval, PreferBlanking, mXExposures);
 	}
 	processLockTransactions();
-	mState = Waiting;
 
-	if( systemdSession && systemdSession->canSend() ) {
+	if (systemdSession && systemdSession->canSend())
+	{
 		TQValueList<TQT_DBusData> params;
 		params << TQT_DBusData::fromBool(false);
 		TQT_DBusMessage reply = systemdSession->sendWithReply("SetIdleHint", params);
 	}
 }
 
-//---------------------------------------------------------------------------
-//
 // XAutoLock has detected the required idle time.
-//
 void SaverEngine::idleTimeout()
 {
-	if (!mValidCryptoCardInserted) {
+	if (!mValidCryptoCardInserted)
+	{
 		// disable X screensaver
-		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset );
+		XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset);
 		XSetScreenSaver(tqt_xdisplay(), 0, mXInterval, PreferBlanking, DontAllowExposures);
-		startLockProcess( DefaultLock );
+		emit activateSaverOrLockSignal(DefaultLock);
 	}
 }
 
 xautolock_corner_t SaverEngine::applyManualSettings(int action)
 {
-	if (action == 0) {
+	if (action == 0)
+	{
 		kdDebug() << "no lock" << endl;
 		return ca_nothing;
 	}
-	else if (action == 1) {
+	else if (action == 1)
+	{
 		kdDebug() << "lock screen" << endl;
 		return ca_forceLock;
 	}
-	else if (action == 2) {
+	else if (action == 2)
+	{
 		kdDebug() << "prevent lock" << endl;
 		return ca_dontLock;
 	}
-	else{
+	else
+	{
 		kdDebug() << "no lock nothing" << endl;
 		return ca_nothing;
 	}
 }
 
-/*!
- * This function try a reconnect to D-Bus.
+/*
+ * This function try to reconnect to D-Bus.
  * \return boolean with the result of the operation
  * \retval true if successful reconnected to D-Bus
  * \retval false if unsuccessful
  */
-bool SaverEngine::dBusReconnect() {
-	// close D-Bus connection
-	dBusClose();
-	// init D-Bus conntection
-	return (dBusConnect());
-}
+bool SaverEngine::dBusReconnect()
+{
+	dBusClose();            // close D-Bus connection
+	return (dBusConnect()); // init D-Bus conntection
+} 
 
-/*!
- * This function is used to close D-Bus connection.
- */
-void SaverEngine::dBusClose() {
-	if( dBusConn.isConnected() ) {
-		if( dBusLocal ) {
+// This function is used to close D-Bus connection.
+void SaverEngine::dBusClose()
+{
+	if (dBusConn.isConnected())
+	{
+		if (dBusLocal)
+		{
 			delete dBusLocal;
-			dBusLocal = 0;
+			dBusLocal = nullptr;
 		}
-		if( dBusWatch ) {
+		if (dBusWatch)
+		{
 			delete dBusWatch;
-			dBusWatch = 0;
+			dBusWatch = nullptr;
 		}
-		if( systemdSession ) {
+		if (systemdSession)
+		{
 			delete systemdSession;
-			systemdSession = 0;
+			systemdSession = nullptr;
 		}
 	}
 	dBusConn.closeConnection(DBUS_CONN_NAME);
 }
 
-/*!
- * This function is used to connect to D-Bus.
- */
-bool SaverEngine::dBusConnect() {
+// This function is used to connect to D-Bus.
+bool SaverEngine::dBusConnect()
+{
 	dBusConn = TQT_DBusConnection::addConnection(TQT_DBusConnection::SystemBus, DBUS_CONN_NAME);
-	if( !dBusConn.isConnected() ) {
+	if (!dBusConn.isConnected())
+	{
 		kdError() << "Failed to open connection to system message bus: " << dBusConn.lastError().message() << endl;
 		TQTimer::singleShot(4000, this, TQ_SLOT(dBusReconnect()));
 		return false;
@@ -856,44 +610,49 @@ bool SaverEngine::dBusConnect() {
 	// watcher for Disconnect signal
 	dBusLocal = new TQT_DBusProxy(DBUS_SERVICE_DBUS, DBUS_PATH_LOCAL, DBUS_INTERFACE_LOCAL, dBusConn);
 	TQObject::connect(dBusLocal, TQ_SIGNAL(dbusSignal(const TQT_DBusMessage&)),
-			  this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&)));
+	        this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&)));
 
 	// watcher for NameOwnerChanged signals
 	dBusWatch = new TQT_DBusProxy(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, dBusConn);
 	TQObject::connect(dBusWatch, TQ_SIGNAL(dbusSignal(const TQT_DBusMessage&)),
-			  this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&)));
+	        this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&)));
 
 	// find already running SystemD
 	TQT_DBusProxy checkSystemD(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, dBusConn);
-	if( checkSystemD.canSend() ) {
+	if (checkSystemD.canSend())
+	{
 		TQValueList<TQT_DBusData> params;
 		params << TQT_DBusData::fromString(SYSTEMD_LOGIN1_SERVICE);
 		TQT_DBusMessage reply = checkSystemD.sendWithReply("NameHasOwner", params);
-		if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1 && reply[0].toBool() ) {
+		if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1 && reply[0].toBool())
+		{
 			onDBusServiceRegistered(SYSTEMD_LOGIN1_SERVICE);
 		}
 	}
 	return true;
 }
 
-/*!
- * This function handles D-Bus service registering
- */
-void SaverEngine::onDBusServiceRegistered(const TQString& service) {
-	if( service == SYSTEMD_LOGIN1_SERVICE ) {
+// This function handles D-Bus service registering
+void SaverEngine::onDBusServiceRegistered(const TQString& service)
+{
+	if (service == SYSTEMD_LOGIN1_SERVICE)
+	{
 		// get current systemd session
 		TQT_DBusProxy managerIface(SYSTEMD_LOGIN1_SERVICE, SYSTEMD_LOGIN1_PATH, SYSTEMD_LOGIN1_MANAGER_IFACE, dBusConn);
 		TQT_DBusObjectPath systemdSessionPath = TQT_DBusObjectPath();
-		if( managerIface.canSend() ) {
+		if (managerIface.canSend())
+		{
 			TQValueList<TQT_DBusData> params;
-			params << TQT_DBusData::fromUInt32( getpid() );
+			params << TQT_DBusData::fromUInt32(getpid());
 			TQT_DBusMessage reply = managerIface.sendWithReply("GetSessionByPID", params);
-			if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1 ) {
+			if (reply.type() == TQT_DBusMessage::ReplyMessage && reply.count() == 1)
+			{
 				systemdSessionPath = reply[0].toObjectPath();
 			}
 		}
 		// wather for systemd session signals
-		if( systemdSessionPath.isValid() ) {
+		if (systemdSessionPath.isValid())
+		{
 			systemdSession = new TQT_DBusProxy(SYSTEMD_LOGIN1_SERVICE, systemdSessionPath, SYSTEMD_LOGIN1_SESSION_IFACE, dBusConn);
 			TQObject::connect(systemdSession, TQ_SIGNAL(dbusSignal(const TQT_DBusMessage&)),
 					  this, TQ_SLOT(handleDBusSignal(const TQT_DBusMessage&)));
@@ -902,41 +661,43 @@ void SaverEngine::onDBusServiceRegistered(const TQString& service) {
 	}
 }
 
-/*!
- * This function handles D-Bus service unregistering
- */
-void SaverEngine::onDBusServiceUnregistered(const TQString& service) {
-	if( service == SYSTEMD_LOGIN1_SERVICE ) {
-		if( systemdSession ) {
+// This function handles D-Bus service unregistering
+void SaverEngine::onDBusServiceUnregistered(const TQString& service)
+{
+	if (service == SYSTEMD_LOGIN1_SERVICE)
+	{
+		if (systemdSession)
+		{
 			delete systemdSession;
-			systemdSession = 0;
+			systemdSession = nullptr;
 		}
 		return;
 	}
 }
 
-/*!
- * This function handles signals from the D-Bus daemon.
- */
-void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg) {
+// This function handles signals from the D-Bus daemon.
+void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg)
+{
 	// dbus terminated
-	if( msg.path() == DBUS_PATH_LOCAL
-	 && msg.interface() == DBUS_INTERFACE_LOCAL
-	 && msg.member() == "Disconnected" ) {
+	if (msg.path() == DBUS_PATH_LOCAL && msg.interface() == DBUS_INTERFACE_LOCAL &&
+	        msg.member() == "Disconnected")
+	{
 		dBusClose();
 		TQTimer::singleShot(1000, this, TQ_SLOT(dBusReconnect()));
 		return;
 	}
 
 	// service registered / unregistered
-	if( msg.path() == DBUS_PATH_DBUS
-	 && msg.interface() == DBUS_INTERFACE_DBUS
-	 && msg.member() == "NameOwnerChanged" ) {
-		if( msg[1].toString().isEmpty() ) {
+	if (msg.path() == DBUS_PATH_DBUS && msg.interface() == DBUS_INTERFACE_DBUS &&
+	        msg.member() == "NameOwnerChanged")
+	{
+		if (msg[1].toString().isEmpty())
+		{
 			// old-owner is empty
 			onDBusServiceRegistered(msg[0].toString());
 		}
-		if( msg[2].toString().isEmpty() ) {
+		if (msg[2].toString().isEmpty())
+		{
 			// new-owner is empty
 			onDBusServiceUnregistered(msg[0].toString());
 		}
@@ -944,69 +705,364 @@ void SaverEngine::handleDBusSignal(const TQT_DBusMessage& msg) {
 	}
 
 	// systemd signal Lock()
-	if( systemdSession && systemdSession->canSend()
-	 && msg.path() == systemdSession->path()
-	 && msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE
-	 && msg.member() == "Lock") {
+	if (systemdSession && systemdSession->canSend() && msg.path() == systemdSession->path() &&
+	        msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE && msg.member() == "Lock")
+	{
 		lockScreen();
 		return;
 	}
 
 	// systemd signal Unlock()
-	if( systemdSession && systemdSession->canSend()
-	 && msg.path() == systemdSession->path()
-	 && msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE
-	 && msg.member() == "Unlock") {
+	if (systemdSession && systemdSession->canSend() && msg.path() == systemdSession->path() &&
+	        msg.interface() == SYSTEMD_LOGIN1_SESSION_IFACE && msg.member() == "Unlock")
+	{
 		// unlock?
 		return;
 	}
 }
 
-bool SaverEngine::waitForLockProcessStart() {
-	sigset_t new_mask;
-	sigset_t empty_mask;
-	sigemptyset(&empty_mask);
+void SaverEngine::lockScreenAndDoNewSession()
+{
+	mNewVTAfterLockEngage = true;
+	lockScreen();
+}
 
-	// ensure that SIGCHLD is not subject to a race condition
-	sigemptyset(&new_mask);
-	sigaddset(&new_mask, SIGCHLD);
+void SaverEngine::lockScreenAndSwitchSession(int vt)
+{
+	mSwitchVTAfterLockEngage = vt;
+	lockScreen();
+}
 
-	pthread_sigmask(SIG_BLOCK, &new_mask, NULL);
-	while ((mLockProcess.isRunning()) && (!mSaverProcessReady)) {
-		// wait for any signal(s) to arrive
-		sigsuspend(&empty_mask);
+SaverEngineEventHandler::SaverEngineEventHandler(SaverEngine *engine) :
+        m_state(Waiting), m_saverProcessReady(false), m_lockProcessRestarting(false),
+        m_terminationRequest(false), m_saverEngine(engine), m_SAKProcess(nullptr)
+{
+	connect(&m_lockProcess, TQ_SIGNAL(processExited(TDEProcess*)),
+	        this, TQ_SLOT(slotLockProcessExited()));
+}
+
+void SaverEngineEventHandler::terminateLockProcess()
+{
+	if (m_state == Waiting)
+	{
+		kill(m_lockProcess.pid(), SIGKILL);
+	}
+	m_lockProcess.detach(); // don't kill it if we crash
+}
+											
+void SaverEngineEventHandler::lockProcessExited()
+{
+	kdDebug(1204) << "SaverEngineEventHandler: lock exited" << endl;
+
+	if (trinity_lockeng_sak_available)
+	{
+		startSAKProcess();
+	}
+	if (m_state == Waiting)
+	{
+		return;
 	}
-	pthread_sigmask(SIG_UNBLOCK, &new_mask, NULL);
 
-	return mLockProcess.isRunning();
+	m_state = Waiting;
+	TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockProcessWaitingGUI()));
 }
 
-bool SaverEngine::waitForLockEngage() {
-	sigset_t empty_mask;
-	sigemptyset(&empty_mask);
+void SaverEngineEventHandler::lockProcessFullyActivated()
+{
+	m_state = Saving;
+	TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockProcessFullyActivatedGUI()));
+}
 
-	// wait for SIGUSR1, SIGUSR2, SIGTTIN
-	while ((mLockProcess.isRunning()) && (mState != Waiting) && (mState != Saving)) {
-		// wait for any signal(s) to arrive
-		sigsuspend(&empty_mask);
+void SaverEngineEventHandler::lockProcessReady()
+{
+	m_saverProcessReady = true;
+}
+
+void SaverEngineEventHandler::lockScreen(bool dcop)
+{
+	if (m_lockProcessRestarting)
+	{
+		kdDebug(1204) << "SaverEngineEventHandler: lock process is restarting, can't handle lock request" << endl;
+		return;
 	}
 
-	return mLockProcess.isRunning();
+	bool ok = true;
+	if (m_state == Waiting)
+	{
+		ok = activateSaverOrLock(ForceLock);
+		// It takes a while for kdesktop_lock to start and lock the screen.
+		// Therefore delay the DCOP call until it tells kdesktop that the locking is in effect.
+		// This is done only for --forcelock .
+		if (ok && m_state != Saving)
+		{
+			if (dcop)
+			{
+				TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(lockScreenGUI()));
+			}
+		}
+	}
 }
 
-void SaverEngine::lockScreenAndDoNewSession() {
-	mNewVTAfterLockEngage = true;
-	lockScreen();
+void SaverEngineEventHandler::saveScreen()
+{
+	if (m_lockProcessRestarting)
+	{
+		kdDebug(1204) << "SaverEngineEventHandler: lock process is restarting, can't handle save request" << endl;
+		return;
+	}
+
+	if (m_state == Waiting)
+	{
+		activateSaverOrLock(DefaultLock);
+	}
 }
 
-void SaverEngine::lockScreenAndSwitchSession(int vt) {
-	mSwitchVTAfterLockEngage = vt;
-	lockScreen();
+void SaverEngineEventHandler::slotLockProcessExited()
+{
+	m_lockProcessRestarting = true;
+
+	bool abnormalExit = false;
+	if (!m_lockProcess.normalExit())
+	{
+		abnormalExit = true;
+	}
+	else if (m_lockProcess.exitStatus() != 0)
+	{
+		abnormalExit = true;
+	}
+	if (m_terminationRequest)
+	{
+		abnormalExit = false;
+		m_terminationRequest = false;
+	}
+
+	// Restart the lock process. This call blocks till
+	// the lock process has restarted.
+	restartLockProcess();
+
+	if (abnormalExit)
+	{
+		// Possible hacking attempt detected, try to relaunch the saver with force lock
+		m_state = Waiting;
+		if (!activateSaverOrLock(ForceLock))
+		{
+			TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(terminateTDESession()));
+		}
+	}
+	m_lockProcessRestarting = false;
 }
 
-void SaverEngineThreadHelperObject::terminateThread() {
-	TQEventLoop* eventLoop = TQApplication::eventLoop();
-	if (eventLoop) {
+/*
+ * Start or restart the lock process.
+ * On the very first invocation, launch the SAK process if required and
+ * auto lock the screen if the option has been enabled in the configuration.
+ */
+bool SaverEngineEventHandler::restartLockProcess()
+{
+	static bool firstStart = true;
+
+	bool autoLoginEnable = false;
+	bool autoLoginLocked = false;
+	if (firstStart)
+	{
+		firstStart = false;
+
+		// Create SAK process only if SAK is enabled
+		struct stat st;
+		KSimpleConfig *config;
+		if (stat(KDE_CONFDIR "/tdm/tdmdistrc" , &st) == 0)
+		{
+			config = new KSimpleConfig(TQString::fromLatin1(KDE_CONFDIR "/tdm/tdmdistrc"));
+		}
+		else
+		{
+			config = new KSimpleConfig(TQString::fromLatin1(KDE_CONFDIR "/tdm/tdmrc"));
+		}
+		config->setGroup("X-:*-Greeter");
+		bool useSAKProcess = false;
+#ifdef BUILD_TSAK
+		useSAKProcess = config->readBoolEntry("UseSAK", false) && KDesktopSettings::useTDESAK();
+#endif
+		if (useSAKProcess)
+		{
+			startSAKProcess();
+		}
+
+		// autolock the desktop if required
+		config->setGroup("X-:0-Core");
+		autoLoginEnable = config->readBoolEntry("AutoLoginEnable", false);
+		autoLoginLocked = config->readBoolEntry("AutoLoginLocked", false);
+		delete config;
+	}
+
+	// (Re)start the lock process
+	if (!m_lockProcess.isRunning())
+	{
+		m_lockProcess.clearArguments();
+		TQString path = TDEStandardDirs::findExe("kdesktop_lock");
+		if (path.isEmpty())
+		{
+			kdDebug(1204) << "Can't find kdesktop_lock!" << endl;
+			return false;
+		}
+		m_lockProcess << path;
+		m_lockProcess << TQString("--internal") << TQString("%1").arg(getpid());
+
+		m_saverProcessReady = false;
+		if (!m_lockProcess.start())
+		{
+			kdDebug(1204) << "Failed to start kdesktop_lock!" << endl;
+			return false;
+		}
+		// Wait for the lock process to signal that it is ready
+		sigset_t empty_mask;
+		sigemptyset(&empty_mask);
+		while (!m_saverProcessReady)
+		{
+			sigsuspend(&empty_mask);
+		}
+		if (!m_lockProcess.isRunning())
+		{
+			kdDebug(1204) << "Failed to initialize kdesktop_lock (unexpected termination)!" << endl;
+			return false;
+		}
+	}
+
+	if (autoLoginEnable && autoLoginLocked)
+	{
+		m_lockProcess.kill(SIGTTOU);
+		m_lockProcess.kill(SIGUSR1);
+	}
+	return true;
+}
+
+// Start the screen saver or lock screen
+bool SaverEngineEventHandler::activateSaverOrLock(LockType lock_type)
+{
+	if (m_state == Saving)
+	{
+		return true;
+	}
+
+	kdDebug(1204) << "SaverEngineEventHandler: starting saver" << endl;
+	m_state = Preparing;
+	if (m_SAKProcess)
+	{
+		m_SAKProcess->kill(SIGTERM);
+	}
+
+	m_saverEngine->enableExports();
+	if (!restartLockProcess())
+	{
+		m_state = Waiting;
+		return false;
+	}
+
+	switch (lock_type)
+	{
+		case ForceLock:
+			m_lockProcess.kill(SIGUSR1);        // Request forcelock
+			break;
+		case DontLock:
+			m_lockProcess.kill(SIGUSR2);        // Request dontlock
+			break;
+		case SecureDialog:
+			m_lockProcess.kill(SIGWINCH);       // Request secure dialog
+			break;
+		default:
+			break;
+	}
+
+	if (m_saverEngine->mBlankOnly)
+	{
+		m_lockProcess.kill(SIGTTIN);          // Request blanking
+	}
+
+	int ret = m_lockProcess.kill(SIGTTOU);  // Start lock
+	if (!ret)
+	{
+		m_state = Waiting;
+		return false;
+	}
+	m_state = Engaging;
+
+	// Ask to the GUI thread to activate X11 saver
+	TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(activateSaverOrLockGUI()));
+
+	return true;
+}
+
+// Stop the screen saver.
+void SaverEngineEventHandler::stopLockProcess()
+{
+	if (m_state == Waiting)
+	{
+		return;
+	}
+
+	kdDebug(1204) << "SaverEngineEventHandler: stopping lock process" << endl;
+
+	m_terminationRequest = true;
+	m_lockProcess.kill();
+	m_state = Waiting;
+
+	// Ask to the GUI thread to stop the X11 saver
+	TQTimer::singleShot(0, m_saverEngine, TQ_SLOT(stopLockProcessGUI()));
+}
+
+void SaverEngineEventHandler::startSAKProcess()
+{
+	if (!m_SAKProcess)
+	{
+		m_SAKProcess = new TDEProcess;
+		*m_SAKProcess << "tdmtsak";
+		connect(m_SAKProcess, TQ_SIGNAL(processExited(TDEProcess*)), this, TQ_SLOT(slotSAKProcessExited()));
+	}
+	if (!m_SAKProcess->isRunning())
+	{
+		m_SAKProcess->start();
+	}
+}
+
+void SaverEngineEventHandler::slotSAKProcessExited()
+{
+	if (!m_SAKProcess)
+	{
+		tqWarning("[kdesktop] SAK process does not exist. Something went wrong. Ignoring.");
+		return;
+	}
+
+	int retcode = m_SAKProcess->exitStatus();
+	if (retcode && m_SAKProcess->normalExit())
+	{
+		trinity_lockeng_sak_available = false;
+		tqWarning("[kdesktop] SAK driven secure dialog is not available for use (retcode %d). "
+		          "Check tdmtsak for proper functionality.", retcode);
+	}
+
+	if (m_state == Preparing)
+	{
+		return;
+	}
+
+	if (m_SAKProcess->normalExit() && trinity_lockeng_sak_available)
+	{
+		if (m_state == Waiting)
+		{
+			activateSaverOrLock(SecureDialog);
+		}
+		else
+		{
+			m_lockProcess.kill(SIGHUP);
+		}
+	}
+}
+
+void SaverEngineEventHandler::terminateThread()
+{
+	TQEventLoop *eventLoop = TQApplication::eventLoop();
+	if (eventLoop)
+	{
 		eventLoop->exit(0);
 	}
 }
diff --git a/kdesktop/lockeng.h b/kdesktop/lockeng.h
index d9285abc1..94d8f9e44 100644
--- a/kdesktop/lockeng.h
+++ b/kdesktop/lockeng.h
@@ -1,6 +1,5 @@
-//===========================================================================
 //
-// This file is part of the KDE project
+// This file is part of the TDE project
 //
 // Copyright (c) 1999 Martin R. Jones <mjones@kde.org>
 //
@@ -8,7 +7,6 @@
 #ifndef __LOCKENG_H__
 #define __LOCKENG_H__
 
-#include <tqwidget.h>
 #include <tqthread.h>
 #include <tdeprocess.h>
 #include <tqvaluevector.h>
@@ -23,31 +21,48 @@ class TDECryptographicCardDevice;
 #else
 #define TDECryptographicCardDevice void
 #endif
+
+/**
+ * Screen saver engine. Handles communication with the lock process.
+ * The engine is split into two parts, the 'SaverEngine' running in the GUI thread and
+ * the 'SaverEngineEventHandler' running in a separate thread and eventloop.
+ * The 'SaverEngine' handles communication with X11, DCOP and DBUS while the
+ * 'SaverEngineEventHandler' handles communication with the actual lock process.
+ * Several actions require cooperation of the two parts, so in various methods
+ * there will be inter-thread calls (using timers or by emitting signals) to
+ * trigger the other side remaining logic.
+ * This complex design is necessary to avoid blocking the main GUI application event loop,
+ * which has several tasks to manage and therefore can't affort to wait in a suspended state.
+ * This was previously leading to deadlock when DCOP calls where executed on the secondary
+ * thread/eventloop, for example when changing desktop while the lock process was restarting.
+ */
+
 class DCOPClientTransaction;
 class TQT_DBusMessage;
 class TQT_DBusProxy;
+class SaverEngineEventHandler;
 
-class SaverEngineThreadHelperObject : public TQObject
+// Type of lock screen
+enum LockType : int
 {
-	TQ_OBJECT
-	
-public slots:
-	void terminateThread();
-	void slotLockProcessWaiting();
-	void slotLockProcessFullyActivated();
+	DontLock = 0,
+	DefaultLock,
+	ForceLock,
+	SecureDialog
+};
 
-signals:
-	void lockProcessWaiting();
-	void lockProcessFullyActivated();
+enum SaverState
+{
+	Waiting,
+	Preparing,
+	Engaging,
+	Saving
 };
 
-//===========================================================================
-/**
- * Screen saver engine.  Handles screensaver window, starting screensaver
- * hacks, and password entry.
- */
-class SaverEngine : public TQWidget, public KScreensaverIface
+class SaverEngine : public TQObject, public KScreensaverIface
 {
+	friend class SaverEngineEventHandler;
+
 	TQ_OBJECT
 public:
 	SaverEngine();
@@ -100,80 +115,53 @@ public:
 	 */
 	virtual void saverLockReady();
 
-	/**
-	 * @internal
-	 */
 	void lockScreen(bool DCOP = false);
 
-	/**
-	 * Called by KDesktop to wait for saver engage
-	 * @internal
-	 */
-	bool waitForLockEngage();
-
-	/**
-	 * @internal
-	 */
 	void lockScreenAndDoNewSession();
-
-	/**
-	 * @internal
-	 */
 	void lockScreenAndSwitchSession(int vt);
 
+	void enableExports(); // Enable wallpaper exports
+
 signals:
-	void terminateHelperThread();
-	void asyncLock();
+	void activateSaverOrLockSignal(LockType lock_type);
+	void lockScreenSignal(bool);
+	void terminateEventHandlerThread();
 
 public slots:
-	void slotLockProcessReady();
-	void lockProcessWaiting();
-	void lockProcessFullyActivated();
 	void handleDBusSignal(const TQT_DBusMessage&);
+	void terminateTDESession();
 
 protected slots:
 	void idleTimeout();
-	void lockProcessExited();
 
 private slots:
-	void handleSecureDialog();
-	void slotSAKProcessExited();
-
 	void cryptographicCardInserted(TDECryptographicCardDevice*);
 	void cryptographicCardRemoved(TDECryptographicCardDevice*);
-
-	/**
-	 * Enable wallpaper exports
-	 */
-	void enableExports();
-	void recoverFromHackingAttempt();
 	void cardStartupTimeout();
-
 	bool dBusReconnect();
 
+	// The following slots are invoked by corresponding methods named without the 'GUI' suffix
+	// in 'SaverEngineEventHandler' to complete the remaining X11 part of the actions
+	void activateSaverOrLockGUI();
+	void lockProcessFullyActivatedGUI();
+	void lockProcessWaitingGUI();
+	void lockScreenGUI();
+	void stopLockProcessGUI();
+
 private:
-	bool restartDesktopLockProcess();
 	void dBusClose();
 	bool dBusConnect();
 	void onDBusServiceRegistered(const TQString&);
 	void onDBusServiceUnregistered(const TQString&);
 
 protected:
-	enum SaverState { Waiting, Preparing, Engaging, Saving };
-	enum LockType { DontLock, DefaultLock, ForceLock, SecureDialog };
-	bool startLockProcess( LockType lock_type );
-	bool waitForLockProcessStart();
-	void stopLockProcess();
-	bool handleKeyPress(XKeyEvent *xke);
 	void processLockTransactions();
 	xautolock_corner_t applyManualSettings(int);
 
 protected:
 	bool mEnabled;
 
-	SaverState mState;
 	XAutoLock *mXAutoLock;
-	TDEProcess mLockProcess;
 	int mTimeout;
 
 	// the original X screensaver parameters
@@ -182,26 +170,60 @@ protected:
 	int mXBlanking;
 	int mXExposures;
 
-	bool mBlankOnly;  // only use the blanker, not the defined saver
 	TQValueVector< DCOPClientTransaction* > mLockTransactions;
 
 public:
-	SaverEngineThreadHelperObject* m_threadHelperObject;
+	bool mBlankOnly;  // only use the blanker, not the defined saver // protected
+	SaverEngineEventHandler *m_saverEngineEventHandler;
 
 private:
-	TQEventLoopThread* m_helperThread;
-	sigset_t mThreadBlockSet;
-	TDEProcess* mSAKProcess;
-	bool mTerminationRequested;
-	bool mSaverProcessReady;
+	TQEventLoopThread* m_eventHandlerThread;
 	bool mNewVTAfterLockEngage;
 	bool mValidCryptoCardInserted;
 	int mSwitchVTAfterLockEngage;
 	struct sigaction mSignalAction;
 	TQT_DBusConnection dBusConn;
-	TQT_DBusProxy* dBusLocal;
-	TQT_DBusProxy* dBusWatch;
-	TQT_DBusProxy* systemdSession;
+	TQT_DBusProxy *dBusLocal;
+	TQT_DBusProxy *dBusWatch;
+	TQT_DBusProxy *systemdSession;
+};
+
+class SaverEngineEventHandler : public TQObject
+{
+	TQ_OBJECT
+	
+public:
+	SaverEngineEventHandler(SaverEngine *engine);
+
+	SaverState getState() const { return m_state; }
+
+	void lockProcessExited();
+	void lockProcessFullyActivated();
+	void lockProcessReady();
+	void terminateLockProcess();
+
+public slots:
+	bool activateSaverOrLock(LockType lock_type);
+	void lockScreen(bool DCOP = false);
+	bool restartLockProcess();
+	void saveScreen();
+	void stopLockProcess();
+	void terminateThread();
+
+protected slots:
+	void slotLockProcessExited();
+	void slotSAKProcessExited();
+
+protected:
+	void startSAKProcess();
+
+	bool m_saverProcessReady;
+	bool m_lockProcessRestarting;
+	bool m_terminationRequest;
+	SaverState m_state;
+	SaverEngine *m_saverEngine;
+	TDEProcess *m_SAKProcess;
+	TDEProcess m_lockProcess;
 };
 
 #endif
diff --git a/kdesktop/main.cpp b/kdesktop/main.cpp
index f3e61e2f2..0465068b6 100644
--- a/kdesktop/main.cpp
+++ b/kdesktop/main.cpp
@@ -71,7 +71,7 @@ static TDECmdLineOptions options[] =
 };
 
 bool argb_visual = false;
-KDesktopApp *myApp = NULL;
+KDesktopApp *myApp = nullptr;
 
 // -----------------------------------------------------------------------------
 
@@ -251,7 +251,7 @@ extern "C" TDE_EXPORT int kdemain( int argc, char **argv )
         else
             XCloseDisplay( dpy );
     }
-    if( myApp == NULL )
+    if (!myApp)
         myApp = new KDesktopApp;
 #else
     myApp = new KDesktopApp;
@@ -260,9 +260,6 @@ extern "C" TDE_EXPORT int kdemain( int argc, char **argv )
 
     KDesktopSettings::instance(kdesktop_name + "rc");
 
-    bool x_root_hack = args->isSet("x-root");
-    bool wait_for_kded = args->isSet("waitforkded");
-
     // This MUST be created before any widgets are created
     SaverEngine saver;
 
@@ -279,16 +276,15 @@ extern "C" TDE_EXPORT int kdemain( int argc, char **argv )
        myApp->config()->reparseConfiguration();
     }
 
-    // for the KDE-already-running check in starttde
-    TDESelectionOwner kde_running( "_KDE_RUNNING", 0 );
-    kde_running.claim( false );
+    // for the TDE-already-running check in starttde
+    TDESelectionOwner tde_running( "_KDE_RUNNING", 0 );
+    tde_running.claim( false );
 
+    bool x_root_hack = args->isSet("x-root");
+    bool wait_for_kded = args->isSet("waitforkded");
     KDesktop desktop( &saver, x_root_hack, wait_for_kded );
 
     args->clear();
-
     myApp->dcopClient()->setDefaultObject( "KDesktopIface" );
-
-	
     return myApp->exec();
 }
-- 
cgit v1.2.3

