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();