File fix-screensaver-dpms.diff of Package kdebase4-workspace

Subject: Fix screensaving and DPMS activation
From: Lubos Lunak
Bug: bnc#462854
Patch-upstream: r916961, r916963, r916966

See comments below or commit log messages for details. This patch also
includes syncing of the screensaving code with 4.2, as I find it safer
to sync with the code I know that works rather than trying to backport.

diff -u -p -N -r krunner/saverengine.cpp krunner/saverengine.cpp
--- krunner/saverengine.cpp	2009-01-27 16:47:51.000000000 +0100
+++ krunner/saverengine.cpp	2009-01-27 16:48:02.000000000 +0100
@@ -46,7 +46,11 @@ SaverEngine::SaverEngine()
     // Save X screensaver parameters
     XGetScreenSaver(QX11Info::display(), &mXTimeout, &mXInterval,
                     &mXBlanking, &mXExposures);
-    // ... and disable it
+    // And disable it. The internal X screensaver is not used at all, but we use its
+    // internal idle timer (and it is also used by DPMS support in X). This timer must not
+    // be altered by this code, since e.g. resetting the counter after activating our
+    // screensaver would prevent DPMS from activating. We use the timer merely to detect
+    // user activity.
     XSetScreenSaver(QX11Info::display(), 0, mXInterval, mXBlanking, mXExposures);
 
     mState = Waiting;
