1 //===========================================================================
3 // This file is part of the KDE project
5 // Copyright (c) 1999 Martin R. Jones <mjones@kde.org>
6 // Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org>
9 //krunner keeps running and checks user inactivity
10 //when it should show screensaver (and maybe lock the session),
11 //it starts krunner_lock, who does all the locking and who
12 //actually starts the screensaver
14 //It's done this way to prevent screen unlocking when krunner
17 #include <config-workspace.h>
18 #include <config-X11.h>
19 #include <config-krunner-lock.h>
20 #include "lockprocess.h"
22 #include "autologout.h"
23 #include "kscreensaversettings.h"
27 #include <kstandarddirs.h>
28 #include <kapplication.h>
29 #include <kservicegroup.h>
31 #include <kmessagebox.h>
32 #include <kglobalsettings.h>
34 #include <klibloader.h>
35 #include <kpushbutton.h>
36 #include <KStandardGuiItem>
37 #include <kauthorized.h>
38 #include <kdesktopfile.h>
39 #include <kservicetypetrader.h>
41 #include <QtGui/QFrame>
47 #include <QSocketNotifier>
48 #include <QDesktopWidget>
50 #include <QTextStream>
57 #ifdef HAVE_SETPRIORITY
59 #include <sys/resource.h>
63 #include <X11/Xutil.h>
64 #include <X11/keysym.h>
65 #include <X11/Xatom.h>
73 #include <X11/extensions/dpms.h>
75 #ifndef HAVE_DPMSINFO_PROTO
76 Status
DPMSInfo ( Display
*, CARD16
*, BOOL
* );
82 #include <X11/extensions/xf86misc.h>
85 #ifdef HAVE_GLXCHOOSEVISUAL
89 #define LOCK_GRACE_DEFAULT 5000
90 #define AUTOLOGOUT_DEFAULT 600
92 static Window gVRoot
= 0;
93 static Window gVRootData
= 0;
94 static Atom gXA_VROOT
;
95 static Atom gXA_SCREENSAVER_VERSION
;
97 //===========================================================================
99 // Screen saver handling process. Handles screensaver window,
100 // starting screensaver hacks, and password entry.f
102 LockProcess::LockProcess(bool child
, bool useBlankOnly
)
103 : QWidget(0L, Qt::X11BypassWindowManagerHint
),
107 mUseBlankOnly(useBlankOnly
),
110 mRestoreXF86Lock(false),
114 setObjectName("save window");
117 kapp
->installX11EventFilter(this);
119 // Get root window size
120 XWindowAttributes rootAttr
;
122 XGetWindowAttributes(QX11Info::display(), RootWindow(QX11Info::display(),
123 info
.screen()), &rootAttr
);
124 mRootWidth
= rootAttr
.width
;
125 mRootHeight
= rootAttr
.height
;
126 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(),
127 SubstructureNotifyMask
| rootAttr
.your_event_mask
);
129 // virtual root property
130 gXA_VROOT
= XInternAtom (QX11Info::display(), "__SWM_VROOT", False
);
131 gXA_SCREENSAVER_VERSION
= XInternAtom (QX11Info::display(), "_SCREENSAVER_VERSION", False
);
133 connect(&mHackProc
, SIGNAL(processExited(K3Process
*)),
134 SLOT(hackExited(K3Process
*)));
136 mSuspendTimer
.setSingleShot(true);
137 connect(&mSuspendTimer
, SIGNAL(timeout()), SLOT(suspend()));
140 QString::fromLatin1( ::getenv( "XDM_MANAGED" )).split(QChar(','), QString::SkipEmptyParts
);
141 for (QStringList::ConstIterator it
= dmopt
.begin(); it
!= dmopt
.end(); ++it
)
142 if ((*it
).startsWith("method="))
143 mMethod
= (*it
).mid(7);
151 DPMSInfo(QX11Info::display(), &state
, &on
);
154 connect(&mCheckDPMS
, SIGNAL(timeout()), SLOT(checkDPMSActive()));
155 // we can save CPU if we stop it as quickly as possible
156 // but we waste CPU if we check too often -> so take 10s
157 mCheckDPMS
.start(10000);
162 greetPlugin
.library
= 0;
165 //---------------------------------------------------------------------------
167 // Destructor - usual cleanups.
169 LockProcess::~LockProcess()
171 if (greetPlugin
.library
) {
172 if (greetPlugin
.info
->done
)
173 greetPlugin
.info
->done();
174 greetPlugin
.library
->unload();
178 static int signal_pipe
[2];
180 static void sigterm_handler(int)
183 ::write( signal_pipe
[1], &tmp
, 1);
186 static void sighup_handler(int)
189 ::write( signal_pipe
[1], &tmp
, 1);
192 void LockProcess::timerEvent(QTimerEvent
*ev
)
194 if (ev
->timerId() == mAutoLogoutTimerId
)
196 killTimer(mAutoLogoutTimerId
);
197 AutoLogout
autologout(this);
198 execDialog(&autologout
);
202 void LockProcess::setupSignals()
204 struct sigaction act
;
206 act
.sa_handler
=SIG_IGN
;
207 sigemptyset(&(act
.sa_mask
));
208 sigaddset(&(act
.sa_mask
), SIGINT
);
210 sigaction(SIGINT
, &act
, 0L);
212 act
.sa_handler
=SIG_IGN
;
213 sigemptyset(&(act
.sa_mask
));
214 sigaddset(&(act
.sa_mask
), SIGQUIT
);
216 sigaction(SIGQUIT
, &act
, 0L);
217 // exit cleanly on SIGTERM
218 act
.sa_handler
= sigterm_handler
;
219 sigemptyset(&(act
.sa_mask
));
220 sigaddset(&(act
.sa_mask
), SIGTERM
);
222 sigaction(SIGTERM
, &act
, 0L);
223 // SIGHUP forces lock
224 act
.sa_handler
= sighup_handler
;
225 sigemptyset(&(act
.sa_mask
));
226 sigaddset(&(act
.sa_mask
), SIGHUP
);
228 sigaction(SIGHUP
, &act
, 0L);
231 QSocketNotifier
* notif
= new QSocketNotifier(signal_pipe
[0], QSocketNotifier::Read
, this);
232 connect( notif
, SIGNAL(activated(int)), SLOT(signalPipeSignal()));
236 void LockProcess::signalPipeSignal()
239 ::read( signal_pipe
[0], &tmp
, 1);
242 else if( tmp
== 'H' ) {
248 //---------------------------------------------------------------------------
249 bool LockProcess::lock()
252 // In case of a forced lock we don't react to events during
253 // the dead-time to give the screensaver some time to activate.
254 // That way we don't accidentally show the password dialog before
255 // the screensaver kicks in because the user moved the mouse after
256 // selecting "lock screen", that looks really untidy.
260 QTimer::singleShot(1000, this, SLOT(slotDeadTimePassed()));
268 //---------------------------------------------------------------------------
269 void LockProcess::slotDeadTimePassed()
274 //---------------------------------------------------------------------------
275 bool LockProcess::defaultSave()
280 QTimer::singleShot(mLockGrace
, this, SLOT(startLock()));
286 //---------------------------------------------------------------------------
287 bool LockProcess::dontLock()
293 //---------------------------------------------------------------------------
294 void LockProcess::quitSaver()
300 //---------------------------------------------------------------------------
302 // Read and apply configuration.
304 void LockProcess::configure()
306 // the configuration is stored in krunner's config file
307 if( KScreenSaverSettings::lock() ) {
308 mLockGrace
= KScreenSaverSettings::lockGrace();
311 else if (mLockGrace
> 300000)
312 mLockGrace
= 300000; // 5 minutes, keep the value sane
317 if ( KScreenSaverSettings::autoLogout() ) {
319 mAutoLogoutTimeout
= KScreenSaverSettings::autoLogoutTimeout();
320 mAutoLogoutTimerId
= startTimer(mAutoLogoutTimeout
* 1000); // in milliseconds
324 //if the user decided that the screensaver should run independent from
325 //dpms, we shouldn't check for it, aleXXX
326 mDPMSDepend
= KScreenSaverSettings::dpmsDependent();
329 mPriority
= KScreenSaverSettings::priority();
330 if (mPriority
< 0) mPriority
= 0;
331 if (mPriority
> 19) mPriority
= 19;
333 mSaver
= KScreenSaverSettings::saver();
334 if (mSaver
.isEmpty() || mUseBlankOnly
) {
335 mSaver
= "kblank.desktop";
340 mPlugins
= KScreenSaverSettings::pluginsUnlock();
341 if (mPlugins
.isEmpty()) {
342 mPlugins
= QStringList("classic");
344 mPluginOptions
= KScreenSaverSettings::pluginOptions();
347 //---------------------------------------------------------------------------
349 // Read the command line needed to run the screensaver given a .desktop file.
351 void LockProcess::readSaver()
353 if (!mSaver
.isEmpty())
355 QString entryName
= mSaver
;
356 if( entryName
.endsWith( ".desktop" ))
357 entryName
= entryName
.left( entryName
.length() - 8 ); // strip it
358 KService::List offers
= KServiceTypeTrader::self()->query( "ScreenSaver",
359 "DesktopEntryName == '" + entryName
.toLower() + '\'' );
360 if( offers
.count() == 0 )
362 kDebug(1204) << "Cannot find screesaver: " << mSaver
<< endl
;
365 QString file
= KStandardDirs::locate("services", offers
.first()->desktopEntryPath());
367 bool opengl
= KAuthorized::authorizeKAction("opengl_screensavers");
368 bool manipulatescreen
= KAuthorized::authorizeKAction("manipulatescreen_screensavers");
369 KDesktopFile
config( file
);
370 KConfigGroup desktopGroup
= config
.desktopGroup();
371 if (!desktopGroup
.readEntry("X-KDE-Type").toUtf8().isEmpty())
373 QString saverType
= desktopGroup
.readEntry("X-KDE-Type").toUtf8();
374 QStringList saverTypes
= saverType
.split( ";");
375 for (int i
= 0; i
< saverTypes
.count(); i
++)
377 if ((saverTypes
[i
] == "ManipulateScreen") && !manipulatescreen
)
379 kDebug(1204) << "Screensaver is type ManipulateScreen and ManipulateScreen is forbidden" << endl
;
382 if ((saverTypes
[i
] == "OpenGL") && !opengl
)
384 kDebug(1204) << "Screensaver is type OpenGL and OpenGL is forbidden" << endl
;
387 if (saverTypes
[i
] == "OpenGL")
389 mOpenGLVisual
= true;
394 kDebug(1204) << "mForbidden: " << (mForbidden
? "true" : "false") << endl
;
396 if (config
.hasActionGroup("Root"))
398 mSaverExec
= config
.actionGroup("Root").readPathEntry("Exec");
403 //---------------------------------------------------------------------------
405 // Create a window to draw our screen saver on.
407 void LockProcess::createSaverWindow()
409 Visual
* visual
= CopyFromParent
;
410 XSetWindowAttributes attrs
;
412 #ifdef HAVE_GLXCHOOSEVISUAL
415 static int attribs
[][ 15 ] =
417 #define R GLX_RED_SIZE
418 #define G GLX_GREEN_SIZE
419 #define B GLX_BLUE_SIZE
420 { GLX_RGBA
, R
, 8, G
, 8, B
, 8, GLX_DEPTH_SIZE
, 8, GLX_DOUBLEBUFFER
, GLX_STENCIL_SIZE
, 1, None
},
421 { GLX_RGBA
, R
, 4, G
, 4, B
, 4, GLX_DEPTH_SIZE
, 4, GLX_DOUBLEBUFFER
, GLX_STENCIL_SIZE
, 1, None
},
422 { GLX_RGBA
, R
, 8, G
, 8, B
, 8, GLX_DEPTH_SIZE
, 8, GLX_DOUBLEBUFFER
, None
},
423 { GLX_RGBA
, R
, 4, G
, 4, B
, 4, GLX_DEPTH_SIZE
, 4, GLX_DOUBLEBUFFER
, None
},
424 { GLX_RGBA
, R
, 8, G
, 8, B
, 8, GLX_DEPTH_SIZE
, 8, GLX_STENCIL_SIZE
, 1, None
},
425 { GLX_RGBA
, R
, 4, G
, 4, B
, 4, GLX_DEPTH_SIZE
, 4, GLX_STENCIL_SIZE
, 1, None
},
426 { GLX_RGBA
, R
, 8, G
, 8, B
, 8, GLX_DEPTH_SIZE
, 8, None
},
427 { GLX_RGBA
, R
, 4, G
, 4, B
, 4, GLX_DEPTH_SIZE
, 4, None
},
428 { GLX_RGBA
, GLX_DEPTH_SIZE
, 8, GLX_DOUBLEBUFFER
, GLX_STENCIL_SIZE
, 1, None
},
429 { GLX_RGBA
, GLX_DEPTH_SIZE
, 8, GLX_DOUBLEBUFFER
, None
},
430 { GLX_RGBA
, GLX_DEPTH_SIZE
, 8, GLX_STENCIL_SIZE
, 1, None
},
431 { GLX_RGBA
, GLX_DEPTH_SIZE
, 8, None
}
436 for( unsigned int i
= 0;
437 i
< sizeof( attribs
) / sizeof( attribs
[ 0 ] );
440 if( XVisualInfo
* info
= glXChooseVisual( x11Info().display(), x11Info().screen(), attribs
[ i
] ))
442 visual
= info
->visual
;
443 static Colormap colormap
= 0;
445 XFreeColormap( x11Info().display(), colormap
);
446 colormap
= XCreateColormap( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()), visual
, AllocNone
);
447 attrs
.colormap
= colormap
;
455 Window w
= XCreateWindow( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()),
456 x(), y(), width(), height(), 0, x11Info().depth(), InputOutput
, visual
, flags
, &attrs
);
460 // Some xscreensaver hacks check for this property
461 const char *version
= "KDE 4.0";
462 XChangeProperty (QX11Info::display(), winId(),
463 gXA_SCREENSAVER_VERSION
, XA_STRING
, 8, PropModeReplace
,
464 (unsigned char *) version
, strlen(version
));
467 XSetWindowAttributes attr
;
468 attr
.event_mask
= KeyPressMask
| ButtonPressMask
| PointerMotionMask
|
469 VisibilityChangeMask
| ExposureMask
;
470 XChangeWindowAttributes(QX11Info::display(), winId(),
475 // set NoBackground so that the saver can capture the current
476 // screen state if necessary
477 setAttribute(Qt::WA_NoSystemBackground
, true);
479 setCursor( Qt::BlankCursor
);
480 setGeometry(0, 0, mRootWidth
, mRootHeight
);
483 kDebug(1204) << "Saver window Id: " << winId() << endl
;
486 //---------------------------------------------------------------------------
488 // Hide the screensaver window
490 void LockProcess::hideSaverWindow()
494 removeVRoot(winId());
495 XDeleteProperty(QX11Info::display(), winId(), gXA_SCREENSAVER_VERSION
);
497 unsigned long vroot_data
[1] = { gVRootData
};
498 XChangeProperty(QX11Info::display(), gVRoot
, gXA_VROOT
, XA_WINDOW
, 32,
499 PropModeReplace
, (unsigned char *)vroot_data
, 1);
502 XSync(QX11Info::display(), False
);
505 //---------------------------------------------------------------------------
506 static int ignoreXError(Display
*, XErrorEvent
*)
511 //---------------------------------------------------------------------------
513 // Save the current virtual root window
515 void LockProcess::saveVRoot()
517 Window rootReturn
, parentReturn
, *children
;
518 unsigned int numChildren
;
520 Window root
= RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info
.screen()));
525 int (*oldHandler
)(Display
*, XErrorEvent
*);
526 oldHandler
= XSetErrorHandler(ignoreXError
);
528 if (XQueryTree(QX11Info::display(), root
, &rootReturn
, &parentReturn
,
529 &children
, &numChildren
))
531 for (unsigned int i
= 0; i
< numChildren
; i
++)
535 unsigned long nitems
, bytesafter
;
536 unsigned char *newRoot
= 0;
538 if ((XGetWindowProperty(QX11Info::display(), children
[i
], gXA_VROOT
, 0, 1,
539 False
, XA_WINDOW
, &actual_type
, &actual_format
, &nitems
, &bytesafter
,
540 &newRoot
) == Success
) && newRoot
)
542 gVRoot
= children
[i
];
543 Window
*dummy
= (Window
*)newRoot
;
545 XFree ((char*) newRoot
);
551 XFree((char *)children
);
555 XSetErrorHandler(oldHandler
);
558 //---------------------------------------------------------------------------
560 // Set the virtual root property
562 void LockProcess::setVRoot(Window win
, Window vr
)
568 unsigned long rw
= RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info
.screen()));
569 unsigned long vroot_data
[1] = { vr
};
571 Window rootReturn
, parentReturn
, *children
;
572 unsigned int numChildren
;
575 XQueryTree(QX11Info::display(), top
, &rootReturn
, &parentReturn
,
576 &children
, &numChildren
);
578 XFree((char *)children
);
579 if (parentReturn
== rw
) {
585 XChangeProperty(QX11Info::display(), top
, gXA_VROOT
, XA_WINDOW
, 32,
586 PropModeReplace
, (unsigned char *)vroot_data
, 1);
589 //---------------------------------------------------------------------------
591 // Remove the virtual root property
593 void LockProcess::removeVRoot(Window win
)
595 XDeleteProperty (QX11Info::display(), win
, gXA_VROOT
);
598 //---------------------------------------------------------------------------
600 // Grab the keyboard. Returns true on success
602 bool LockProcess::grabKeyboard()
604 int rv
= XGrabKeyboard( QX11Info::display(), QApplication::desktop()->winId(),
605 True
, GrabModeAsync
, GrabModeAsync
, CurrentTime
);
607 return (rv
== GrabSuccess
);
610 #define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \
611 EnterWindowMask | LeaveWindowMask
613 //---------------------------------------------------------------------------
615 // Grab the mouse. Returns true on success
617 bool LockProcess::grabMouse()
619 int rv
= XGrabPointer( QX11Info::display(), QApplication::desktop()->winId(),
620 True
, GRABEVENTS
, GrabModeAsync
, GrabModeAsync
, None
,
621 QCursor(Qt::BlankCursor
).handle(), CurrentTime
);
623 return (rv
== GrabSuccess
);
626 //---------------------------------------------------------------------------
628 // Grab keyboard and mouse. Returns true on success.
630 bool LockProcess::grabInput()
632 XSync(QX11Info::display(), False
);
648 XUngrabKeyboard(QX11Info::display(), CurrentTime
);
658 //---------------------------------------------------------------------------
660 // Release mouse an keyboard grab.
662 void LockProcess::ungrabInput()
664 XUngrabKeyboard(QX11Info::display(), CurrentTime
);
665 XUngrabPointer(QX11Info::display(), CurrentTime
);
669 //---------------------------------------------------------------------------
671 // Start the screen saver.
673 bool LockProcess::startSaver()
675 if (!child_saver
&& !grabInput())
677 kWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" << endl
;
685 QSocketNotifier
*notifier
= new QSocketNotifier(mParent
, QSocketNotifier::Read
, this);
686 connect(notifier
, SIGNAL( activated (int)), SLOT( quitSaver()));
691 setCursor( Qt::BlankCursor
);
694 XSync(QX11Info::display(), False
);
696 setVRoot( winId(), winId() );
701 //---------------------------------------------------------------------------
703 // Stop the screen saver.
705 void LockProcess::stopSaver()
707 kDebug(1204) << "LockProcess: stopping saver" << endl
;
714 DM().setLock( false );
716 const char *out
= "GOAWAY!";
717 for (QList
<int>::ConstIterator it
= child_sockets
.begin(); it
!= child_sockets
.end(); ++it
)
718 write(*it
, out
, sizeof(out
));
723 QVariant
LockProcess::getConf(void *ctx
, const char *key
, const QVariant
&dflt
)
725 LockProcess
*that
= (LockProcess
*)ctx
;
726 QString fkey
= QLatin1String( key
) + '=';
727 for (QStringList::ConstIterator it
= that
->mPluginOptions
.begin();
728 it
!= that
->mPluginOptions
.end(); ++it
)
729 if ((*it
).startsWith( fkey
))
730 return (*it
).mid( fkey
.length() );
734 void LockProcess::cantLock( const QString
&txt
)
736 msgBox( QMessageBox::Critical
, i18n("Will not lock the session, as unlocking would be impossible:\n") + txt
);
739 #if 0 // placeholders for later
740 i18n("Cannot start <i>kcheckpass</i>.");
741 i18n("<i>kcheckpass</i> is unable to operate. Possibly it is not SetUID root.");
744 //---------------------------------------------------------------------------
746 // Make the screen saver password protected.
748 bool LockProcess::startLock()
750 for (QStringList::ConstIterator it
= mPlugins
.begin(); it
!= mPlugins
.end(); ++it
) {
751 GreeterPluginHandle plugin
;
752 QString path
= KLibLoader::self()->findLibrary(
753 ((*it
)[0] == '/' ? *it
: "kgreet_" + *it
).toLatin1() );
754 if (path
.isEmpty()) {
755 kWarning(1204) << "GreeterPlugin " << *it
<< " does not exist" << endl
;
758 if (!(plugin
.library
= KLibLoader::self()->library( path
.toLatin1() ))) {
759 kWarning(1204) << "Cannot load GreeterPlugin " << *it
<< " (" << path
<< ")" << endl
;
762 plugin
.info
= (kgreeterplugin_info
*)plugin
.library
->resolveSymbol( "kgreeterplugin_info" );
764 kWarning(1204) << "GreeterPlugin " << *it
<< " (" << path
<< ") is no valid greet widget plugin" << endl
;
765 plugin
.library
->unload();
768 if (plugin
.info
->method
&& !mMethod
.isEmpty() && mMethod
!= plugin
.info
->method
) {
769 kDebug(1204) << "GreeterPlugin " << *it
<< " (" << path
<< ") serves " << plugin
.info
->method
<< ", not " << mMethod
<< endl
;
770 plugin
.library
->unload();
773 if (!plugin
.info
->init( mMethod
, getConf
, this )) {
774 kDebug(1204) << "GreeterPlugin " << *it
<< " (" << path
<< ") refuses to serve " << mMethod
<< endl
;
775 plugin
.library
->unload();
778 kDebug(1204) << "GreeterPlugin " << *it
<< " (" << plugin
.info
->method
<< ", " << plugin
.info
->name
<< ") loaded" << endl
;
779 greetPlugin
= plugin
;
781 DM().setLock( true );
784 cantLock( i18n("No appropriate greeter plugin configured.") );
788 //---------------------------------------------------------------------------
792 bool LockProcess::startHack()
794 if (mSaverExec
.isEmpty())
799 if (mHackProc
.isRunning())
804 mHackProc
.clearArguments();
806 QTextStream
ts(&mSaverExec
, QIODevice::ReadOnly
);
809 QString path
= KStandardDirs::findExe(word
);
815 kDebug(1204) << "Starting hack: " << path
<< endl
;
822 word
= word
.setNum(winId());
830 if (mHackProc
.start() == true)
832 #ifdef HAVE_SETPRIORITY
833 setpriority(PRIO_PROCESS
, mHackProc
.pid(), mPriority
);
835 //bitBlt(this, 0, 0, &mOriginal);
841 // we aren't allowed to start the specified screensaver either because it didn't run for some reason
842 // according to the kiosk restrictions forbid it
844 palette
.setColor(backgroundRole(), Qt::black
);
851 //---------------------------------------------------------------------------
853 void LockProcess::stopHack()
855 if (mHackProc
.isRunning())
858 if (!mHackProc
.wait(10))
860 mHackProc
.kill(SIGKILL
);
865 //---------------------------------------------------------------------------
867 void LockProcess::hackExited(K3Process
*)
869 // Hack exited while we're supposed to be saving the screen.
870 // Make sure the saver window is black.
872 palette
.setColor(backgroundRole(), Qt::black
);
876 void LockProcess::suspend()
878 if( !mSuspended
&& mHackProc
.isRunning() )
880 mHackProc
.kill(SIGSTOP
);
881 QApplication::syncX();
882 mSavedScreen
= QPixmap::grabWindow( winId());
887 void LockProcess::resume( bool force
)
889 if( !force
&& (!mDialogs
.isEmpty() || !mVisibility
))
890 return; // no resuming with dialog visible or when not visible
891 if( mSuspended
&& mHackProc
.isRunning() )
893 XForceScreenSaver(QX11Info::display(), ScreenSaverReset
);
894 bitBlt( this, 0, 0, &mSavedScreen
);
895 QApplication::syncX();
896 mHackProc
.kill(SIGCONT
);
901 //---------------------------------------------------------------------------
903 // Show the password dialog
904 // This is called only in the master process
906 bool LockProcess::checkPass()
908 killTimer(mAutoLogoutTimerId
);
909 PasswordDlg
passDlg( this, &greetPlugin
);
911 int ret
= execDialog( &passDlg
);
913 XWindowAttributes rootAttr
;
914 XGetWindowAttributes(QX11Info::display(), QX11Info::appRootWindow(), &rootAttr
);
915 if(( rootAttr
.your_event_mask
& SubstructureNotifyMask
) == 0 )
917 kWarning() << "ERROR: Something removed SubstructureNotifyMask from the root window!!!" << endl
;
918 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(),
919 SubstructureNotifyMask
| rootAttr
.your_event_mask
);
922 return ret
== QDialog::Accepted
;
925 static void fakeFocusIn( WId window
)
927 // We have keyboard grab, so this application will
928 // get keyboard events even without having focus.
929 // Fake FocusIn to make Qt realize it has the active
930 // window, so that it will correctly show cursor in the dialog.
932 memset(&ev
, 0, sizeof(ev
));
933 ev
.xfocus
.display
= QX11Info::display();
934 ev
.xfocus
.type
= FocusIn
;
935 ev
.xfocus
.window
= window
;
936 ev
.xfocus
.mode
= NotifyNormal
;
937 ev
.xfocus
.detail
= NotifyAncestor
;
938 XSendEvent( QX11Info::display(), window
, False
, NoEventMask
, &ev
);
941 int LockProcess::execDialog( QDialog
*dlg
)
945 QRect rect
= dlg
->geometry();
946 rect
.moveCenter(KGlobalSettings::desktopGeometry(QCursor::pos()).center());
947 dlg
->move( rect
.topLeft() );
949 if (mDialogs
.isEmpty())
952 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS
,
953 QCursor(Qt::ArrowCursor
).handle(), CurrentTime
);
955 mDialogs
.prepend( dlg
);
956 fakeFocusIn( dlg
->winId());
957 int rt
= dlg
->exec();
958 int pos
= mDialogs
.indexOf( dlg
);
960 mDialogs
.remove( pos
);
961 if( mDialogs
.isEmpty() ) {
962 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS
,
963 QCursor(Qt::BlankCursor
).handle(), CurrentTime
);
966 fakeFocusIn( mDialogs
.first()->winId());
970 void LockProcess::preparePopup()
972 QWidget
*dlg
= (QWidget
*)sender();
973 mDialogs
.prepend( dlg
);
974 fakeFocusIn( dlg
->winId() );
977 void LockProcess::cleanupPopup()
979 QWidget
*dlg
= (QWidget
*)sender();
981 int pos
= mDialogs
.indexOf( dlg
);
982 mDialogs
.remove( pos
);
983 fakeFocusIn( mDialogs
.first()->winId() );
986 //---------------------------------------------------------------------------
990 bool LockProcess::x11Event(XEvent
*event
)
997 if (mBusy
|| !mDialogs
.isEmpty())
1000 if (!mLocked
|| checkPass())
1005 else if (mAutoLogout
) // we need to restart the auto logout countdown
1007 killTimer(mAutoLogoutTimerId
);
1008 mAutoLogoutTimerId
= startTimer(mAutoLogoutTimeout
);
1013 case VisibilityNotify
:
1014 if( event
->xvisibility
.window
== winId())
1015 { // mVisibility == false means the screensaver is not visible at all
1016 // e.g. when switched to text console
1017 mVisibility
= !(event
->xvisibility
.state
== VisibilityFullyObscured
);
1019 mSuspendTimer
.start(2000);
1022 mSuspendTimer
.stop();
1025 if (event
->xvisibility
.state
!= VisibilityUnobscured
)
1030 case ConfigureNotify
: // from SubstructureNotifyMask on the root window
1031 if(event
->xconfigure
.event
== QX11Info::appRootWindow())
1034 case MapNotify
: // from SubstructureNotifyMask on the root window
1035 if( event
->xmap
.event
== QX11Info::appRootWindow())
1040 // We have grab with the grab window being the root window.
1041 // This results in key events being sent to the root window,
1042 // but they should be sent to the dialog if it's visible.
1043 // It could be solved by setFocus() call, but that would mess
1044 // the focus after this process exits.
1045 // Qt seems to be quite hard to persuade to redirect the event,
1046 // so let's simply dupe it with correct destination window,
1047 // and ignore the original one.
1048 if(!mDialogs
.isEmpty() && ( event
->type
== KeyPress
|| event
->type
== KeyRelease
)
1049 && event
->xkey
.window
!= mDialogs
.first()->winId())
1051 XEvent ev2
= *event
;
1052 ev2
.xkey
.window
= ev2
.xkey
.subwindow
= mDialogs
.first()->winId();
1053 qApp
->x11ProcessEvent( &ev2
);
1060 void LockProcess::stayOnTop()
1062 if(!mDialogs
.isEmpty())
1064 // this restacking is written in a way so that
1065 // if the stacking positions actually don't change,
1066 // all restacking operations will be no-op,
1067 // and no ConfigureNotify will be generated,
1068 // thus avoiding possible infinite loops
1069 XRaiseWindow( QX11Info::display(), mDialogs
.first()->winId()); // raise topmost
1070 // and stack others below it
1071 Window
* stack
= new Window
[ mDialogs
.count() + 1 ];
1073 for( QVector
< QWidget
* >::ConstIterator it
= mDialogs
.begin();
1074 it
!= mDialogs
.end();
1076 stack
[ count
++ ] = (*it
)->winId();
1077 stack
[ count
++ ] = winId();
1078 XRestackWindows( x11Info().display(), stack
, count
);
1082 XRaiseWindow(QX11Info::display(), winId());
1085 void LockProcess::checkDPMSActive()
1090 DPMSInfo(QX11Info::display(), &state
, &on
);
1091 //kDebug() << "checkDPMSActive " << on << " " << state << endl;
1092 if (state
== DPMSModeStandby
|| state
== DPMSModeSuspend
|| state
== DPMSModeOff
)
1095 } else if ( mSuspended
)
1102 #if defined(HAVE_XF86MISC) && defined(HAVE_XF86MISCSETGRABKEYSSTATE)
1103 // see http://cvsweb.xfree86.org/cvsweb/xc/programs/Xserver/hw/xfree86/common/xf86Events.c#rev3.113
1104 // This allows enabling the "Allow{Deactivate/Closedown}Grabs" options in XF86Config,
1105 // and krunner_lock will still lock the session.
1106 static enum { Unknown
, Yes
, No
} can_do_xf86_lock
= Unknown
;
1107 void LockProcess::lockXF86()
1109 if( can_do_xf86_lock
== Unknown
)
1112 if( XF86MiscQueryVersion( QX11Info::display(), &major
, &minor
)
1113 && major
>= 0 && minor
>= 5 )
1114 can_do_xf86_lock
= Yes
;
1116 can_do_xf86_lock
= No
;
1118 if( can_do_xf86_lock
!= Yes
)
1120 if( mRestoreXF86Lock
)
1122 if( XF86MiscSetGrabKeysState( QX11Info::display(), False
) != MiscExtGrabStateSuccess
)
1125 mRestoreXF86Lock
= true;
1128 void LockProcess::unlockXF86()
1130 if( can_do_xf86_lock
!= Yes
)
1132 if( !mRestoreXF86Lock
)
1134 XF86MiscSetGrabKeysState( QX11Info::display(), True
);
1135 mRestoreXF86Lock
= false;
1138 void LockProcess::lockXF86()
1142 void LockProcess::unlockXF86()
1147 void LockProcess::msgBox( QMessageBox::Icon type
, const QString
&txt
)
1149 QDialog
box( 0, Qt::X11BypassWindowManagerHint
);
1150 box
.setModal( true );
1151 QFrame
*winFrame
= new QFrame( &box
);
1152 winFrame
->setFrameStyle( QFrame::WinPanel
| QFrame::Raised
);
1153 winFrame
->setLineWidth( 2 );
1154 QLabel
*label1
= new QLabel( winFrame
);
1155 label1
->setPixmap( QMessageBox::standardIcon( type
) );
1156 QLabel
*label2
= new QLabel( txt
, winFrame
);
1157 KPushButton
*button
= new KPushButton( KStandardGuiItem::ok(), winFrame
);
1158 button
->setDefault( true );
1159 button
->setSizePolicy( QSizePolicy( QSizePolicy::Preferred
, QSizePolicy::Preferred
) );
1160 connect( button
, SIGNAL( clicked() ), &box
, SLOT( accept() ) );
1162 QVBoxLayout
*vbox
= new QVBoxLayout( &box
);
1163 vbox
->addWidget( winFrame
);
1164 QGridLayout
*grid
= new QGridLayout( winFrame
);
1165 grid
->setSpacing( 10 );
1166 grid
->addWidget( label1
, 0, 0, Qt::AlignCenter
);
1167 grid
->addWidget( label2
, 0, 1, Qt::AlignCenter
);
1168 grid
->addWidget( button
, 1, 0, 1, 2, Qt::AlignCenter
);
1173 #include "lockprocess.moc"