Backported build fixes from SVN. Now builds fine against the lastes SVN trunk.
[krunner/jaws.git] / lock / lockprocess.cc
blobedc53094ccca057c64638d09d20d38eb2db84782
1 //===========================================================================
2 //
3 // This file is part of the KDE project
4 //
5 // Copyright (c) 1999 Martin R. Jones <mjones@kde.org>
6 // Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org>
7 //
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
15 //crashes
17 #include <config-workspace.h>
18 #include <config-X11.h>
19 #include <config-krunner-lock.h>
20 #include "lockprocess.h"
21 #include "lockdlg.h"
22 #include "autologout.h"
23 #include "kscreensaversettings.h"
25 #include <dmctl.h>
27 #include <kstandarddirs.h>
28 #include <kapplication.h>
29 #include <kservicegroup.h>
30 #include <kdebug.h>
31 #include <kmessagebox.h>
32 #include <kglobalsettings.h>
33 #include <klocale.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>
42 #include <QLabel>
43 #include <QLayout>
44 #include <QCursor>
45 #include <QTimer>
46 #include <QFile>
47 #include <QSocketNotifier>
48 #include <QDesktopWidget>
49 #include <QX11Info>
50 #include <QTextStream>
52 #include <QDateTime>
54 #include <stdlib.h>
55 #include <assert.h>
56 #include <signal.h>
57 #ifdef HAVE_SETPRIORITY
58 #include <sys/time.h>
59 #include <sys/resource.h>
60 #endif
62 #include <X11/Xlib.h>
63 #include <X11/Xutil.h>
64 #include <X11/keysym.h>
65 #include <X11/Xatom.h>
67 #ifdef HAVE_DPMS
68 extern "C" {
69 #include <X11/Xmd.h>
70 #ifndef Bool
71 #define Bool BOOL
72 #endif
73 #include <X11/extensions/dpms.h>
75 #ifndef HAVE_DPMSINFO_PROTO
76 Status DPMSInfo ( Display *, CARD16 *, BOOL * );
77 #endif
79 #endif
81 #ifdef HAVE_XF86MISC
82 #include <X11/extensions/xf86misc.h>
83 #endif
85 #ifdef HAVE_GLXCHOOSEVISUAL
86 #include <GL/glx.h>
87 #endif
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),
104 mOpenGLVisual(0),
105 child_saver(child),
106 mParent(0),
107 mUseBlankOnly(useBlankOnly),
108 mSuspended(false),
109 mVisibility(false),
110 mRestoreXF86Lock(false),
111 mForbidden(false),
112 mAutoLogout(false)
114 setObjectName("save window");
115 setupSignals();
117 kapp->installX11EventFilter(this);
119 // Get root window size
120 XWindowAttributes rootAttr;
121 QX11Info info;
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()));
139 QStringList dmopt =
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);
145 configure();
147 #ifdef HAVE_DPMS
148 if (mDPMSDepend) {
149 BOOL on;
150 CARD16 state;
151 DPMSInfo(QX11Info::display(), &state, &on);
152 if (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);
160 #endif
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)
182 char tmp = 'T';
183 ::write( signal_pipe[1], &tmp, 1);
186 static void sighup_handler(int)
188 char tmp = 'H';
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;
205 // ignore SIGINT
206 act.sa_handler=SIG_IGN;
207 sigemptyset(&(act.sa_mask));
208 sigaddset(&(act.sa_mask), SIGINT);
209 act.sa_flags = 0;
210 sigaction(SIGINT, &act, 0L);
211 // ignore SIGQUIT
212 act.sa_handler=SIG_IGN;
213 sigemptyset(&(act.sa_mask));
214 sigaddset(&(act.sa_mask), SIGQUIT);
215 act.sa_flags = 0;
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);
221 act.sa_flags = 0;
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);
227 act.sa_flags = 0;
228 sigaction(SIGHUP, &act, 0L);
230 pipe(signal_pipe);
231 QSocketNotifier* notif = new QSocketNotifier(signal_pipe[0], QSocketNotifier::Read, this);
232 connect( notif, SIGNAL(activated(int)), SLOT(signalPipeSignal()));
236 void LockProcess::signalPipeSignal()
238 char tmp;
239 ::read( signal_pipe[0], &tmp, 1);
240 if( tmp == 'T' )
241 quitSaver();
242 else if( tmp == 'H' ) {
243 if( !mLocked )
244 startLock();
248 //---------------------------------------------------------------------------
249 bool LockProcess::lock()
251 if (startSaver()) {
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.
257 mBusy = true;
258 if (startLock())
260 QTimer::singleShot(1000, this, SLOT(slotDeadTimePassed()));
261 return true;
263 stopSaver();
264 mBusy = false;
266 return false;
268 //---------------------------------------------------------------------------
269 void LockProcess::slotDeadTimePassed()
271 mBusy = false;
274 //---------------------------------------------------------------------------
275 bool LockProcess::defaultSave()
277 mLocked = false;
278 if (startSaver()) {
279 if (mLockGrace >= 0)
280 QTimer::singleShot(mLockGrace, this, SLOT(startLock()));
281 return true;
283 return false;
286 //---------------------------------------------------------------------------
287 bool LockProcess::dontLock()
289 mLocked = false;
290 return startSaver();
293 //---------------------------------------------------------------------------
294 void LockProcess::quitSaver()
296 stopSaver();
297 qApp->quit();
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();
309 if (mLockGrace < 0)
310 mLockGrace = 0;
311 else if (mLockGrace > 300000)
312 mLockGrace = 300000; // 5 minutes, keep the value sane
313 } else {
314 mLockGrace = -1;
317 if ( KScreenSaverSettings::autoLogout() ) {
318 mAutoLogout = true;
319 mAutoLogoutTimeout = KScreenSaverSettings::autoLogoutTimeout();
320 mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); // in milliseconds
323 #ifdef HAVE_DPMS
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();
327 #endif
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";
338 readSaver();
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;
363 return;
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;
380 mForbidden = true;
382 if ((saverTypes[i] == "OpenGL") && !opengl)
384 kDebug(1204) << "Screensaver is type OpenGL and OpenGL is forbidden" << endl;
385 mForbidden = true;
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;
411 int flags = 0;
412 #ifdef HAVE_GLXCHOOSEVISUAL
413 if( mOpenGLVisual )
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 }
432 #undef R
433 #undef G
434 #undef B
436 for( unsigned int i = 0;
437 i < sizeof( attribs ) / sizeof( attribs[ 0 ] );
438 ++i )
440 if( XVisualInfo* info = glXChooseVisual( x11Info().display(), x11Info().screen(), attribs[ i ] ))
442 visual = info->visual;
443 static Colormap colormap = 0;
444 if( colormap != 0 )
445 XFreeColormap( x11Info().display(), colormap );
446 colormap = XCreateColormap( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()), visual, AllocNone );
447 attrs.colormap = colormap;
448 flags |= CWColormap;
449 XFree( info );
450 break;
454 #endif
455 Window w = XCreateWindow( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()),
456 x(), y(), width(), height(), 0, x11Info().depth(), InputOutput, visual, flags, &attrs );
458 create( w );
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(),
471 CWEventMask, &attr);
473 // erase();
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);
481 hide();
483 kDebug(1204) << "Saver window Id: " << winId() << endl;
486 //---------------------------------------------------------------------------
488 // Hide the screensaver window
490 void LockProcess::hideSaverWindow()
492 hide();
493 lower();
494 removeVRoot(winId());
495 XDeleteProperty(QX11Info::display(), winId(), gXA_SCREENSAVER_VERSION);
496 if ( gVRoot ) {
497 unsigned long vroot_data[1] = { gVRootData };
498 XChangeProperty(QX11Info::display(), gVRoot, gXA_VROOT, XA_WINDOW, 32,
499 PropModeReplace, (unsigned char *)vroot_data, 1);
500 gVRoot = 0;
502 XSync(QX11Info::display(), False);
505 //---------------------------------------------------------------------------
506 static int ignoreXError(Display *, XErrorEvent *)
508 return 0;
511 //---------------------------------------------------------------------------
513 // Save the current virtual root window
515 void LockProcess::saveVRoot()
517 Window rootReturn, parentReturn, *children;
518 unsigned int numChildren;
519 QX11Info info;
520 Window root = RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info.screen()));
522 gVRoot = 0;
523 gVRootData = 0;
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++)
533 Atom actual_type;
534 int actual_format;
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;
544 gVRootData = *dummy;
545 XFree ((char*) newRoot);
546 break;
549 if (children)
551 XFree((char *)children);
555 XSetErrorHandler(oldHandler);
558 //---------------------------------------------------------------------------
560 // Set the virtual root property
562 void LockProcess::setVRoot(Window win, Window vr)
564 if (gVRoot)
565 removeVRoot(gVRoot);
567 QX11Info info;
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;
573 Window top = win;
574 while (1) {
575 XQueryTree(QX11Info::display(), top , &rootReturn, &parentReturn,
576 &children, &numChildren);
577 if (children)
578 XFree((char *)children);
579 if (parentReturn == rw) {
580 break;
581 } else
582 top = parentReturn;
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);
634 if (!grabKeyboard())
636 sleep(1);
637 if (!grabKeyboard())
639 return false;
643 if (!grabMouse())
645 sleep(1);
646 if (!grabMouse())
648 XUngrabKeyboard(QX11Info::display(), CurrentTime);
649 return false;
653 lockXF86();
655 return true;
658 //---------------------------------------------------------------------------
660 // Release mouse an keyboard grab.
662 void LockProcess::ungrabInput()
664 XUngrabKeyboard(QX11Info::display(), CurrentTime);
665 XUngrabPointer(QX11Info::display(), CurrentTime);
666 unlockXF86();
669 //---------------------------------------------------------------------------
671 // Start the screen saver.
673 bool LockProcess::startSaver()
675 if (!child_saver && !grabInput())
677 kWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" << endl;
678 return false;
680 mBusy = false;
682 saveVRoot();
684 if (mParent) {
685 QSocketNotifier *notifier = new QSocketNotifier(mParent, QSocketNotifier::Read, this);
686 connect(notifier, SIGNAL( activated (int)), SLOT( quitSaver()));
688 createSaverWindow();
689 move(0, 0);
690 show();
691 setCursor( Qt::BlankCursor );
693 raise();
694 XSync(QX11Info::display(), False);
696 setVRoot( winId(), winId() );
697 startHack();
698 return true;
701 //---------------------------------------------------------------------------
703 // Stop the screen saver.
705 void LockProcess::stopSaver()
707 kDebug(1204) << "LockProcess: stopping saver" << endl;
708 resume( true );
709 stopHack();
710 hideSaverWindow();
711 mVisibility = false;
712 if (!child_saver) {
713 if (mLocked)
714 DM().setLock( false );
715 ungrabInput();
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));
722 // private static
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() );
731 return dflt;
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.");
742 #endif
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;
756 continue;
758 if (!(plugin.library = KLibLoader::self()->library( path.toLatin1() ))) {
759 kWarning(1204) << "Cannot load GreeterPlugin " << *it << " (" << path << ")" << endl;
760 continue;
762 plugin.info = (kgreeterplugin_info*)plugin.library->resolveSymbol( "kgreeterplugin_info" );
763 if (!plugin.info ) {
764 kWarning(1204) << "GreeterPlugin " << *it << " (" << path << ") is no valid greet widget plugin" << endl;
765 plugin.library->unload();
766 continue;
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();
771 continue;
773 if (!plugin.info->init( mMethod, getConf, this )) {
774 kDebug(1204) << "GreeterPlugin " << *it << " (" << path << ") refuses to serve " << mMethod << endl;
775 plugin.library->unload();
776 continue;
778 kDebug(1204) << "GreeterPlugin " << *it << " (" << plugin.info->method << ", " << plugin.info->name << ") loaded" << endl;
779 greetPlugin = plugin;
780 mLocked = true;
781 DM().setLock( true );
782 return true;
784 cantLock( i18n("No appropriate greeter plugin configured.") );
785 return false;
788 //---------------------------------------------------------------------------
792 bool LockProcess::startHack()
794 if (mSaverExec.isEmpty())
796 return false;
799 if (mHackProc.isRunning())
801 stopHack();
804 mHackProc.clearArguments();
806 QTextStream ts(&mSaverExec, QIODevice::ReadOnly);
807 QString word;
808 ts >> word;
809 QString path = KStandardDirs::findExe(word);
811 if (!path.isEmpty())
813 mHackProc << path;
815 kDebug(1204) << "Starting hack: " << path << endl;
817 while (!ts.atEnd())
819 ts >> word;
820 if (word == "%w")
822 word = word.setNum(winId());
824 mHackProc << word;
827 if (!mForbidden)
830 if (mHackProc.start() == true)
832 #ifdef HAVE_SETPRIORITY
833 setpriority(PRIO_PROCESS, mHackProc.pid(), mPriority);
834 #endif
835 //bitBlt(this, 0, 0, &mOriginal);
836 return true;
839 else
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
843 QPalette palette;
844 palette.setColor(backgroundRole(), Qt::black);
845 setPalette(palette);
848 return false;
851 //---------------------------------------------------------------------------
853 void LockProcess::stopHack()
855 if (mHackProc.isRunning())
857 mHackProc.kill();
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.
871 QPalette palette;
872 palette.setColor(backgroundRole(), Qt::black);
873 setPalette(palette);
876 void LockProcess::suspend()
878 if( !mSuspended && mHackProc.isRunning() )
880 mHackProc.kill(SIGSTOP);
881 QApplication::syncX();
882 mSavedScreen = QPixmap::grabWindow( winId());
884 mSuspended = true;
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);
898 mSuspended = false;
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.
931 XEvent ev;
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 )
943 dlg->adjustSize();
945 QRect rect = dlg->geometry();
946 rect.moveCenter(KGlobalSettings::desktopGeometry(QCursor::pos()).center());
947 dlg->move( rect.topLeft() );
949 if (mDialogs.isEmpty())
951 suspend();
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 );
959 if (pos != -1)
960 mDialogs.remove( pos );
961 if( mDialogs.isEmpty() ) {
962 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS,
963 QCursor(Qt::BlankCursor).handle(), CurrentTime);
964 resume( false );
965 } else
966 fakeFocusIn( mDialogs.first()->winId());
967 return rt;
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 //---------------------------------------------------------------------------
988 // X11 Event.
990 bool LockProcess::x11Event(XEvent *event)
992 switch (event->type)
994 case KeyPress:
995 case ButtonPress:
996 case MotionNotify:
997 if (mBusy || !mDialogs.isEmpty())
998 break;
999 mBusy = true;
1000 if (!mLocked || checkPass())
1002 stopSaver();
1003 qApp->quit();
1005 else if (mAutoLogout) // we need to restart the auto logout countdown
1007 killTimer(mAutoLogoutTimerId);
1008 mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout);
1010 mBusy = false;
1011 return true;
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);
1018 if(!mVisibility)
1019 mSuspendTimer.start(2000);
1020 else
1022 mSuspendTimer.stop();
1023 resume( false );
1025 if (event->xvisibility.state != VisibilityUnobscured)
1026 stayOnTop();
1028 break;
1030 case ConfigureNotify: // from SubstructureNotifyMask on the root window
1031 if(event->xconfigure.event == QX11Info::appRootWindow())
1032 stayOnTop();
1033 break;
1034 case MapNotify: // from SubstructureNotifyMask on the root window
1035 if( event->xmap.event == QX11Info::appRootWindow())
1036 stayOnTop();
1037 break;
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 );
1054 return true;
1057 return false;
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 ];
1072 int count = 0;
1073 for( QVector< QWidget* >::ConstIterator it = mDialogs.begin();
1074 it != mDialogs.end();
1075 ++it )
1076 stack[ count++ ] = (*it)->winId();
1077 stack[ count++ ] = winId();
1078 XRestackWindows( x11Info().display(), stack, count );
1079 delete[] stack;
1081 else
1082 XRaiseWindow(QX11Info::display(), winId());
1085 void LockProcess::checkDPMSActive()
1087 #ifdef HAVE_DPMS
1088 BOOL on;
1089 CARD16 state;
1090 DPMSInfo(QX11Info::display(), &state, &on);
1091 //kDebug() << "checkDPMSActive " << on << " " << state << endl;
1092 if (state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff)
1094 suspend();
1095 } else if ( mSuspended )
1097 resume( true );
1099 #endif
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 )
1111 int major, minor;
1112 if( XF86MiscQueryVersion( QX11Info::display(), &major, &minor )
1113 && major >= 0 && minor >= 5 )
1114 can_do_xf86_lock = Yes;
1115 else
1116 can_do_xf86_lock = No;
1118 if( can_do_xf86_lock != Yes )
1119 return;
1120 if( mRestoreXF86Lock )
1121 return;
1122 if( XF86MiscSetGrabKeysState( QX11Info::display(), False ) != MiscExtGrabStateSuccess )
1123 return;
1124 // success
1125 mRestoreXF86Lock = true;
1128 void LockProcess::unlockXF86()
1130 if( can_do_xf86_lock != Yes )
1131 return;
1132 if( !mRestoreXF86Lock )
1133 return;
1134 XF86MiscSetGrabKeysState( QX11Info::display(), True );
1135 mRestoreXF86Lock = false;
1137 #else
1138 void LockProcess::lockXF86()
1142 void LockProcess::unlockXF86()
1145 #endif
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 );
1170 execDialog( &box );
1173 #include "lockprocess.moc"