@@ -87,18 +91,9 @@ SaverEngine::~SaverEngine()
 
 void SaverEngine::Lock()
 {
-    bool ok = true;
     if (mState == Waiting)
     {
-        ok = startLockProcess( ForceLock );
-// It takes a while for krunner_lock to start and lock the screen.
-// Therefore delay the DBus call until it tells krunner that the locking is in effect.
-// This is done only for --forcelock .
-        if( ok && calledFromDBus())
-        {
-            mLockTransactions.append(message().createReply());
-            setDelayedReply(true);
-        }
+        startLockProcess( ForceLock );
     }
     else
     {
@@ -133,6 +128,7 @@ void SaverEngine::saverLockReady()
 
 void SaverEngine::SimulateUserActivity()
 {
+    XForceScreenSaver( QX11Info::display(), ScreenSaverReset );
     if ( mXAutoLock && mState == Waiting )
     {
         mXAutoLock->resetTrigger();
@@ -274,6 +282,14 @@ bool SaverEngine::startLockProcess( Lock
     emit ActiveChanged(true); // DBus signal
     mState = Preparing;
 
+    // It takes a while for krunner_lock to start and lock the screen.
+    // Therefore delay the DBus call until it tells krunner that the locking is in effect.
+    // This is done only for --forcelock .
+    if (lock_type == ForceLock && calledFromDBus()) {
+        mLockTransactions.append(message().createReply());
+        setDelayedReply(true);
+    }
+
     return true;
 }
 
@@ -311,6 +327,8 @@ void SaverEngine::lockProcessExited()
 //
 void SaverEngine::idleTimeout()
 {
+    if( mState != Waiting )
+        return; // already saving
     startLockProcess( DefaultLock );
 }
 
diff -u -p -N -r krunner/xautolock_c.h krunner/xautolock_c.h
--- krunner/xautolock_c.h	2009-01-27 16:47:51.000000000 +0100
+++ krunner/xautolock_c.h	2009-01-27 16:48:02.000000000 +0100
@@ -21,12 +21,7 @@
 #ifndef __xautolock_c_h
 #define __xautolock_c_h
 
-#include <config-xautolock.h>
-
 #include <X11/Xlib.h>
-#ifdef HAVE_XSCREENSAVER
-# include <X11/extensions/scrnsaver.h>
-#endif
 #ifdef __cplusplus
 # include <fixx11h.h>
 #endif
@@ -55,19 +50,12 @@ extern "C"
 {
 #endif
 void xautolock_processEvent( XEvent* ev );
-void xautolock_queryIdleTime( Display* d);
 void xautolock_processQueue( void );
 void xautolock_queryPointer (Display* d);
 void xautolock_initDiy (Display* d);
 void xautolock_resetTriggers( void );
 void xautolock_setTrigger( int );
 int xautolock_ignoreWindow( Window );
-#ifdef HAVE_XSCREENSAVER
-extern int xautolock_useMit;
-extern unsigned long xautolock_lastIdleTime;
-#else
-# define xautolock_useMit 0
-#endif
 extern xautolock_corner_t xautolock_corners[ 4 ];
 #ifdef __cplusplus
 }
diff -u -p -N -r krunner/xautolock.cpp krunner/xautolock.cpp
--- krunner/xautolock.cpp	2009-01-27 16:47:51.000000000 +0100
+++ krunner/xautolock.cpp	2009-01-27 16:48:02.000000000 +0100
@@ -38,10 +38,6 @@ Status DPMSInfo ( Display *, CARD16 *, B
 
 #include <ctime>
 
-#ifdef HAVE_XSCREENSAVER
-int xautolock_useMit;
-unsigned long xautolock_lastIdleTime;
-#endif
 xautolock_corner_t xautolock_corners[ 4 ];
 
 static XAutoLock* self = NULL;
@@ -62,10 +58,14 @@ XAutoLock::XAutoLock()
 {
     self = this;
 #ifdef HAVE_XSCREENSAVER
+    mMitInfo = 0;
     int dummy;
-    xautolock_useMit = XScreenSaverQueryExtension( QX11Info::display(), &dummy, &dummy );
+    if (XScreenSaverQueryExtension( QX11Info::display(), &dummy, &dummy ))
+    {
+        mMitInfo = XScreenSaverAllocInfo();
+    }
+    else
 #endif
-    if( !xautolock_useMit )
     {
         kapp->installX11EventFilter( this );
         int (*oldHandler)(Display *, XErrorEvent *);
@@ -83,8 +83,10 @@ XAutoLock::XAutoLock()
     mActive = false;
 
     mTimerId = startTimer( CHECK_INTERVAL );
+    // This is an internal clock timer (in seconds), used instead of querying system time.
+    // It is incremented manually, preventing from problems with clock jumps.
+    // In other words, this is the 'now' time and the reference point for other times here.
     mElapsed = 0;
-
 }
 
 //---------------------------------------------------------------------------
@@ -126,8 +128,6 @@ void XAutoLock::start()
 {
     mActive = true;
     resetTrigger();
-    XSetScreenSaver(QX11Info::display(), mTimeout + 10, 100, PreferBlanking, DontAllowExposures); // We'll handle blanking
-    kDebug() << "XSetScreenSaver" << mTimeout + 10;
 }
 
 //---------------------------------------------------------------------------
@@ -138,8 +138,6 @@ void XAutoLock::stop()
 {
     mActive = false;
     resetTrigger();
-    XSetScreenSaver(QX11Info::display(), 0, 100, PreferBlanking, DontAllowExposures); // No blanking at all
-    kDebug() << "XSetScreenSaver 0";
 }
 
 //---------------------------------------------------------------------------
@@ -148,12 +146,15 @@ void XAutoLock::stop()
 //
 void XAutoLock::resetTrigger()
 {
+    // Time of the last user activity (used only when the internal XScreensaver
+    // idle counter is not available).
     mLastReset = mElapsed;
+    // Time when screensaver should be activated.
     mTrigger = mElapsed + mTimeout;
 #ifdef HAVE_XSCREENSAVER
-    xautolock_lastIdleTime = 0;
+    mLastIdle = 0;
 #endif
-    XForceScreenSaver( QX11Info::display(), ScreenSaverReset );
+    // Do not reset the internal X screensaver here (no XForceScreenSaver())
 }
 
 //---------------------------------------------------------------------------
@@ -188,57 +189,79 @@ void XAutoLock::timerEvent(QTimerEvent *
     }
     mElapsed += CHECK_INTERVAL / 1000;
 
-    int (*oldHandler)(Display *, XErrorEvent *) = NULL;
-    if( !xautolock_useMit )
+#ifdef HAVE_XSCREENSAVER
+    if (!mMitInfo)
+#endif
     { // only the diy way needs special X handler
         XSync( QX11Info::display(), False );
-        oldHandler = XSetErrorHandler(catchFalseAlarms);
+        int (*oldHandler)(Display *, XErrorEvent *) =
+                XSetErrorHandler(catchFalseAlarms);
+
+        xautolock_processQueue();
+
+        XSetErrorHandler(oldHandler);
     }
 
-    xautolock_processQueue();
+#ifdef HAVE_XSCREENSAVER
+    if (mMitInfo)
+    {
+        Display *d = QX11Info::display();
+        // Check user idle time. If it is smaller than before, it is either
+        // clock jump or user activity, so reset the trigger time. Checking whether
+        // there is user inactivity timeout is done below using mTrigger and mElapsed.
+        XScreenSaverQueryInfo(d, DefaultRootWindow(d), mMitInfo);
+        if (mLastIdle < mMitInfo->idle)
+            mLastIdle = mMitInfo->idle;
+        else
+            resetTrigger();
+    }
+#endif /* HAVE_XSCREENSAVER */
 
-    xautolock_queryIdleTime( QX11Info::display());
+    // This needs to be after the above check, so it overrides it.
     xautolock_queryPointer( QX11Info::display());
 
-    if( !xautolock_useMit )
-        XSetErrorHandler(oldHandler);
-
     bool activate = false;
 
-    // kDebug() << now << mTrigger;
+    // This is the test whether to activate screensaver. If we have reached the time
+    // and for the whole timeout period there was no activity (which would change mTrigger
+    // again), activate.
     if (mElapsed >= mTrigger)
-    {
-        resetTrigger();
         activate = true;
-    }
 
 #ifdef HAVE_DPMS
     BOOL on;
     CARD16 state;
+    CARD16 timeout1, timeout2, timeout3;
     DPMSInfo( QX11Info::display(), &state, &on );
+    DPMSGetTimeouts( QX11Info::display(), &timeout1, &timeout2, &timeout3 );
 
     // kDebug() << "DPMSInfo " << state << on;
     // If DPMS is active, it makes XScreenSaverQueryInfo() report idle time
     // that is always smaller than DPMS timeout (X bug I guess). So if DPMS
     // saving is active, simply always activate our saving too, otherwise
     // this could prevent locking from working.
+    // X.Org 7.4: With this version activating DPMS resets the screensaver idle timer,
+    // so keep this. It probably makes sense to always do this anyway.
     if(state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff)
         activate = true;
-    if(!on && mDPMS) {
+    // If we are DPMS-dependent and either DPMS is turned off completely or all
+    // three DPMS modes are turned off, don't activate (apps use this to turn off
+    // screensavers).
+    if(mDPMS && (!on || (timeout1 == 0 && timeout2 == 0 && timeout3 == 0 ))) {
         activate = false;
         resetTrigger();
     }
 #endif
 
-#ifdef HAVE_XSCREENSAVER
-    static XScreenSaverInfo* mitInfo = 0;
-    if (!mitInfo) mitInfo = XScreenSaverAllocInfo ();
-    if (XScreenSaverQueryInfo (QX11Info::display(), QX11Info::appRootWindow(), mitInfo)) {
-        // kDebug() << "XScreenSaverQueryInfo " << mitInfo->state << ScreenSaverDisabled;
-        if (mitInfo->state == ScreenSaverDisabled)
-            activate = false;
-    }
-#endif
+    // Do not check whether internal X screensaver is enabled or disabled, since we
+    // have disabled it ourselves. Some apps might try to disable it too to prevent
+    // screensavers, but then our logic breaks[*]. Those apps need to disable DPMS anyway,
+    // or they will still have problems, so the DPMS code above should be enough.
+    // Besides, I doubt other screensaver implementations check this either.
+    // [*] We can't run with X screensaver enabled, since then sooner or later
+    // the internal screensaver will activate instead of our screensaver and we cannot
+    // prevent its activation by resetting the idle counter since that would also
+    // reset DPMS saving.
 
     if(mActive && activate)
         emit timeout();
@@ -249,7 +272,9 @@ bool XAutoLock::x11Event( XEvent* ev )
     xautolock_processEvent( ev );
 // don't futher process key events that were received only because XAutoLock wants them
     if( ev->type == KeyPress && !ev->xkey.send_event
-        && !xautolock_useMit
+#ifdef HAVE_XSCREENSAVER
+        && !mMitInfo
+#endif
         && !QWidget::find( ev->xkey.window ))
         return true;
     return false;
@@ -265,10 +290,8 @@ bool XAutoLock::ignoreWindow( WId w )
 time_t XAutoLock::idleTime()
 {
 #ifdef HAVE_XSCREENSAVER
-    static XScreenSaverInfo* mitInfo = 0;
-    if (!mitInfo) mitInfo = XScreenSaverAllocInfo ();
-    if (XScreenSaverQueryInfo (QX11Info::display(), QX11Info::appRootWindow(), mitInfo))
-        return mitInfo->idle / 1000;
+    if (mMitInfo)
+        return mMitInfo->idle / 1000;
 #endif
     return mElapsed - mLastReset;
 }
diff -u -p -N -r krunner/xautolock_engine.c krunner/xautolock_engine.c
--- krunner/xautolock_engine.c	2009-01-27 16:47:51.000000000 +0100
+++ krunner/xautolock_engine.c	2009-01-27 16:48:02.000000000 +0100
@@ -20,30 +20,6 @@
 #include "xautolock_c.h"
 
 /*
- *  Function for querying the idle time from the server.
- *  Only used if the Xscreensaver
- *  extension is present.
- */
-void 
-xautolock_queryIdleTime (Display* d)
-{
-#ifdef HAVE_XSCREENSAVER
-  if( xautolock_useMit )
-  {
-    static XScreenSaverInfo* mitInfo = 0; 
-    if (!mitInfo) mitInfo = XScreenSaverAllocInfo ();
-    XScreenSaverQueryInfo (d, DefaultRootWindow (d), mitInfo);
-    if (xautolock_lastIdleTime < mitInfo->idle)
-        xautolock_lastIdleTime = mitInfo->idle;
-    else
-        xautolock_resetTriggers ();
-  }
-#else
-  (void)d;
-#endif /* HAVE_XSCREENSAVER */
-}
-
-/*
  *  Function for monitoring pointer movements. This implements the 
  *  `corners' feature and as a side effect also tracks pointer 
  *  related user activity. The latter actually is only needed when
diff -u -p -N -r krunner/xautolock.h krunner/xautolock.h
--- krunner/xautolock.h	2009-01-27 16:47:51.000000000 +0100
+++ krunner/xautolock.h	2009-01-27 16:48:01.000000000 +0100
@@ -8,10 +8,16 @@
 #ifndef __XAUTOLOCK_H__
 #define __XAUTOLOCK_H__
 
+#include <config-xautolock.h>
+
 #include <QWidget>
 
 #include <X11/Xlib.h>
+#ifdef HAVE_XSCREENSAVER
+# include <X11/extensions/scrnsaver.h>
+#endif
 #include <fixx11h.h>
+
 //===========================================================================
 //
 // Detect user inactivity.
@@ -76,6 +82,10 @@ protected:
     time_t  mLastReset;
     time_t  mElapsed;
     bool    mDPMS;
+#ifdef HAVE_XSCREENSAVER
+    XScreenSaverInfo *mMitInfo;
+    ulong   mLastIdle;
+#endif
 };
 
 #endif
Index: krunner/lock/lockprocess.cc
===================================================================
--- krunner/lock/lockprocess.cc	(revision 916960)
+++ krunner/lock/lockprocess.cc	(revision 916966)
@@ -1103,7 +1103,6 @@
         return; // no resuming with dialog visible or when not visible
     if( mSuspended && mHackProc.state() == QProcess::Running )
     {
-        XForceScreenSaver(QX11Info::display(), ScreenSaverReset );
         QPainter p( this );
         p.drawPixmap( 0, 0, mSavedScreen );
         p.end();
openSUSE Build Service is sponsored by