add more spacing
[personal-kdebase.git] / workspace / krunner / lock / lockprocess.cc
blobdb48f0e3731c469076c246344fcbb470c1cd045d
1 //===========================================================================
2 //
3 // This file is part of the KDE project
4 //
5 // Copyright 1999 Martin R. Jones <mjones@kde.org>
6 // Copyright 2003 Oswald Buddenhagen <ossi@kde.org>
7 // Copyright 2008 Chani Armitage <chanika@gmail.com>
8 //
10 //krunner keeps running and checks user inactivity
11 //when it should show screensaver (and maybe lock the session),
12 //it starts kscreenlocker, who does all the locking and who
13 //actually starts the screensaver
15 //It's done this way to prevent screen unlocking when krunner
16 //crashes
18 #include "lockprocess.h"
19 #include "lockprocessadaptor.h"
21 #include <config-workspace.h>
22 #include <config-X11.h>
23 #include <config-krunner-lock.h>
24 #include "lockdlg.h"
25 #include "autologout.h"
26 #include "kscreensaversettings.h"
28 #include <kdisplaymanager.h>
30 #include <KStandardDirs>
31 #include <KApplication>
32 #include <KServiceGroup>
33 #include <KDebug>
34 #include <KMessageBox>
35 #include <KGlobalSettings>
36 #include <KLocale>
37 #include <KLibrary>
38 #include <KPushButton>
39 #include <KStandardGuiItem>
40 #include <KAuthorized>
41 #include <KDesktopFile>
42 #include <kservicetypetrader.h>
43 #include <kmacroexpander.h>
44 #include <kshell.h>
46 #include <QtGui/QFrame>
47 #include <QLabel>
48 #include <QLayout>
49 #include <QCursor>
50 #include <QTimer>
51 #include <QFile>
52 #include <QSocketNotifier>
53 #include <QDesktopWidget>
54 #include <QX11Info>
55 #include <QTextStream>
56 #include <QPainter>
57 #include <QDBusConnection>
58 #include <QDBusConnectionInterface>
59 #include <QDBusInterface>
61 #include <QDateTime>
63 #include <stdlib.h>
64 #include <assert.h>
65 #include <signal.h>
66 #include <unistd.h>
67 #ifdef HAVE_SETPRIORITY
68 #include <sys/time.h>
69 #include <sys/resource.h>
70 #endif
72 #include <X11/Xlib.h>
73 #include <X11/Xutil.h>
74 #include <X11/keysym.h>
75 #include <X11/Xatom.h>
77 #ifdef HAVE_DPMS
78 extern "C" {
79 #include <X11/Xmd.h>
80 #ifndef Bool
81 #define Bool BOOL
82 #endif
83 #include <X11/extensions/dpms.h>
85 #ifndef HAVE_DPMSINFO_PROTO
86 Status DPMSInfo ( Display *, CARD16 *, BOOL * );
87 #endif
89 #endif
91 #ifdef HAVE_XF86MISC
92 #include <X11/extensions/xf86misc.h>
93 #endif
95 #ifdef HAVE_GLXCHOOSEVISUAL
96 #include <GL/glx.h>
97 #endif
99 #define LOCK_GRACE_DEFAULT 5000
100 #define AUTOLOGOUT_DEFAULT 600
102 static Window gVRoot = 0;
103 static Window gVRootData = 0;
104 static Atom gXA_VROOT;
105 static Atom gXA_SCREENSAVER_VERSION;
107 //#define CHECK_XSELECTINPUT
108 #ifdef CHECK_XSELECTINPUT
109 #include <dlfcn.h>
110 static bool check_xselectinput = false;
111 extern "C"
112 int XSelectInput( Display* dpy, Window w, long e )
114 typedef int (*ptr)(Display*, Window, long);
115 static ptr fun = NULL;
116 if( fun == NULL )
117 fun = (ptr)dlsym( RTLD_NEXT, "XSelectInput" );
118 if( check_xselectinput && w == DefaultRootWindow( dpy ))
119 kDebug() << kBacktrace();
120 return fun( dpy, w, e );
122 #endif
125 //===========================================================================
127 // Screen saver handling process. Handles screensaver window,
128 // starting screensaver hacks, and password entry.f
130 LockProcess::LockProcess(bool child, bool useBlankOnly)
131 : QWidget(0L, Qt::X11BypassWindowManagerHint),
132 mLocked(false),
133 mBusy(false),
134 mPlasmaDBus(0),
135 mSetupMode(false),
136 mOpenGLVisual(false),
137 child_saver(child),
138 mParent(0),
139 mUseBlankOnly(useBlankOnly),
140 mSuspended(false),
141 mVisibility(false),
142 mRestoreXF86Lock(false),
143 mForbidden(false),
144 mAutoLogoutTimerId(0)
146 setObjectName("save window");
147 setupSignals();
149 new LockProcessAdaptor(this);
150 QDBusConnection::sessionBus().registerService("org.kde.screenlocker");
151 QDBusConnection::sessionBus().registerObject("/LockProcess", this);
153 kapp->installX11EventFilter(this);
155 // Get root window size
156 XWindowAttributes rootAttr;
157 QX11Info info;
158 XGetWindowAttributes(QX11Info::display(), RootWindow(QX11Info::display(),
159 info.screen()), &rootAttr);
160 kapp->desktop(); // make Qt set its event mask on the root window first
161 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(),
162 SubstructureNotifyMask | rootAttr.your_event_mask );
163 #ifdef CHECK_XSELECTINPUT
164 check_xselectinput = true;
165 #endif
166 setGeometry(0, 0, rootAttr.width, rootAttr.height);
168 // virtual root property
169 gXA_VROOT = XInternAtom (QX11Info::display(), "__SWM_VROOT", False);
170 gXA_SCREENSAVER_VERSION = XInternAtom (QX11Info::display(), "_SCREENSAVER_VERSION", False);
172 connect(&mHackProc, SIGNAL(finished(int, QProcess::ExitStatus)),
173 SLOT(hackExited()));
175 mSuspendTimer.setSingleShot(true);
176 connect(&mSuspendTimer, SIGNAL(timeout()), SLOT(suspend()));
178 QStringList dmopt =
179 QString::fromLatin1( ::getenv( "XDM_MANAGED" )).split(QChar(','), QString::SkipEmptyParts);
180 for (QStringList::ConstIterator it = dmopt.constBegin(); it != dmopt.constEnd(); ++it)
181 if ((*it).startsWith("method="))
182 mMethod = (*it).mid(7);
184 configure();
186 #ifdef HAVE_DPMS
187 if (mDPMSDepend) {
188 BOOL on;
189 CARD16 state;
190 DPMSInfo(QX11Info::display(), &state, &on);
191 if (on)
193 connect(&mCheckDPMS, SIGNAL(timeout()), SLOT(checkDPMSActive()));
194 // we can save CPU if we stop it as quickly as possible
195 // but we waste CPU if we check too often -> so take 10s
196 mCheckDPMS.start(10000);
199 #endif
201 greetPlugin.library = 0;
203 mSuppressUnlock.setSingleShot(true);
204 connect(&mSuppressUnlock, SIGNAL(timeout()), SLOT(deactivatePlasma()));
207 //---------------------------------------------------------------------------
209 // Destructor - usual cleanups.
211 LockProcess::~LockProcess()
213 if (greetPlugin.library) {
214 if (greetPlugin.info->done)
215 greetPlugin.info->done();
216 greetPlugin.library->unload();
220 static int signal_pipe[2];
222 static void sigterm_handler(int)
224 char tmp = 'T';
225 ::write( signal_pipe[1], &tmp, 1);
228 static void sighup_handler(int)
230 char tmp = 'H';
231 ::write( signal_pipe[1], &tmp, 1);
234 void LockProcess::timerEvent(QTimerEvent *ev)
236 if (ev->timerId() == mAutoLogoutTimerId)
238 killTimer(mAutoLogoutTimerId);
239 AutoLogout autologout(this);
240 execDialog(&autologout);
244 void LockProcess::setupSignals()
246 struct sigaction act;
247 // ignore SIGINT
248 act.sa_handler=SIG_IGN;
249 sigemptyset(&(act.sa_mask));
250 sigaddset(&(act.sa_mask), SIGINT);
251 act.sa_flags = 0;
252 sigaction(SIGINT, &act, 0L);
253 // ignore SIGQUIT
254 act.sa_handler=SIG_IGN;
255 sigemptyset(&(act.sa_mask));
256 sigaddset(&(act.sa_mask), SIGQUIT);
257 act.sa_flags = 0;
258 sigaction(SIGQUIT, &act, 0L);
259 // exit cleanly on SIGTERM
260 act.sa_handler= sigterm_handler;
261 sigemptyset(&(act.sa_mask));
262 sigaddset(&(act.sa_mask), SIGTERM);
263 act.sa_flags = 0;
264 sigaction(SIGTERM, &act, 0L);
265 // SIGHUP forces lock
266 act.sa_handler= sighup_handler;
267 sigemptyset(&(act.sa_mask));
268 sigaddset(&(act.sa_mask), SIGHUP);
269 act.sa_flags = 0;
270 sigaction(SIGHUP, &act, 0L);
272 pipe(signal_pipe);
273 QSocketNotifier* notif = new QSocketNotifier(signal_pipe[0], QSocketNotifier::Read, this);
274 connect( notif, SIGNAL(activated(int)), SLOT(signalPipeSignal()));
278 void LockProcess::signalPipeSignal()
280 char tmp;
281 ::read( signal_pipe[0], &tmp, 1);
282 if( tmp == 'T' )
283 quitSaver();
284 else if( tmp == 'H' ) {
285 if( !mLocked )
286 startLock();
290 //---------------------------------------------------------------------------
291 bool LockProcess::lock()
293 if (startSaver()) {
294 // In case of a forced lock we don't react to events during
295 // the dead-time to give the screensaver some time to activate.
296 // That way we don't accidentally show the password dialog before
297 // the screensaver kicks in because the user moved the mouse after
298 // selecting "lock screen", that looks really untidy.
299 mBusy = true;
300 if (startLock())
302 QTimer::singleShot(1000, this, SLOT(slotDeadTimePassed()));
303 return true;
305 stopSaver();
306 mBusy = false;
308 return false;
310 //---------------------------------------------------------------------------
311 void LockProcess::slotDeadTimePassed()
313 mBusy = false;
316 //---------------------------------------------------------------------------
317 bool LockProcess::defaultSave()
319 mLocked = false;
320 if (startSaver()) {
321 if (mLockGrace >= 0)
322 QTimer::singleShot(mLockGrace, this, SLOT(startLock()));
323 return true;
325 return false;
328 bool LockProcess::startSetup()
330 mPlasmaEnabled = true; //force it on in case the user didn't click apply yet
331 mLocked = false;
332 mSetupMode = true;
333 return startSaver();
334 //plasma startup will handle the suppressunlock bit
336 //---------------------------------------------------------------------------
337 bool LockProcess::dontLock()
339 mLocked = false;
340 return startSaver();
343 //---------------------------------------------------------------------------
344 void LockProcess::quitSaver()
346 stopSaver();
347 qApp->quit();
350 //---------------------------------------------------------------------------
352 // Read and apply configuration.
354 void LockProcess::configure()
356 // the configuration is stored in krunner's config file
357 if( KScreenSaverSettings::lock() ) {
358 mLockGrace = KScreenSaverSettings::lockGrace();
359 if (mLockGrace < 0)
360 mLockGrace = 0;
361 else if (mLockGrace > 300000)
362 mLockGrace = 300000; // 5 minutes, keep the value sane
363 } else {
364 mLockGrace = -1;
367 if ( KScreenSaverSettings::autoLogout() ) {
368 mAutoLogoutTimeout = KScreenSaverSettings::autoLogoutTimeout();
369 mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); // in milliseconds
372 #ifdef HAVE_DPMS
373 mDPMSDepend = KScreenSaverSettings::suspendWhenInvisible();
374 #endif
376 mPriority = KScreenSaverSettings::priority();
377 if (mPriority < 0) mPriority = 0;
378 if (mPriority > 19) mPriority = 19;
380 mSaver = KScreenSaverSettings::saver();
381 if (mSaver.isEmpty() || mUseBlankOnly) {
382 mSaver = "kblank.desktop";
385 readSaver();
387 mPlasmaEnabled = KScreenSaverSettings::plasmaEnabled();
389 mSuppressUnlockTimeout = qMax(0, KScreenSaverSettings::timeout() * 1000);
390 mSuppressUnlockTimeout = qMax(mSuppressUnlockTimeout, 30 * 1000); //min. 30 secs FIXME is this a good idea?
392 mPlugins = KScreenSaverSettings::pluginsUnlock();
393 if (mPlugins.isEmpty()) {
394 mPlugins << "classic" << "generic";
396 mPluginOptions = KScreenSaverSettings::pluginOptions();
399 //---------------------------------------------------------------------------
401 // Read the command line needed to run the screensaver given a .desktop file.
403 void LockProcess::readSaver()
405 if (!mSaver.isEmpty())
407 QString entryName = mSaver;
408 if( entryName.endsWith( ".desktop" ))
409 entryName = entryName.left( entryName.length() - 8 ); // strip it
410 KService::List offers = KServiceTypeTrader::self()->query( "ScreenSaver",
411 "DesktopEntryName == '" + entryName.toLower() + '\'' );
412 if( offers.count() == 0 )
414 kDebug(1204) << "Cannot find screesaver: " << mSaver;
415 return;
417 QString file = KStandardDirs::locate("services", offers.first()->entryPath());
419 bool opengl = KAuthorized::authorizeKAction("opengl_screensavers");
420 bool manipulatescreen = KAuthorized::authorizeKAction("manipulatescreen_screensavers");
421 KDesktopFile config( file );
422 KConfigGroup desktopGroup = config.desktopGroup();
423 if (!desktopGroup.readEntry("X-KDE-Type").toUtf8().isEmpty())
425 QString saverType = desktopGroup.readEntry("X-KDE-Type").toUtf8();
426 QStringList saverTypes = saverType.split( ";");
427 for (int i = 0; i < saverTypes.count(); i++)
429 if ((saverTypes[i] == "ManipulateScreen") && !manipulatescreen)
431 kDebug(1204) << "Screensaver is type ManipulateScreen and ManipulateScreen is forbidden";
432 mForbidden = true;
434 if ((saverTypes[i] == "OpenGL") && !opengl)
436 kDebug(1204) << "Screensaver is type OpenGL and OpenGL is forbidden";
437 mForbidden = true;
439 if (saverTypes[i] == "OpenGL")
441 mOpenGLVisual = true;
446 kDebug(1204) << "mForbidden: " << (mForbidden ? "true" : "false");
448 if (config.hasActionGroup("Root"))
450 mSaverExec = config.actionGroup("Root").readPathEntry("Exec", QString());
455 //---------------------------------------------------------------------------
457 // Create a window to draw our screen saver on.
459 void LockProcess::createSaverWindow()
461 Visual* visual = CopyFromParent;
462 int depth = CopyFromParent;
463 XSetWindowAttributes attrs;
464 int flags = CWOverrideRedirect;
465 #ifdef HAVE_GLXCHOOSEVISUAL
466 // this code is (partially) duplicated in kdebase/workspace/kcontrol/screensaver
467 if( mOpenGLVisual )
469 static int attribs[][ 15 ] =
471 #define R GLX_RED_SIZE
472 #define G GLX_GREEN_SIZE
473 #define B GLX_BLUE_SIZE
474 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None },
475 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None },
476 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None },
477 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, None },
478 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None },
479 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_STENCIL_SIZE, 1, None },
480 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, None },
481 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, None },
482 { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None },
483 { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None },
484 { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None },
485 { GLX_RGBA, GLX_DEPTH_SIZE, 8, None }
486 #undef R
487 #undef G
488 #undef B
490 for( unsigned int i = 0;
491 i < sizeof( attribs ) / sizeof( attribs[ 0 ] );
492 ++i )
494 if( XVisualInfo* info = glXChooseVisual( x11Info().display(), x11Info().screen(), attribs[ i ] ))
496 visual = info->visual;
497 depth = info->depth;
498 static Colormap colormap = 0;
499 if( colormap != 0 )
500 XFreeColormap( x11Info().display(), colormap );
501 colormap = XCreateColormap( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()), visual, AllocNone );
502 attrs.colormap = colormap;
503 flags |= CWColormap;
504 XFree( info );
505 break;
509 #endif
510 attrs.override_redirect = 1;
511 hide();
512 Window w = XCreateWindow( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()),
513 x(), y(), width(), height(), 0, depth, InputOutput, visual, flags, &attrs );
515 create( w, false, true );
517 // Some xscreensaver hacks check for this property
518 const char *version = "KDE 4.0";
519 XChangeProperty (QX11Info::display(), winId(),
520 gXA_SCREENSAVER_VERSION, XA_STRING, 8, PropModeReplace,
521 (unsigned char *) version, strlen(version));
524 XSetWindowAttributes attr;
525 attr.event_mask = KeyPressMask | ButtonPressMask | PointerMotionMask |
526 VisibilityChangeMask | ExposureMask;
527 XChangeWindowAttributes(QX11Info::display(), winId(),
528 CWEventMask, &attr);
530 // erase();
532 // set NoBackground so that the saver can capture the current
533 // screen state if necessary
534 setAttribute(Qt::WA_PaintOnScreen, true);
535 setAttribute(Qt::WA_NoSystemBackground, true);
536 setAttribute(Qt::WA_PaintOutsidePaintEvent, true); // for bitBlt in resume()
538 setCursor( Qt::BlankCursor );
540 kDebug(1204) << "Saver window Id: " << winId();
543 //---------------------------------------------------------------------------
545 // Hide the screensaver window
547 void LockProcess::hideSaverWindow()
549 hide();
550 lower();
551 removeVRoot(winId());
552 XDeleteProperty(QX11Info::display(), winId(), gXA_SCREENSAVER_VERSION);
553 if ( gVRoot ) {
554 unsigned long vroot_data[1] = { gVRootData };
555 XChangeProperty(QX11Info::display(), gVRoot, gXA_VROOT, XA_WINDOW, 32,
556 PropModeReplace, (unsigned char *)vroot_data, 1);
557 gVRoot = 0;
559 XSync(QX11Info::display(), False);
562 //---------------------------------------------------------------------------
563 static int ignoreXError(Display *, XErrorEvent *)
565 return 0;
568 //---------------------------------------------------------------------------
570 // Save the current virtual root window
572 void LockProcess::saveVRoot()
574 Window rootReturn, parentReturn, *children;
575 unsigned int numChildren;
576 QX11Info info;
577 Window root = RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info.screen()));
579 gVRoot = 0;
580 gVRootData = 0;
582 int (*oldHandler)(Display *, XErrorEvent *);
583 oldHandler = XSetErrorHandler(ignoreXError);
585 if (XQueryTree(QX11Info::display(), root, &rootReturn, &parentReturn,
586 &children, &numChildren))
588 for (unsigned int i = 0; i < numChildren; i++)
590 Atom actual_type;
591 int actual_format;
592 unsigned long nitems, bytesafter;
593 unsigned char *newRoot = 0;
595 if ((XGetWindowProperty(QX11Info::display(), children[i], gXA_VROOT, 0, 1,
596 False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytesafter,
597 &newRoot) == Success) && newRoot)
599 gVRoot = children[i];
600 Window *dummy = (Window*)newRoot;
601 gVRootData = *dummy;
602 XFree ((char*) newRoot);
603 break;
606 if (children)
608 XFree((char *)children);
612 XSetErrorHandler(oldHandler);
615 //---------------------------------------------------------------------------
617 // Set the virtual root property
619 void LockProcess::setVRoot(Window win, Window vr)
621 if (gVRoot)
622 removeVRoot(gVRoot);
624 QX11Info info;
625 unsigned long rw = RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info.screen()));
626 unsigned long vroot_data[1] = { vr };
628 Window rootReturn, parentReturn, *children;
629 unsigned int numChildren;
630 Window top = win;
631 while (1) {
632 XQueryTree(QX11Info::display(), top , &rootReturn, &parentReturn,
633 &children, &numChildren);
634 if (children)
635 XFree((char *)children);
636 if (parentReturn == rw) {
637 break;
638 } else
639 top = parentReturn;
642 XChangeProperty(QX11Info::display(), top, gXA_VROOT, XA_WINDOW, 32,
643 PropModeReplace, (unsigned char *)vroot_data, 1);
646 //---------------------------------------------------------------------------
648 // Remove the virtual root property
650 void LockProcess::removeVRoot(Window win)
652 XDeleteProperty (QX11Info::display(), win, gXA_VROOT);
655 //---------------------------------------------------------------------------
657 // Grab the keyboard. Returns true on success
659 bool LockProcess::grabKeyboard()
661 int rv = XGrabKeyboard( QX11Info::display(), QApplication::desktop()->winId(),
662 True, GrabModeAsync, GrabModeAsync, CurrentTime );
664 return (rv == GrabSuccess);
667 #define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \
668 EnterWindowMask | LeaveWindowMask
670 //---------------------------------------------------------------------------
672 // Grab the mouse. Returns true on success
674 bool LockProcess::grabMouse()
676 int rv = XGrabPointer( QX11Info::display(), QApplication::desktop()->winId(),
677 True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None,
678 QCursor(Qt::BlankCursor).handle(), CurrentTime );
680 return (rv == GrabSuccess);
683 //---------------------------------------------------------------------------
685 // Grab keyboard and mouse. Returns true on success.
687 bool LockProcess::grabInput()
689 XSync(QX11Info::display(), False);
691 if (!grabKeyboard())
693 sleep(1);
694 if (!grabKeyboard())
696 return false;
700 if (!grabMouse())
702 sleep(1);
703 if (!grabMouse())
705 XUngrabKeyboard(QX11Info::display(), CurrentTime);
706 return false;
710 lockXF86();
712 return true;
715 //---------------------------------------------------------------------------
717 // Release mouse an keyboard grab.
719 void LockProcess::ungrabInput()
721 XUngrabKeyboard(QX11Info::display(), CurrentTime);
722 XUngrabPointer(QX11Info::display(), CurrentTime);
723 unlockXF86();
726 //---------------------------------------------------------------------------
728 // Start the screen saver.
730 bool LockProcess::startSaver()
732 if (!child_saver && !grabInput())
734 kWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" ;
735 return false;
737 mBusy = false;
739 saveVRoot();
741 if (mParent) {
742 QSocketNotifier *notifier = new QSocketNotifier(mParent, QSocketNotifier::Read, this);
743 connect(notifier, SIGNAL( activated (int)), SLOT( quitSaver()));
745 createSaverWindow();
746 move(0, 0);
747 show();
748 setCursor( Qt::BlankCursor );
750 raise();
751 XSync(QX11Info::display(), False);
753 setVRoot( winId(), winId() );
754 startHack();
755 startPlasma();
756 return true;
759 //---------------------------------------------------------------------------
761 // Stop the screen saver.
763 void LockProcess::stopSaver()
765 kDebug(1204) << "LockProcess: stopping saver";
766 resume( true );
767 stopPlasma();
768 stopHack();
769 hideSaverWindow();
770 mVisibility = false;
771 if (!child_saver) {
772 if (mLocked)
773 KDisplayManager().setLock( false );
774 ungrabInput();
775 const char *out = "GOAWAY!";
776 for (QList<int>::ConstIterator it = child_sockets.constBegin(); it != child_sockets.constEnd(); ++it)
777 write(*it, out, sizeof(out));
781 // private static
782 QVariant LockProcess::getConf(void *ctx, const char *key, const QVariant &dflt)
784 LockProcess *that = (LockProcess *)ctx;
785 QString fkey = QLatin1String( key ) + '=';
786 for (QStringList::ConstIterator it = that->mPluginOptions.constBegin();
787 it != that->mPluginOptions.constEnd(); ++it)
788 if ((*it).startsWith( fkey ))
789 return (*it).mid( fkey.length() );
790 return dflt;
793 void LockProcess::cantLock( const QString &txt)
795 msgBox( 0, QMessageBox::Critical, i18n("Will not lock the session, as unlocking would be impossible:\n") + txt );
798 #if 0 // placeholders for later
799 i18n("Cannot start <i>kcheckpass</i>.");
800 i18n("<i>kcheckpass</i> is unable to operate. Possibly it is not setuid root.");
801 #endif
803 //---------------------------------------------------------------------------
805 // Make the screen saver password protected.
807 bool LockProcess::startLock()
809 if (loadGreetPlugin()) {
810 mLocked = true;
811 KDisplayManager().setLock(true);
812 lockPlasma();
813 return true;
815 return false;
818 bool LockProcess::loadGreetPlugin()
820 if (greetPlugin.library) {
821 //we were locked once before, so all the plugin loading's done already
822 //FIXME should I be unloading the plugin on unlock instead?
823 return true;
825 for (QStringList::ConstIterator it = mPlugins.constBegin(); it != mPlugins.constEnd(); ++it) {
826 GreeterPluginHandle plugin;
827 KLibrary *lib = new KLibrary( (*it)[0] == '/' ? *it : "kgreet_" + *it );
828 if (lib->fileName().isEmpty()) {
829 kWarning(1204) << "GreeterPlugin " << *it << " does not exist" ;
830 delete lib;
831 continue;
833 if (!lib->load()) {
834 kWarning(1204) << "Cannot load GreeterPlugin " << *it << " (" << lib->fileName() << ")" ;
835 delete lib;
836 continue;
838 plugin.library = lib;
839 plugin.info = (KGreeterPluginInfo *)lib->resolveSymbol( "kgreeterplugin_info" );
840 if (!plugin.info ) {
841 kWarning(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") is no valid greet widget plugin" ;
842 lib->unload();
843 delete lib;
844 continue;
846 if (plugin.info->method && !mMethod.isEmpty() && mMethod != plugin.info->method) {
847 kDebug(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") serves " << plugin.info->method << ", not " << mMethod;
848 lib->unload();
849 delete lib;
850 continue;
852 if (!plugin.info->init( mMethod, getConf, this )) {
853 kDebug(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") refuses to serve " << mMethod;
854 lib->unload();
855 delete lib;
856 continue;
858 kDebug(1204) << "GreeterPlugin " << *it << " (" << plugin.info->method << ", " << plugin.info->name << ") loaded";
859 greetPlugin = plugin;
860 return true;
862 cantLock( i18n("No appropriate greeter plugin configured.") );
863 return false;
866 //---------------------------------------------------------------------------
870 bool LockProcess::startHack()
872 kDebug(1204) << "Starting hack:" << mSaverExec;
874 if (mSaverExec.isEmpty() || mForbidden)
876 hackExited();
877 return false;
880 QHash<QChar, QString> keyMap;
881 keyMap.insert('w', QString::number(winId()));
882 mHackProc << KShell::splitArgs(KMacroExpander::expandMacrosShellQuote(mSaverExec, keyMap));
884 mHackProc.start();
885 if (mHackProc.waitForStarted())
887 #ifdef HAVE_SETPRIORITY
888 setpriority(PRIO_PROCESS, mHackProc.pid(), mPriority);
889 #endif
890 return true;
893 hackExited();
894 return false;
897 //---------------------------------------------------------------------------
899 void LockProcess::stopHack()
901 if (mHackProc.state() != QProcess::NotRunning)
903 mHackProc.terminate();
904 if (!mHackProc.waitForFinished(10000))
906 mHackProc.kill();
911 //---------------------------------------------------------------------------
913 void LockProcess::hackExited()
915 // Hack exited while we're supposed to be saving the screen.
916 // Make sure the saver window is black.
917 XSetWindowBackground(QX11Info::display(), winId(), 0);
918 XClearWindow(QX11Info::display(), winId());
921 bool LockProcess::startPlasma()
923 if (!mPlasmaEnabled) {
924 return false;
927 if (mSetupMode) {
928 mSuppressUnlock.start(mSuppressUnlockTimeout);
929 XChangeActivePointerGrab(QX11Info::display(), GRABEVENTS,
930 QCursor(Qt::ArrowCursor).handle(), CurrentTime);
933 kDebug() << "looking for plasma-overlay";
934 if (!mPlasmaDBus) {
935 //try to get it, in case it's already running somehow
936 //FIXME I don't like hardcoded strings
937 //mPlasmaDBus = new QDBusInterface("org.kde.plasma-overlay", "/MainApplication", QString(),
938 mPlasmaDBus = new org::kde::plasmaoverlay::App("org.kde.plasma-overlay", "/App",
939 QDBusConnection::sessionBus(), this);
940 //FIXME this might-already-be-running stuff seems really really Wrong.
943 if (mPlasmaDBus->isValid()) {
944 kDebug() << "weird, plasma-overlay is already running";
945 mPlasmaDBus->call(QDBus::NoBlock, "setup", mSetupMode);
946 return true;
949 kDebug () << "...not found" << "starting plasma-overlay";
950 delete mPlasmaDBus;
951 mPlasmaDBus = 0;
953 connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceOwnerChanged(QString, QString, QString)),
954 this, SLOT(newService(QString, QString, QString)));
956 KProcess *plasmaProc = new KProcess;
957 plasmaProc->setProgram("plasma-overlay");
958 if (mSetupMode) {
959 *plasmaProc << "--setup";
962 //make sure it goes away when it's done (and not before)
963 connect(plasmaProc, SIGNAL(finished(int,QProcess::ExitStatus)), plasmaProc, SLOT(deleteLater()));
965 plasmaProc->start();
966 kDebug() << "process begun";
968 //plasma gets 15 seconds to load, or we assume it failed
969 QTimer::singleShot(15 * 1000, this, SLOT(checkPlasma()));
970 return true;
973 void LockProcess::checkPlasma()
975 if (!mPlasmaEnabled) {
976 kDebug() << "You're Doing It Wrong!";
977 return;
979 if (mPlasmaDBus && mPlasmaDBus->isValid()) {
980 //hooray, looks like it started ok
981 kDebug() << "success!";
982 //...but just in case, make sure we're not waiting on it
983 mSetupMode = false;
984 return;
987 kDebug() << "ohnoes. plasma = teh fail.";
988 disablePlasma();
991 bool LockProcess::isPlasmaValid()
993 //FIXME I'm assuming that if it's valid, calls will succeed. so if that's not the case we'll
994 //need to change things so that plasma's disabled properly if it fails
995 //damn. isValid is not quite enough. a call may still fail, and then we need to bail.
996 if (!(mPlasmaEnabled && mPlasmaDBus)) {
997 return false; //no plasma, at least not yet
999 if (mPlasmaDBus->isValid()) {
1000 return true;
1002 //oh crap, it ran away on us.
1003 disablePlasma();
1004 return false;
1007 void LockProcess::disablePlasma()
1009 kDebug();
1010 mPlasmaEnabled = false;
1011 mSetupMode = false;
1012 mSuppressUnlock.stop(); //FIXME we might need to start the lock timer ala deactivatePlasma()
1013 //actually we could be lazy and just call deactivatePlasma() TODO check that this'll really work
1014 delete mPlasmaDBus;
1015 mPlasmaDBus=0;
1018 void LockProcess::stopPlasma()
1020 if (mPlasmaDBus && mPlasmaDBus->isValid()) {
1021 mPlasmaDBus->call(QDBus::NoBlock, "quit");
1022 } else {
1023 kDebug() << "cannot stop plasma-overlay";
1027 void LockProcess::newService(QString name, QString oldOwner, QString newOwner)
1029 Q_UNUSED(oldOwner);
1030 if (name != "org.kde.plasma-overlay") {
1031 return;
1034 if (mPlasmaDBus) {
1035 if (newOwner.isEmpty()) {
1036 kDebug() << "plasma ran away?";
1037 disablePlasma();
1038 } else {
1039 kDebug() << "I'm confused!!";
1041 return;
1044 kDebug() << "plasma! yaay!";
1045 mPlasmaDBus = new org::kde::plasmaoverlay::App(name, "/App",
1046 QDBusConnection::sessionBus(), this);
1048 //XXX this isn't actually used any more iirc
1049 connect(mPlasmaDBus, SIGNAL(hidden()), SLOT(unSuppressUnlock()));
1051 if (!mDialogs.isEmpty()) {
1052 //whoops, activation probably failed earlier
1053 mPlasmaDBus->call(QDBus::NoBlock, "setActive", true);
1057 void LockProcess::deactivatePlasma()
1059 if (isPlasmaValid()) {
1060 mPlasmaDBus->call(QDBus::NoBlock, "setActive", false);
1062 if (!mLocked && mLockGrace >=0) {
1063 QTimer::singleShot(mLockGrace, this, SLOT(startLock())); //this is only ok because any activity will quit
1067 void LockProcess::lockPlasma()
1069 if (isPlasmaValid()) {
1070 mPlasmaDBus->call(QDBus::NoBlock, "lock");
1074 void LockProcess::unSuppressUnlock()
1076 //note: suppressing unlock also now means suppressing quit-on-activity
1077 //maybe some var renaming is in order.
1078 mSuppressUnlock.stop();
1081 void LockProcess::quit()
1083 mSuppressUnlock.stop();
1084 if (!mLocked || checkPass()) {
1085 quitSaver();
1089 void LockProcess::suspend()
1091 if( !mSuspended && mHackProc.state() == QProcess::Running )
1093 ::kill(mHackProc.pid(), SIGSTOP);
1094 QApplication::syncX();
1095 mSavedScreen = QPixmap::grabWindow( winId());
1097 mSuspended = true;
1100 void LockProcess::resume( bool force )
1102 if( !force && (!mDialogs.isEmpty() || !mVisibility ))
1103 return; // no resuming with dialog visible or when not visible
1104 if( mSuspended && mHackProc.state() == QProcess::Running )
1106 XForceScreenSaver(QX11Info::display(), ScreenSaverReset );
1107 QPainter p( this );
1108 p.drawPixmap( 0, 0, mSavedScreen );
1109 p.end();
1110 mSavedScreen = QPixmap();
1111 QApplication::syncX();
1112 ::kill(mHackProc.pid(), SIGCONT);
1114 mSuspended = false;
1117 //---------------------------------------------------------------------------
1119 // Show the password dialog
1120 // This is called only in the master process
1122 bool LockProcess::checkPass()
1124 killTimer(mAutoLogoutTimerId);
1126 if (isPlasmaValid()) {
1127 mPlasmaDBus->call(QDBus::NoBlock, "setActive", true);
1130 PasswordDlg passDlg( this, &greetPlugin);
1131 int ret = execDialog( &passDlg );
1133 if (isPlasmaValid()) {
1134 if (ret == QDialog::Rejected) {
1135 mSuppressUnlock.start(mSuppressUnlockTimeout);
1136 } else if (ret == TIMEOUT_CODE) {
1137 mPlasmaDBus->call(QDBus::NoBlock, "setActive", false);
1141 XWindowAttributes rootAttr;
1142 XGetWindowAttributes(QX11Info::display(), QX11Info::appRootWindow(), &rootAttr);
1143 if(( rootAttr.your_event_mask & SubstructureNotifyMask ) == 0 )
1145 kWarning() << "ERROR: Something removed SubstructureNotifyMask from the root window!!!" ;
1146 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(),
1147 SubstructureNotifyMask | rootAttr.your_event_mask );
1150 return ret == QDialog::Accepted;
1153 bool LockProcess::checkPass(const QString &reason)
1155 if (! mLocked) {
1156 //we were never locked... how can we unlock?!
1157 //if anyone finds a use case for checking the password while unlocked, they'll have to load
1158 //the greetplugin n'stuff
1159 return false;
1161 PasswordDlg passDlg(this, &greetPlugin, reason);
1162 int ret = execDialog( &passDlg );
1163 kDebug() << ret;
1165 //FIXME do we need to copy&paste that SubstructureNotifyMask code above?
1166 if (ret == QDialog::Accepted) {
1167 //we don't quit on a custom checkpass, but we do unlock
1168 //so that the user doesn't have to type their password twice
1169 mLocked = false;
1170 KDisplayManager().setLock(false);
1171 //FIXME while suppressUnlock *should* always be running, if it isn't
1172 //(say if someone's doing things they shouldn't with dbus) then it won't get started by this
1173 //which means that a successful unlock will never re-lock
1174 //in fact, the next bit of activity would lead to the screensaver quitting.
1175 //possible solutions:
1176 //-treat this function like activity: quit if already unlocked, ensure suppress is started
1177 //if we're locked and the dialog's rejected
1178 //-return true if already unlocked, without doing anything, same as above if locked
1179 //-let it quit, and tell people not to do such silly things :P
1180 return true;
1182 return false;
1185 static void fakeFocusIn( WId window )
1187 // We have keyboard grab, so this application will
1188 // get keyboard events even without having focus.
1189 // Fake FocusIn to make Qt realize it has the active
1190 // window, so that it will correctly show cursor in the dialog.
1191 XEvent ev;
1192 memset(&ev, 0, sizeof(ev));
1193 ev.xfocus.display = QX11Info::display();
1194 ev.xfocus.type = FocusIn;
1195 ev.xfocus.window = window;
1196 ev.xfocus.mode = NotifyNormal;
1197 ev.xfocus.detail = NotifyAncestor;
1198 XSendEvent( QX11Info::display(), window, False, NoEventMask, &ev );
1201 bool LockProcess::eventFilter(QObject *o, QEvent *e)
1203 if (e->type() == QEvent::Resize) {
1204 QWidget *w = static_cast<QWidget *>(o);
1205 mFrames.value(w)->resize(w->size());
1207 return false;
1210 int LockProcess::execDialog( QDialog *dlg )
1212 QFrame *winFrame = new QFrame( dlg );
1213 winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised );
1214 winFrame->setLineWidth( 2 );
1215 winFrame->lower();
1216 mFrames.insert(dlg, winFrame);
1217 dlg->installEventFilter(this);
1219 dlg->adjustSize();
1221 QRect rect = dlg->geometry();
1222 rect.moveCenter(KGlobalSettings::desktopGeometry(QCursor::pos()).center());
1223 dlg->move( rect.topLeft() );
1225 if (mDialogs.isEmpty())
1227 suspend();
1228 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS,
1229 QCursor(Qt::ArrowCursor).handle(), CurrentTime);
1231 mDialogs.prepend( dlg );
1232 fakeFocusIn( dlg->winId());
1233 int rt = dlg->exec();
1234 int pos = mDialogs.indexOf( dlg );
1235 if (pos != -1)
1236 mDialogs.remove( pos );
1237 if( mDialogs.isEmpty() ) {
1238 resume( false );
1240 updateFocus();
1242 dlg->removeEventFilter(this);
1243 mFrames.remove(dlg);
1245 return rt;
1248 void LockProcess::preparePopup()
1250 QWidget *dlg = (QWidget *)sender();
1251 mDialogs.prepend( dlg );
1252 fakeFocusIn( dlg->winId() );
1255 void LockProcess::cleanupPopup()
1257 QWidget *dlg = (QWidget *)sender();
1259 int pos = mDialogs.indexOf( dlg );
1260 mDialogs.remove( pos );
1261 updateFocus();
1264 void LockProcess::updateFocus()
1266 if (mDialogs.isEmpty()) {
1267 if (mForeignInputWindows.isEmpty()) {
1268 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS,
1269 QCursor(Qt::BlankCursor).handle(), CurrentTime);
1270 } else {
1271 fakeFocusIn(mForeignInputWindows.first());
1273 } else {
1274 fakeFocusIn(mDialogs.first()->winId());
1278 //---------------------------------------------------------------------------
1280 // X11 Event.
1282 bool LockProcess::x11Event(XEvent *event)
1284 bool ret = false;
1285 switch (event->type)
1287 case ButtonPress:
1288 if (!mDialogs.isEmpty() && event->xbutton.window == event->xbutton.root) {
1289 //kDebug() << "close" << mDialogs.first()->effectiveWinId();
1290 KDialog *dlg = qobject_cast<KDialog*>(mDialogs.first());
1291 if (dlg) {
1292 //kDebug() << "casting success";
1293 dlg->reject();
1295 break;
1297 case KeyPress:
1298 case MotionNotify:
1299 if (mBusy || !mDialogs.isEmpty()) {
1300 //kDebug() << "busy";
1301 //FIXME shouldn't we be resetting some timers?
1302 break;
1304 mBusy = true;
1305 //something happened. do we quit, ask for a password or forward it to plasma?
1306 //if we're supposed to be forwarding, we check that there's actually a plasma window up
1307 //so that the user isn't trapped if plasma crashes or is slow to load.
1308 //however, if plasma started in setup mode, we don't want to let anything happen until
1309 //it has a chance to load.
1310 //note: mSetupMode should end when we either get a winid or hit the checkPlasma timeout
1311 if (mSuppressUnlock.isActive() && (mSetupMode || !mForeignInputWindows.isEmpty())) {
1312 mSuppressUnlock.start(); //help, help, I'm being suppressed!
1313 } else if (!mLocked || checkPass()) {
1314 quitSaver();
1315 mBusy = false;
1316 return true; //it's better not to forward any input while quitting, right?
1318 if (mAutoLogoutTimerId) // we need to restart the auto logout countdown
1320 killTimer(mAutoLogoutTimerId);
1321 mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000);
1323 mBusy = false;
1324 ret = true;
1325 break;
1327 case VisibilityNotify:
1328 if( event->xvisibility.window == winId())
1329 { // mVisibility == false means the screensaver is not visible at all
1330 // e.g. when switched to text console
1331 // ...or when plasma's over it non-compositely?
1332 // hey, this gives me free "suspend saver when plasma obscures it"
1333 mVisibility = !(event->xvisibility.state == VisibilityFullyObscured);
1334 if (!mVisibility) {
1335 mSuspendTimer.start(2000);
1336 kDebug() << "fully obscured";
1337 } else {
1338 kDebug() << "not fully obscured";
1339 mSuspendTimer.stop();
1340 resume( false );
1342 if (mForeignWindows.isEmpty() && event->xvisibility.state != VisibilityUnobscured) {
1343 kDebug() << "no plasma; saver obscured";
1344 stayOnTop();
1346 } else if (!mForeignWindows.isEmpty() && event->xvisibility.window == mForeignWindows.last() &&
1347 event->xvisibility.state != VisibilityUnobscured) {
1348 //FIXME now that we have several plasma winids this doesn't feel valid
1349 //but I don't know what to do about it!
1350 kDebug() << "plasma obscured!";
1351 stayOnTop();
1353 break;
1355 case ConfigureNotify: // from SubstructureNotifyMask on the root window
1356 if(event->xconfigure.event == QX11Info::appRootWindow()) {
1357 //kDebug() << "ConfigureNotify:";
1358 //the stacking order changed, so let's change the stacking order!
1359 stayOnTop();
1361 break;
1362 case MapNotify: // from SubstructureNotifyMask on the root window
1363 if( event->xmap.event == QX11Info::appRootWindow()) {
1364 kDebug() << "MapNotify:" << event->xmap.window;
1365 WindowType type = windowType(event->xmap.window);
1366 if (type != IgnoreWindow) {
1367 if (mForeignWindows.contains(event->xmap.window)) {
1368 kDebug() << "uhoh! duplicate!";
1369 } else {
1370 //ordered youngest-on-top
1371 mForeignWindows.prepend(event->xmap.window);
1373 if (type & InputWindow) {
1374 kDebug() << "input window";
1375 if (mForeignInputWindows.contains(event->xmap.window)) {
1376 kDebug() << "uhoh! duplicate again"; //never happens
1377 } else {
1378 //ordered youngest-on-top
1379 mForeignInputWindows.prepend(event->xmap.window);
1380 fakeFocusIn(event->xmap.window);
1382 mSetupMode = false; //no more waiting for plasma
1385 stayOnTop();
1387 break;
1388 case UnmapNotify:
1389 if (event->xmap.event == QX11Info::appRootWindow()) {
1390 kDebug() << "UnmapNotify:" << event->xunmap.window;
1391 mForeignWindows.removeAll(event->xunmap.window);
1392 if (mForeignInputWindows.removeAll(event->xunmap.window)) {
1393 updateFocus();
1398 // We have grab with the grab window being the root window.
1399 // This results in key events being sent to the root window,
1400 // but they should be sent to the dialog if it's visible.
1401 // It could be solved by setFocus() call, but that would mess
1402 // the focus after this process exits.
1403 // Qt seems to be quite hard to persuade to redirect the event,
1404 // so let's simply dupe it with correct destination window,
1405 // and ignore the original one.
1406 if (!mDialogs.isEmpty()) {
1407 if ((event->type == KeyPress || event->type == KeyRelease) &&
1408 event->xkey.window != mDialogs.first()->winId()) {
1409 //kDebug() << "forward to dialog";
1410 XEvent ev2 = *event;
1411 ev2.xkey.window = ev2.xkey.subwindow = mDialogs.first()->winId();
1412 qApp->x11ProcessEvent( &ev2 );
1413 ret = true;
1415 } else if (!mForeignInputWindows.isEmpty()) {
1416 //when there are no dialogs, forward some events to plasma
1417 switch (event->type) {
1418 case KeyPress:
1419 case KeyRelease:
1420 case ButtonPress:
1421 case ButtonRelease:
1422 case MotionNotify:
1424 //kDebug() << "forward to plasma";
1425 XEvent ev2 = *event;
1426 ev2.xkey.window = ev2.xkey.subwindow = mForeignInputWindows.first();
1427 XSendEvent(QX11Info::display(), ev2.xkey.window, False, NoEventMask, &ev2);
1428 ret = true;
1430 default:
1431 break;
1435 return ret;
1438 LockProcess::WindowType LockProcess::windowType(WId id)
1440 Atom tag = XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False);
1441 Atom actualType;
1442 int actualFormat;
1443 unsigned long nitems, remaining;
1444 unsigned char *data = 0;
1445 Display *display = QX11Info::display();
1447 int result = XGetWindowProperty(display, id, tag, 0, 1, False, tag, &actualType,
1448 &actualFormat, &nitems, &remaining, &data);
1450 //kDebug() << (result == Success) << (actualType == tag);
1451 WindowType type = IgnoreWindow;
1452 if (result == Success && actualType == tag) {
1453 if (nitems != 1 || actualFormat != 8) {
1454 kDebug() << "malformed property";
1455 } else {
1456 kDebug() << "i can haz plasma window?" << data[0];
1457 switch (data[0]) {
1458 case 0: //FIXME magic numbers
1459 type = SimpleWindow;
1460 break;
1461 case 1:
1462 type = InputWindow;
1463 break;
1464 case 2:
1465 type = DefaultWindow;
1466 break;
1470 if (data) {
1471 XFree(data);
1473 return type;
1474 /* if (result != Success) {
1475 return false;
1477 if (actualType == tag) {
1478 return true;
1480 //managed windows will have a pesky frame we have to bypass
1481 XWindowAttributes attr;
1482 XGetWindowAttributes(display, id, &attr);
1483 if (!attr.override_redirect) {
1484 //check the real client window
1485 if (Window client = XmuClientWindow(display, id)) {
1486 result = XGetWindowProperty(display, client, tag, 0, 0, False, tag, &actualType,
1487 &actualFormat, &nitems, &remaining, &data);
1488 kDebug() << (result == Success) << (actualType == tag);
1489 if (data) {
1490 XFree(data);
1492 return (result == Success) && (actualType == tag);
1495 return false;*/
1498 void LockProcess::stayOnTop()
1500 if(!(mDialogs.isEmpty() && mForeignWindows.isEmpty()))
1502 // this restacking is written in a way so that
1503 // if the stacking positions actually don't change,
1504 // all restacking operations will be no-op,
1505 // and no ConfigureNotify will be generated,
1506 // thus avoiding possible infinite loops
1507 Window* stack = new Window[ mDialogs.count() + mForeignWindows.count() + 1 ];
1508 int count = 0;
1509 if (!mDialogs.isEmpty()) {
1510 XRaiseWindow( QX11Info::display(), mDialogs.first()->winId()); // raise topmost
1511 // and stack others below it
1512 for( QVector< QWidget* >::ConstIterator it = mDialogs.constBegin();
1513 it != mDialogs.constEnd();
1514 ++it )
1515 stack[ count++ ] = (*it)->winId();
1516 } else {
1517 XRaiseWindow( QX11Info::display(), mForeignWindows.first()); // raise topmost
1519 //now the plasma stuff below the dialogs
1520 foreach (const WId w, mForeignWindows) {
1521 stack[count++] = w;
1523 //finally, the saver window
1524 stack[ count++ ] = winId();
1525 XRestackWindows( x11Info().display(), stack, count );
1526 //kDebug() << "restacked" << count;
1527 delete[] stack;
1528 } else {
1529 XRaiseWindow(QX11Info::display(), winId());
1533 void LockProcess::checkDPMSActive()
1535 #ifdef HAVE_DPMS
1536 BOOL on;
1537 CARD16 state;
1538 DPMSInfo(QX11Info::display(), &state, &on);
1539 //kDebug() << "checkDPMSActive " << on << " " << state;
1540 if (state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff)
1542 suspend();
1543 } else if ( mSuspended )
1545 resume( true );
1547 #endif
1550 #if defined(HAVE_XF86MISC) && defined(HAVE_XF86MISCSETGRABKEYSSTATE)
1551 // see http://cvsweb.xfree86.org/cvsweb/xc/programs/Xserver/hw/xfree86/common/xf86Events.c#rev3.113
1552 // This allows enabling the "Allow{Deactivate/Closedown}Grabs" options in XF86Config,
1553 // and kscreenlocker will still lock the session.
1554 static enum { Unknown, Yes, No } can_do_xf86_lock = Unknown;
1555 void LockProcess::lockXF86()
1557 if( can_do_xf86_lock == Unknown )
1559 int major, minor;
1560 if( XF86MiscQueryVersion( QX11Info::display(), &major, &minor )
1561 && (major > 0 || minor >= 5) )
1562 can_do_xf86_lock = Yes;
1563 else
1564 can_do_xf86_lock = No;
1566 if( can_do_xf86_lock != Yes )
1567 return;
1568 if( mRestoreXF86Lock )
1569 return;
1570 if( XF86MiscSetGrabKeysState( QX11Info::display(), False ) != MiscExtGrabStateSuccess )
1571 return;
1572 // success
1573 mRestoreXF86Lock = true;
1576 void LockProcess::unlockXF86()
1578 if( can_do_xf86_lock != Yes )
1579 return;
1580 if( !mRestoreXF86Lock )
1581 return;
1582 XF86MiscSetGrabKeysState( QX11Info::display(), True );
1583 mRestoreXF86Lock = false;
1585 #else
1586 void LockProcess::lockXF86()
1590 void LockProcess::unlockXF86()
1593 #endif
1595 void LockProcess::msgBox( QWidget *parent, QMessageBox::Icon type, const QString &txt )
1597 QDialog box( parent, Qt::X11BypassWindowManagerHint );
1598 box.setModal( true );
1600 QLabel *label1 = new QLabel( &box );
1601 label1->setPixmap( QMessageBox::standardIcon( type ) );
1602 QLabel *label2 = new QLabel( txt, &box );
1603 KPushButton *button = new KPushButton( KStandardGuiItem::ok(), &box );
1604 button->setDefault( true );
1605 button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) );
1606 connect( button, SIGNAL( clicked() ), &box, SLOT( accept() ) );
1608 QGridLayout *grid = new QGridLayout( &box );
1609 grid->setSpacing( 10 );
1610 grid->addWidget( label1, 0, 0, Qt::AlignCenter );
1611 grid->addWidget( label2, 0, 1, Qt::AlignCenter );
1612 grid->addWidget( button, 1, 0, 1, 2, Qt::AlignCenter );
1614 execDialog( &box );
1617 #include "lockprocess.moc"