check for plasma-overlay going away
[kdebase.git] / workspace / krunner / lock / lockprocess.cc
bloba6238e93e908f4bca196351cf9f5a69638bb86de
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 krunner_lock, 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
124 const int TIMEOUT_CODE = 2; //from PasswordDlg
126 //===========================================================================
128 // Screen saver handling process. Handles screensaver window,
129 // starting screensaver hacks, and password entry.f
131 LockProcess::LockProcess(bool child, bool useBlankOnly)
132 : QWidget(0L, Qt::X11BypassWindowManagerHint),
133 mLocked(false),
134 mBusy(false),
135 mPlasmaDBus(0),
136 mSetupMode(false),
137 mOpenGLVisual(false),
138 child_saver(child),
139 mParent(0),
140 mUseBlankOnly(useBlankOnly),
141 mSuspended(false),
142 mVisibility(false),
143 mRestoreXF86Lock(false),
144 mForbidden(false),
145 mAutoLogoutTimerId(0)
147 setObjectName("save window");
148 setupSignals();
150 new LockProcessAdaptor(this);
151 QDBusConnection::sessionBus().registerService("org.kde.krunner_lock");
152 QDBusConnection::sessionBus().registerObject("/LockProcess", this);
154 kapp->installX11EventFilter(this);
156 // Get root window size
157 XWindowAttributes rootAttr;
158 QX11Info info;
159 XGetWindowAttributes(QX11Info::display(), RootWindow(QX11Info::display(),
160 info.screen()), &rootAttr);
161 kapp->desktop(); // make Qt set its event mask on the root window first
162 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(),
163 SubstructureNotifyMask | rootAttr.your_event_mask );
164 #ifdef CHECK_XSELECTINPUT
165 check_xselectinput = true;
166 #endif
167 setGeometry(0, 0, rootAttr.width, rootAttr.height);
169 // virtual root property
170 gXA_VROOT = XInternAtom (QX11Info::display(), "__SWM_VROOT", False);
171 gXA_SCREENSAVER_VERSION = XInternAtom (QX11Info::display(), "_SCREENSAVER_VERSION", False);
173 connect(&mHackProc, SIGNAL(finished(int, QProcess::ExitStatus)),
174 SLOT(hackExited()));
176 mSuspendTimer.setSingleShot(true);
177 connect(&mSuspendTimer, SIGNAL(timeout()), SLOT(suspend()));
179 QStringList dmopt =
180 QString::fromLatin1( ::getenv( "XDM_MANAGED" )).split(QChar(','), QString::SkipEmptyParts);
181 for (QStringList::ConstIterator it = dmopt.begin(); it != dmopt.end(); ++it)
182 if ((*it).startsWith("method="))
183 mMethod = (*it).mid(7);
185 configure();
187 #ifdef HAVE_DPMS
188 if (mDPMSDepend) {
189 BOOL on;
190 CARD16 state;
191 DPMSInfo(QX11Info::display(), &state, &on);
192 if (on)
194 connect(&mCheckDPMS, SIGNAL(timeout()), SLOT(checkDPMSActive()));
195 // we can save CPU if we stop it as quickly as possible
196 // but we waste CPU if we check too often -> so take 10s
197 mCheckDPMS.start(10000);
200 #endif
202 greetPlugin.library = 0;
204 mSuppressUnlock.setSingleShot(true);
205 connect(&mSuppressUnlock, SIGNAL(timeout()), SLOT(deactivatePlasma()));
208 //---------------------------------------------------------------------------
210 // Destructor - usual cleanups.
212 LockProcess::~LockProcess()
214 if (greetPlugin.library) {
215 if (greetPlugin.info->done)
216 greetPlugin.info->done();
217 greetPlugin.library->unload();
221 static int signal_pipe[2];
223 static void sigterm_handler(int)
225 char tmp = 'T';
226 ::write( signal_pipe[1], &tmp, 1);
229 static void sighup_handler(int)
231 char tmp = 'H';
232 ::write( signal_pipe[1], &tmp, 1);
235 void LockProcess::timerEvent(QTimerEvent *ev)
237 if (ev->timerId() == mAutoLogoutTimerId)
239 killTimer(mAutoLogoutTimerId);
240 AutoLogout autologout(this);
241 execDialog(&autologout);
245 void LockProcess::setupSignals()
247 struct sigaction act;
248 // ignore SIGINT
249 act.sa_handler=SIG_IGN;
250 sigemptyset(&(act.sa_mask));
251 sigaddset(&(act.sa_mask), SIGINT);
252 act.sa_flags = 0;
253 sigaction(SIGINT, &act, 0L);
254 // ignore SIGQUIT
255 act.sa_handler=SIG_IGN;
256 sigemptyset(&(act.sa_mask));
257 sigaddset(&(act.sa_mask), SIGQUIT);
258 act.sa_flags = 0;
259 sigaction(SIGQUIT, &act, 0L);
260 // exit cleanly on SIGTERM
261 act.sa_handler= sigterm_handler;
262 sigemptyset(&(act.sa_mask));
263 sigaddset(&(act.sa_mask), SIGTERM);
264 act.sa_flags = 0;
265 sigaction(SIGTERM, &act, 0L);
266 // SIGHUP forces lock
267 act.sa_handler= sighup_handler;
268 sigemptyset(&(act.sa_mask));
269 sigaddset(&(act.sa_mask), SIGHUP);
270 act.sa_flags = 0;
271 sigaction(SIGHUP, &act, 0L);
273 pipe(signal_pipe);
274 QSocketNotifier* notif = new QSocketNotifier(signal_pipe[0], QSocketNotifier::Read, this);
275 connect( notif, SIGNAL(activated(int)), SLOT(signalPipeSignal()));
279 void LockProcess::signalPipeSignal()
281 char tmp;
282 ::read( signal_pipe[0], &tmp, 1);
283 if( tmp == 'T' )
284 quitSaver();
285 else if( tmp == 'H' ) {
286 if( !mLocked )
287 startLock();
291 //---------------------------------------------------------------------------
292 bool LockProcess::lock()
294 if (startSaver()) {
295 // In case of a forced lock we don't react to events during
296 // the dead-time to give the screensaver some time to activate.
297 // That way we don't accidentally show the password dialog before
298 // the screensaver kicks in because the user moved the mouse after
299 // selecting "lock screen", that looks really untidy.
300 mBusy = true;
301 if (startLock())
303 QTimer::singleShot(1000, this, SLOT(slotDeadTimePassed()));
304 return true;
306 stopSaver();
307 mBusy = false;
309 return false;
311 //---------------------------------------------------------------------------
312 void LockProcess::slotDeadTimePassed()
314 mBusy = false;
317 //---------------------------------------------------------------------------
318 bool LockProcess::defaultSave()
320 mLocked = false;
321 if (startSaver()) {
322 if (mLockGrace >= 0)
323 QTimer::singleShot(mLockGrace, this, SLOT(startLock()));
324 return true;
326 return false;
329 bool LockProcess::startSetup()
331 if (!mPlasmaEnabled) {
332 return defaultSave();
335 mLocked = false;
336 mSetupMode = true;
337 return startSaver();
338 //plasma startup will handle the suppressunlock bit
340 //---------------------------------------------------------------------------
341 bool LockProcess::dontLock()
343 mLocked = false;
344 return startSaver();
347 //---------------------------------------------------------------------------
348 void LockProcess::quitSaver()
350 stopSaver();
351 qApp->quit();
354 //---------------------------------------------------------------------------
356 // Read and apply configuration.
358 void LockProcess::configure()
360 // the configuration is stored in krunner's config file
361 if( KScreenSaverSettings::lock() ) {
362 mLockGrace = KScreenSaverSettings::lockGrace();
363 if (mLockGrace < 0)
364 mLockGrace = 0;
365 else if (mLockGrace > 300000)
366 mLockGrace = 300000; // 5 minutes, keep the value sane
367 } else {
368 mLockGrace = -1;
371 if ( KScreenSaverSettings::autoLogout() ) {
372 mAutoLogoutTimeout = KScreenSaverSettings::autoLogoutTimeout();
373 mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); // in milliseconds
376 #ifdef HAVE_DPMS
377 mDPMSDepend = KScreenSaverSettings::suspendWhenInvisible();
378 #endif
380 mPriority = KScreenSaverSettings::priority();
381 if (mPriority < 0) mPriority = 0;
382 if (mPriority > 19) mPriority = 19;
384 mSaver = KScreenSaverSettings::saver();
385 if (mSaver.isEmpty() || mUseBlankOnly) {
386 mSaver = "kblank.desktop";
389 readSaver();
391 mPlasmaEnabled = KScreenSaverSettings::plasmaEnabled();
393 mSuppressUnlockTimeout = qMax(0, KScreenSaverSettings::timeout() * 1000);
394 mSuppressUnlockTimeout = qMax(mSuppressUnlockTimeout, 30 * 1000); //min. 30 secs FIXME is this a good idea?
396 mPlugins = KScreenSaverSettings::pluginsUnlock();
397 if (mPlugins.isEmpty()) {
398 mPlugins << "classic" << "generic";
400 mPluginOptions = KScreenSaverSettings::pluginOptions();
403 //---------------------------------------------------------------------------
405 // Read the command line needed to run the screensaver given a .desktop file.
407 void LockProcess::readSaver()
409 if (!mSaver.isEmpty())
411 QString entryName = mSaver;
412 if( entryName.endsWith( ".desktop" ))
413 entryName = entryName.left( entryName.length() - 8 ); // strip it
414 KService::List offers = KServiceTypeTrader::self()->query( "ScreenSaver",
415 "DesktopEntryName == '" + entryName.toLower() + '\'' );
416 if( offers.count() == 0 )
418 kDebug(1204) << "Cannot find screesaver: " << mSaver;
419 return;
421 QString file = KStandardDirs::locate("services", offers.first()->entryPath());
423 bool opengl = KAuthorized::authorizeKAction("opengl_screensavers");
424 bool manipulatescreen = KAuthorized::authorizeKAction("manipulatescreen_screensavers");
425 KDesktopFile config( file );
426 KConfigGroup desktopGroup = config.desktopGroup();
427 if (!desktopGroup.readEntry("X-KDE-Type").toUtf8().isEmpty())
429 QString saverType = desktopGroup.readEntry("X-KDE-Type").toUtf8();
430 QStringList saverTypes = saverType.split( ";");
431 for (int i = 0; i < saverTypes.count(); i++)
433 if ((saverTypes[i] == "ManipulateScreen") && !manipulatescreen)
435 kDebug(1204) << "Screensaver is type ManipulateScreen and ManipulateScreen is forbidden";
436 mForbidden = true;
438 if ((saverTypes[i] == "OpenGL") && !opengl)
440 kDebug(1204) << "Screensaver is type OpenGL and OpenGL is forbidden";
441 mForbidden = true;
443 if (saverTypes[i] == "OpenGL")
445 mOpenGLVisual = true;
450 kDebug(1204) << "mForbidden: " << (mForbidden ? "true" : "false");
452 if (config.hasActionGroup("Root"))
454 mSaverExec = config.actionGroup("Root").readPathEntry("Exec", QString());
459 //---------------------------------------------------------------------------
461 // Create a window to draw our screen saver on.
463 void LockProcess::createSaverWindow()
465 Visual* visual = CopyFromParent;
466 int depth = CopyFromParent;
467 XSetWindowAttributes attrs;
468 int flags = CWOverrideRedirect;
469 #ifdef HAVE_GLXCHOOSEVISUAL
470 // this code is (partially) duplicated in kdebase/workspace/kcontrol/screensaver
471 if( mOpenGLVisual )
473 static int attribs[][ 15 ] =
475 #define R GLX_RED_SIZE
476 #define G GLX_GREEN_SIZE
477 #define B GLX_BLUE_SIZE
478 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None },
479 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None },
480 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None },
481 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, None },
482 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None },
483 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_STENCIL_SIZE, 1, None },
484 { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, None },
485 { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, None },
486 { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None },
487 { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None },
488 { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None },
489 { GLX_RGBA, GLX_DEPTH_SIZE, 8, None }
490 #undef R
491 #undef G
492 #undef B
494 for( unsigned int i = 0;
495 i < sizeof( attribs ) / sizeof( attribs[ 0 ] );
496 ++i )
498 if( XVisualInfo* info = glXChooseVisual( x11Info().display(), x11Info().screen(), attribs[ i ] ))
500 visual = info->visual;
501 depth = info->depth;
502 static Colormap colormap = 0;
503 if( colormap != 0 )
504 XFreeColormap( x11Info().display(), colormap );
505 colormap = XCreateColormap( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()), visual, AllocNone );
506 attrs.colormap = colormap;
507 flags |= CWColormap;
508 XFree( info );
509 break;
513 #endif
514 attrs.override_redirect = 1;
515 hide();
516 Window w = XCreateWindow( x11Info().display(), RootWindow( x11Info().display(), x11Info().screen()),
517 x(), y(), width(), height(), 0, depth, InputOutput, visual, flags, &attrs );
519 create( w, false, true );
521 // Some xscreensaver hacks check for this property
522 const char *version = "KDE 4.0";
523 XChangeProperty (QX11Info::display(), winId(),
524 gXA_SCREENSAVER_VERSION, XA_STRING, 8, PropModeReplace,
525 (unsigned char *) version, strlen(version));
528 XSetWindowAttributes attr;
529 attr.event_mask = KeyPressMask | ButtonPressMask | PointerMotionMask |
530 VisibilityChangeMask | ExposureMask;
531 XChangeWindowAttributes(QX11Info::display(), winId(),
532 CWEventMask, &attr);
534 // erase();
536 // set NoBackground so that the saver can capture the current
537 // screen state if necessary
538 setAttribute(Qt::WA_PaintOnScreen, true);
539 setAttribute(Qt::WA_NoSystemBackground, true);
540 setAttribute(Qt::WA_PaintOutsidePaintEvent, true); // for bitBlt in resume()
542 setCursor( Qt::BlankCursor );
544 kDebug(1204) << "Saver window Id: " << winId();
547 //---------------------------------------------------------------------------
549 // Hide the screensaver window
551 void LockProcess::hideSaverWindow()
553 hide();
554 lower();
555 removeVRoot(winId());
556 XDeleteProperty(QX11Info::display(), winId(), gXA_SCREENSAVER_VERSION);
557 if ( gVRoot ) {
558 unsigned long vroot_data[1] = { gVRootData };
559 XChangeProperty(QX11Info::display(), gVRoot, gXA_VROOT, XA_WINDOW, 32,
560 PropModeReplace, (unsigned char *)vroot_data, 1);
561 gVRoot = 0;
563 XSync(QX11Info::display(), False);
566 //---------------------------------------------------------------------------
567 static int ignoreXError(Display *, XErrorEvent *)
569 return 0;
572 //---------------------------------------------------------------------------
574 // Save the current virtual root window
576 void LockProcess::saveVRoot()
578 Window rootReturn, parentReturn, *children;
579 unsigned int numChildren;
580 QX11Info info;
581 Window root = RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info.screen()));
583 gVRoot = 0;
584 gVRootData = 0;
586 int (*oldHandler)(Display *, XErrorEvent *);
587 oldHandler = XSetErrorHandler(ignoreXError);
589 if (XQueryTree(QX11Info::display(), root, &rootReturn, &parentReturn,
590 &children, &numChildren))
592 for (unsigned int i = 0; i < numChildren; i++)
594 Atom actual_type;
595 int actual_format;
596 unsigned long nitems, bytesafter;
597 unsigned char *newRoot = 0;
599 if ((XGetWindowProperty(QX11Info::display(), children[i], gXA_VROOT, 0, 1,
600 False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytesafter,
601 &newRoot) == Success) && newRoot)
603 gVRoot = children[i];
604 Window *dummy = (Window*)newRoot;
605 gVRootData = *dummy;
606 XFree ((char*) newRoot);
607 break;
610 if (children)
612 XFree((char *)children);
616 XSetErrorHandler(oldHandler);
619 //---------------------------------------------------------------------------
621 // Set the virtual root property
623 void LockProcess::setVRoot(Window win, Window vr)
625 if (gVRoot)
626 removeVRoot(gVRoot);
628 QX11Info info;
629 unsigned long rw = RootWindowOfScreen(ScreenOfDisplay(QX11Info::display(), info.screen()));
630 unsigned long vroot_data[1] = { vr };
632 Window rootReturn, parentReturn, *children;
633 unsigned int numChildren;
634 Window top = win;
635 while (1) {
636 XQueryTree(QX11Info::display(), top , &rootReturn, &parentReturn,
637 &children, &numChildren);
638 if (children)
639 XFree((char *)children);
640 if (parentReturn == rw) {
641 break;
642 } else
643 top = parentReturn;
646 XChangeProperty(QX11Info::display(), top, gXA_VROOT, XA_WINDOW, 32,
647 PropModeReplace, (unsigned char *)vroot_data, 1);
650 //---------------------------------------------------------------------------
652 // Remove the virtual root property
654 void LockProcess::removeVRoot(Window win)
656 XDeleteProperty (QX11Info::display(), win, gXA_VROOT);
659 //---------------------------------------------------------------------------
661 // Grab the keyboard. Returns true on success
663 bool LockProcess::grabKeyboard()
665 int rv = XGrabKeyboard( QX11Info::display(), QApplication::desktop()->winId(),
666 True, GrabModeAsync, GrabModeAsync, CurrentTime );
668 return (rv == GrabSuccess);
671 #define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \
672 EnterWindowMask | LeaveWindowMask
674 //---------------------------------------------------------------------------
676 // Grab the mouse. Returns true on success
678 bool LockProcess::grabMouse()
680 int rv = XGrabPointer( QX11Info::display(), QApplication::desktop()->winId(),
681 True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None,
682 QCursor(Qt::BlankCursor).handle(), CurrentTime );
684 return (rv == GrabSuccess);
687 //---------------------------------------------------------------------------
689 // Grab keyboard and mouse. Returns true on success.
691 bool LockProcess::grabInput()
693 XSync(QX11Info::display(), False);
695 if (!grabKeyboard())
697 sleep(1);
698 if (!grabKeyboard())
700 return false;
704 if (!grabMouse())
706 sleep(1);
707 if (!grabMouse())
709 XUngrabKeyboard(QX11Info::display(), CurrentTime);
710 return false;
714 lockXF86();
716 return true;
719 //---------------------------------------------------------------------------
721 // Release mouse an keyboard grab.
723 void LockProcess::ungrabInput()
725 XUngrabKeyboard(QX11Info::display(), CurrentTime);
726 XUngrabPointer(QX11Info::display(), CurrentTime);
727 unlockXF86();
730 //---------------------------------------------------------------------------
732 // Start the screen saver.
734 bool LockProcess::startSaver()
736 if (!child_saver && !grabInput())
738 kWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" ;
739 return false;
741 mBusy = false;
743 saveVRoot();
745 if (mParent) {
746 QSocketNotifier *notifier = new QSocketNotifier(mParent, QSocketNotifier::Read, this);
747 connect(notifier, SIGNAL( activated (int)), SLOT( quitSaver()));
749 createSaverWindow();
750 move(0, 0);
751 show();
752 setCursor( Qt::BlankCursor );
754 raise();
755 XSync(QX11Info::display(), False);
757 setVRoot( winId(), winId() );
758 startHack();
759 startPlasma();
760 return true;
763 //---------------------------------------------------------------------------
765 // Stop the screen saver.
767 void LockProcess::stopSaver()
769 kDebug(1204) << "LockProcess: stopping saver";
770 resume( true );
771 stopPlasma();
772 stopHack();
773 hideSaverWindow();
774 mVisibility = false;
775 if (!child_saver) {
776 if (mLocked)
777 KDisplayManager().setLock( false );
778 ungrabInput();
779 const char *out = "GOAWAY!";
780 for (QList<int>::ConstIterator it = child_sockets.begin(); it != child_sockets.end(); ++it)
781 write(*it, out, sizeof(out));
785 // private static
786 QVariant LockProcess::getConf(void *ctx, const char *key, const QVariant &dflt)
788 LockProcess *that = (LockProcess *)ctx;
789 QString fkey = QLatin1String( key ) + '=';
790 for (QStringList::ConstIterator it = that->mPluginOptions.begin();
791 it != that->mPluginOptions.end(); ++it)
792 if ((*it).startsWith( fkey ))
793 return (*it).mid( fkey.length() );
794 return dflt;
797 void LockProcess::cantLock( const QString &txt)
799 msgBox( 0, QMessageBox::Critical, i18n("Will not lock the session, as unlocking would be impossible:\n") + txt );
802 #if 0 // placeholders for later
803 i18n("Cannot start <i>kcheckpass</i>.");
804 i18n("<i>kcheckpass</i> is unable to operate. Possibly it is not setuid root.");
805 #endif
807 //---------------------------------------------------------------------------
809 // Make the screen saver password protected.
811 bool LockProcess::startLock()
813 if (loadGreetPlugin()) {
814 mLocked = true;
815 KDisplayManager().setLock(true);
816 lockPlasma();
817 return true;
819 return false;
822 bool LockProcess::loadGreetPlugin()
824 if (greetPlugin.library) {
825 //we were locked once before, so all the plugin loading's done already
826 //FIXME should I be unloading the plugin on unlock instead?
827 return true;
829 for (QStringList::ConstIterator it = mPlugins.begin(); it != mPlugins.end(); ++it) {
830 GreeterPluginHandle plugin;
831 KLibrary *lib = new KLibrary( (*it)[0] == '/' ? *it : "kgreet_" + *it );
832 if (lib->fileName().isEmpty()) {
833 kWarning(1204) << "GreeterPlugin " << *it << " does not exist" ;
834 delete lib;
835 continue;
837 if (!lib->load()) {
838 kWarning(1204) << "Cannot load GreeterPlugin " << *it << " (" << lib->fileName() << ")" ;
839 delete lib;
840 continue;
842 plugin.library = lib;
843 plugin.info = (KGreeterPluginInfo *)lib->resolveSymbol( "kgreeterplugin_info" );
844 if (!plugin.info ) {
845 kWarning(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") is no valid greet widget plugin" ;
846 lib->unload();
847 delete lib;
848 continue;
850 if (plugin.info->method && !mMethod.isEmpty() && mMethod != plugin.info->method) {
851 kDebug(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") serves " << plugin.info->method << ", not " << mMethod;
852 lib->unload();
853 delete lib;
854 continue;
856 if (!plugin.info->init( mMethod, getConf, this )) {
857 kDebug(1204) << "GreeterPlugin " << *it << " (" << lib->fileName() << ") refuses to serve " << mMethod;
858 lib->unload();
859 delete lib;
860 continue;
862 kDebug(1204) << "GreeterPlugin " << *it << " (" << plugin.info->method << ", " << plugin.info->name << ") loaded";
863 greetPlugin = plugin;
864 return true;
866 cantLock( i18n("No appropriate greeter plugin configured.") );
867 return false;
870 //---------------------------------------------------------------------------
874 bool LockProcess::startHack()
876 kDebug(1204) << "Starting hack:" << mSaverExec;
878 if (mSaverExec.isEmpty() || mForbidden)
880 hackExited();
881 return false;
884 QHash<QChar, QString> keyMap;
885 keyMap.insert('w', QString::number(winId()));
886 mHackProc << KShell::splitArgs(KMacroExpander::expandMacrosShellQuote(mSaverExec, keyMap));
888 mHackProc.start();
889 if (mHackProc.waitForStarted())
891 #ifdef HAVE_SETPRIORITY
892 setpriority(PRIO_PROCESS, mHackProc.pid(), mPriority);
893 #endif
894 return true;
897 hackExited();
898 return false;
901 //---------------------------------------------------------------------------
903 void LockProcess::stopHack()
905 if (mHackProc.state() != QProcess::NotRunning)
907 mHackProc.terminate();
908 if (!mHackProc.waitForFinished(10000))
910 mHackProc.kill();
915 //---------------------------------------------------------------------------
917 void LockProcess::hackExited()
919 // Hack exited while we're supposed to be saving the screen.
920 // Make sure the saver window is black.
921 XSetWindowBackground(QX11Info::display(), winId(), 0);
922 XClearWindow(QX11Info::display(), winId());
925 bool LockProcess::startPlasma()
927 if (!mPlasmaEnabled) {
928 return false;
930 if (mSetupMode) {
931 mSuppressUnlock.start(mSuppressUnlockTimeout);
932 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS,
933 QCursor(Qt::ArrowCursor).handle(), CurrentTime);
935 kDebug() << "looking for plasma-overlay";
936 if (!mPlasmaDBus) {
937 //try to get it, in case it's already running somehow
938 //FIXME I don't like hardcoded strings
939 //mPlasmaDBus = new QDBusInterface("org.kde.plasma-overlay", "/MainApplication", QString(),
940 mPlasmaDBus = new org::kde::plasmaoverlay::App("org.kde.plasma-overlay", "/App",
941 QDBusConnection::sessionBus(), this);
942 //FIXME this might-already-be-running stuff seems really really Wrong.
944 if (mPlasmaDBus->isValid()) {
945 kDebug() << "weird, plasma-overlay is already running";
946 //FIXME I'd like to just kill it off or ignore it
947 //maybe it should *not* be a kuniqueapp?
948 //FIXME oh, and if it's already visible then we won't get a mapnotify for it
949 //and then it won't be in our foreign window lists and so stuff will break
950 if (mSetupMode) {
951 mPlasmaDBus->call(QDBus::NoBlock, "activate");
952 //it'll be locked because we haven't put plasma in setup mode. doh.
953 //FIXME make a dbus call to change that
954 } else {
955 mPlasmaDBus->call(QDBus::NoBlock, "deactivate");
957 return true;
959 kDebug () << "...not found";
960 delete mPlasmaDBus;
961 mPlasmaDBus = 0;
962 kDebug() << "starting plasma-overlay";
963 connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceOwnerChanged(QString, QString,
964 QString)),
965 SLOT(newService(QString, QString, QString)));
966 mPlasmaProc.setProgram("plasma-overlay");
967 if (mSetupMode) {
968 mPlasmaProc << "--setup";
970 mPlasmaProc.start();
971 kDebug() << "process begun";
972 //plasma gets 15 seconds to load, or we assume it failed
973 QTimer::singleShot(15 * 1000, this, SLOT(checkPlasma()));
974 return true;
977 void LockProcess::checkPlasma()
979 if (!mPlasmaEnabled) {
980 kDebug() << "You're Doing It Wrong!";
981 return;
983 if (mPlasmaDBus && mPlasmaDBus->isValid()) {
984 //hooray, looks like it started ok
985 kDebug() << "success!";
986 //...but just in case, make sure we're not waiting on it
987 mSetupMode = false;
988 return;
991 kDebug() << "ohnoes. plasma = teh fail.";
992 disablePlasma();
995 bool LockProcess::isPlasmaValid()
997 //FIXME I'm assuming that if it's valid, calls will succeed. so if that's not the case we'll
998 //need to change things so that plasma's disabled properly if it fails
999 //damn. isValid is not quite enough. a call may still fail, and then we need to bail.
1000 if (!(mPlasmaEnabled && mPlasmaDBus)) {
1001 return false; //no plasma, at least not yet
1003 if (mPlasmaDBus->isValid()) {
1004 return true;
1006 //oh crap, it ran away on us.
1007 disablePlasma();
1008 return false;
1011 void LockProcess::disablePlasma()
1013 kDebug();
1014 mPlasmaEnabled = false;
1015 mSetupMode = false;
1016 mSuppressUnlock.stop(); //FIXME we might need to start the lock timer ala deactivatePlasma()
1017 //actually we could be lazy and just call deactivatePlasma() TODO check that this'll really work
1018 delete mPlasmaDBus;
1019 mPlasmaDBus=0;
1022 void LockProcess::stopPlasma()
1024 if (mPlasmaDBus && mPlasmaDBus->isValid()) {
1025 mPlasmaDBus->call(QDBus::NoBlock, "quit");
1026 } else {
1027 kDebug() << "cannot stop plasma-overlay";
1031 void LockProcess::newService(QString name, QString oldOwner, QString newOwner)
1033 Q_UNUSED(oldOwner);
1034 if (name != "org.kde.plasma-overlay") {
1035 return;
1038 if (mPlasmaDBus) {
1039 if (newOwner.isEmpty()) {
1040 kDebug() << "plasma ran away?";
1041 disablePlasma();
1042 } else {
1043 kDebug() << "I'm confused!!";
1045 return;
1048 kDebug() << "plasma! yaay!";
1049 mPlasmaDBus = new org::kde::plasmaoverlay::App(name, "/App",
1050 QDBusConnection::sessionBus(), this);
1052 //XXX this isn't actually used any more iirc
1053 connect(mPlasmaDBus, SIGNAL(hidden()), SLOT(unSuppressUnlock()));
1055 if (!mDialogs.isEmpty()) {
1056 //whoops, activation probably failed earlier
1057 mPlasmaDBus->call(QDBus::NoBlock, "activate");
1061 void LockProcess::deactivatePlasma()
1063 if (isPlasmaValid()) {
1064 mPlasmaDBus->call(QDBus::NoBlock, "deactivate");
1066 if (!mLocked && mLockGrace >=0) {
1067 QTimer::singleShot(mLockGrace, this, SLOT(startLock())); //this is only ok because any activity will quit
1071 void LockProcess::lockPlasma()
1073 if (isPlasmaValid()) {
1074 mPlasmaDBus->call(QDBus::NoBlock, "lock");
1078 void LockProcess::unSuppressUnlock()
1080 //note: suppressing unlock also now means suppressing quit-on-activity
1081 //maybe some var renaming is in order.
1082 mSuppressUnlock.stop();
1085 void LockProcess::quit()
1087 mSuppressUnlock.stop();
1088 if (!mLocked || checkPass()) {
1089 quitSaver();
1093 void LockProcess::suspend()
1095 if( !mSuspended && mHackProc.state() == QProcess::Running )
1097 ::kill(mHackProc.pid(), SIGSTOP);
1098 QApplication::syncX();
1099 mSavedScreen = QPixmap::grabWindow( winId());
1101 mSuspended = true;
1104 void LockProcess::resume( bool force )
1106 if( !force && (!mDialogs.isEmpty() || !mVisibility ))
1107 return; // no resuming with dialog visible or when not visible
1108 if( mSuspended && mHackProc.state() == QProcess::Running )
1110 XForceScreenSaver(QX11Info::display(), ScreenSaverReset );
1111 QPainter p( this );
1112 p.drawPixmap( 0, 0, mSavedScreen );
1113 p.end();
1114 mSavedScreen = QPixmap();
1115 QApplication::syncX();
1116 ::kill(mHackProc.pid(), SIGCONT);
1118 mSuspended = false;
1121 //---------------------------------------------------------------------------
1123 // Show the password dialog
1124 // This is called only in the master process
1126 bool LockProcess::checkPass()
1128 killTimer(mAutoLogoutTimerId);
1130 if (isPlasmaValid()) {
1131 mPlasmaDBus->call(QDBus::NoBlock, "activate");
1134 PasswordDlg passDlg( this, &greetPlugin);
1135 int ret = execDialog( &passDlg );
1137 if (isPlasmaValid()) {
1138 if (ret == QDialog::Rejected) {
1139 mSuppressUnlock.start(mSuppressUnlockTimeout);
1140 } else if (ret == TIMEOUT_CODE) {
1141 mPlasmaDBus->call(QDBus::NoBlock, "deactivate");
1145 XWindowAttributes rootAttr;
1146 XGetWindowAttributes(QX11Info::display(), QX11Info::appRootWindow(), &rootAttr);
1147 if(( rootAttr.your_event_mask & SubstructureNotifyMask ) == 0 )
1149 kWarning() << "ERROR: Something removed SubstructureNotifyMask from the root window!!!" ;
1150 XSelectInput( QX11Info::display(), QX11Info::appRootWindow(),
1151 SubstructureNotifyMask | rootAttr.your_event_mask );
1154 return ret == QDialog::Accepted;
1157 bool LockProcess::checkPass(const QString &reason)
1159 PasswordDlg passDlg(this, &greetPlugin, reason);
1160 int ret = execDialog( &passDlg );
1161 kDebug() << ret;
1163 //FIXME do we need to copy&paste that SubstructureNotifyMask code above?
1164 if (ret == QDialog::Accepted) {
1165 //we don't quit on a custom checkpass, but we do unlock
1166 //so that the user doesn't have to type their password twice
1167 mLocked = false;
1168 KDisplayManager().setLock(false);
1169 //FIXME while suppressUnlock *should* always be running, if it isn't
1170 //(say if someone's doing things they shouldn't with dbus) then it won't get started by this
1171 //which means that a successful unlock will never re-lock
1172 //in fact, the next bit of activity would lead to the screensaver quitting.
1173 //possible solutions:
1174 //-treat this function like activity: quit if already unlocked, ensure suppress is started
1175 //if we're locked and the dialog's rejected
1176 //-return true if already unlocked, without doing anything, same as above if locked
1177 //-let it quit, and tell people not to do such silly things :P
1178 return true;
1180 return false;
1183 static void fakeFocusIn( WId window )
1185 // We have keyboard grab, so this application will
1186 // get keyboard events even without having focus.
1187 // Fake FocusIn to make Qt realize it has the active
1188 // window, so that it will correctly show cursor in the dialog.
1189 XEvent ev;
1190 memset(&ev, 0, sizeof(ev));
1191 ev.xfocus.display = QX11Info::display();
1192 ev.xfocus.type = FocusIn;
1193 ev.xfocus.window = window;
1194 ev.xfocus.mode = NotifyNormal;
1195 ev.xfocus.detail = NotifyAncestor;
1196 XSendEvent( QX11Info::display(), window, False, NoEventMask, &ev );
1199 bool LockProcess::eventFilter(QObject *o, QEvent *e)
1201 if (e->type() == QEvent::Resize) {
1202 QWidget *w = static_cast<QWidget *>(o);
1203 mFrames.value(w)->resize(w->size());
1205 return false;
1208 int LockProcess::execDialog( QDialog *dlg )
1210 QFrame *winFrame = new QFrame( dlg );
1211 winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised );
1212 winFrame->setLineWidth( 2 );
1213 winFrame->lower();
1214 mFrames.insert(dlg, winFrame);
1215 dlg->installEventFilter(this);
1217 dlg->adjustSize();
1219 QRect rect = dlg->geometry();
1220 rect.moveCenter(KGlobalSettings::desktopGeometry(QCursor::pos()).center());
1221 dlg->move( rect.topLeft() );
1223 if (mDialogs.isEmpty())
1225 suspend();
1226 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS,
1227 QCursor(Qt::ArrowCursor).handle(), CurrentTime);
1229 mDialogs.prepend( dlg );
1230 fakeFocusIn( dlg->winId());
1231 int rt = dlg->exec();
1232 int pos = mDialogs.indexOf( dlg );
1233 if (pos != -1)
1234 mDialogs.remove( pos );
1235 if( mDialogs.isEmpty() ) {
1236 resume( false );
1238 updateFocus();
1240 dlg->removeEventFilter(this);
1241 mFrames.remove(dlg);
1243 return rt;
1246 void LockProcess::preparePopup()
1248 QWidget *dlg = (QWidget *)sender();
1249 mDialogs.prepend( dlg );
1250 fakeFocusIn( dlg->winId() );
1253 void LockProcess::cleanupPopup()
1255 QWidget *dlg = (QWidget *)sender();
1257 int pos = mDialogs.indexOf( dlg );
1258 mDialogs.remove( pos );
1259 updateFocus();
1262 void LockProcess::updateFocus()
1264 if (mDialogs.isEmpty()) {
1265 if (mForeignInputWindows.isEmpty()) {
1266 XChangeActivePointerGrab( QX11Info::display(), GRABEVENTS,
1267 QCursor(Qt::BlankCursor).handle(), CurrentTime);
1268 } else {
1269 fakeFocusIn(mForeignInputWindows.first());
1271 } else {
1272 fakeFocusIn(mDialogs.first()->winId());
1276 //---------------------------------------------------------------------------
1278 // X11 Event.
1280 bool LockProcess::x11Event(XEvent *event)
1282 bool ret = false;
1283 switch (event->type)
1285 case ButtonPress:
1286 if (!mDialogs.isEmpty() && event->xbutton.window == event->xbutton.root) {
1287 //kDebug() << "close" << mDialogs.first()->effectiveWinId();
1288 KDialog *dlg = qobject_cast<KDialog*>(mDialogs.first());
1289 if (dlg) {
1290 //kDebug() << "casting success";
1291 dlg->reject();
1293 break;
1295 case KeyPress:
1296 case MotionNotify:
1297 if (mBusy || !mDialogs.isEmpty()) {
1298 //kDebug() << "busy";
1299 //FIXME shouldn't we be resetting some timers?
1300 break;
1302 mBusy = true;
1303 //something happened. do we quit, ask for a password or forward it to plasma?
1304 //if we're supposed to be forwarding, we check that there's actually a plasma window up
1305 //so that the user isn't trapped if plasma crashes or is slow to load.
1306 //however, if plasma started in setup mode, we don't want to let anything happen until
1307 //it has a chance to load.
1308 //note: mSetupMode should end when we either get a winid or hit the checkPlasma timeout
1309 if (mSuppressUnlock.isActive() && (mSetupMode || !mForeignInputWindows.isEmpty())) {
1310 mSuppressUnlock.start(); //help, help, I'm being suppressed!
1311 } else if (!mLocked || checkPass()) {
1312 quitSaver();
1313 mBusy = false;
1314 return true; //it's better not to forward any input while quitting, right?
1316 if (mAutoLogoutTimerId) // we need to restart the auto logout countdown
1318 killTimer(mAutoLogoutTimerId);
1319 mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000);
1321 mBusy = false;
1322 ret = true;
1323 break;
1325 case VisibilityNotify:
1326 if( event->xvisibility.window == winId())
1327 { // mVisibility == false means the screensaver is not visible at all
1328 // e.g. when switched to text console
1329 // ...or when plasma's over it non-compositely?
1330 // hey, this gives me free "suspend saver when plasma obscures it"
1331 mVisibility = !(event->xvisibility.state == VisibilityFullyObscured);
1332 if (!mVisibility) {
1333 mSuspendTimer.start(2000);
1334 kDebug() << "fully obscured";
1335 } else {
1336 kDebug() << "not fully obscured";
1337 mSuspendTimer.stop();
1338 resume( false );
1340 if (mForeignWindows.isEmpty() && event->xvisibility.state != VisibilityUnobscured) {
1341 kDebug() << "no plasma; saver obscured";
1342 stayOnTop();
1344 } else if (!mForeignWindows.isEmpty() && event->xvisibility.window == mForeignWindows.last() &&
1345 event->xvisibility.state != VisibilityUnobscured) {
1346 //FIXME now that we have several plasma winids this doesn't feel valid
1347 //but I don't know what to do about it!
1348 kDebug() << "plasma obscured!";
1349 stayOnTop();
1351 break;
1353 case ConfigureNotify: // from SubstructureNotifyMask on the root window
1354 if(event->xconfigure.event == QX11Info::appRootWindow()) {
1355 //kDebug() << "ConfigureNotify:";
1356 //the stacking order changed, so let's change the stacking order!
1357 stayOnTop();
1359 break;
1360 case MapNotify: // from SubstructureNotifyMask on the root window
1361 if( event->xmap.event == QX11Info::appRootWindow()) {
1362 kDebug() << "MapNotify:" << event->xmap.window;
1363 WindowType type = windowType(event->xmap.window);
1364 //TODO get the view id here
1365 if (type != IgnoreWindow) {
1366 if (mForeignWindows.contains(event->xmap.window)) {
1367 kDebug() << "uhoh! duplicate!";
1368 } else {
1369 //ordered youngest-on-top
1370 mForeignWindows.prepend(event->xmap.window);
1372 if (type & InputWindow) {
1373 kDebug() << "input window";
1374 if (mForeignInputWindows.contains(event->xmap.window)) {
1375 kDebug() << "uhoh! duplicate again"; //never happens
1376 } else {
1377 //ordered youngest-on-top
1378 mForeignInputWindows.prepend(event->xmap.window);
1379 fakeFocusIn(event->xmap.window);
1381 mSetupMode = false; //no more waiting for plasma
1384 stayOnTop();
1386 break;
1387 case UnmapNotify:
1388 if (event->xmap.event == QX11Info::appRootWindow()) {
1389 kDebug() << "UnmapNotify:" << event->xunmap.window;
1390 mForeignWindows.removeAll(event->xunmap.window);
1391 if (mForeignInputWindows.removeAll(event->xunmap.window)) {
1392 updateFocus();
1397 // We have grab with the grab window being the root window.
1398 // This results in key events being sent to the root window,
1399 // but they should be sent to the dialog if it's visible.
1400 // It could be solved by setFocus() call, but that would mess
1401 // the focus after this process exits.
1402 // Qt seems to be quite hard to persuade to redirect the event,
1403 // so let's simply dupe it with correct destination window,
1404 // and ignore the original one.
1405 if (!mDialogs.isEmpty()) {
1406 if ((event->type == KeyPress || event->type == KeyRelease) &&
1407 event->xkey.window != mDialogs.first()->winId()) {
1408 //kDebug() << "forward to dialog";
1409 XEvent ev2 = *event;
1410 ev2.xkey.window = ev2.xkey.subwindow = mDialogs.first()->winId();
1411 qApp->x11ProcessEvent( &ev2 );
1412 ret = true;
1414 } else if (!mForeignInputWindows.isEmpty()) {
1415 //when there are no dialogs, forward some events to plasma
1416 switch (event->type) {
1417 case KeyPress:
1418 case KeyRelease:
1419 case ButtonPress:
1420 case ButtonRelease:
1421 case MotionNotify:
1423 //kDebug() << "forward to plasma";
1424 XEvent ev2 = *event;
1425 ev2.xkey.window = ev2.xkey.subwindow = mForeignInputWindows.first();
1426 XSendEvent(QX11Info::display(), ev2.xkey.window, False, NoEventMask, &ev2);
1427 ret = true;
1429 default:
1430 break;
1434 return ret;
1437 LockProcess::WindowType LockProcess::windowType(WId id)
1439 Atom tag = XInternAtom(QX11Info::display(), "_KDE_SCREENSAVER_OVERRIDE", False);
1440 Atom actualType;
1441 int actualFormat;
1442 unsigned long nitems, remaining;
1443 unsigned char *data = 0;
1444 Display *display = QX11Info::display();
1446 int result = XGetWindowProperty(display, id, tag, 0, 1, False, tag, &actualType,
1447 &actualFormat, &nitems, &remaining, &data);
1449 //kDebug() << (result == Success) << (actualType == tag);
1450 WindowType type = IgnoreWindow;
1451 if (result == Success && actualType == tag) {
1452 if (nitems != 1 || actualFormat != 8) {
1453 kDebug() << "malformed property";
1454 } else {
1455 kDebug() << "i can haz plasma window?" << data[0];
1456 switch (data[0]) {
1457 case 0: //FIXME magic numbers
1458 type = SimpleWindow;
1459 break;
1460 case 1:
1461 type = InputWindow;
1462 break;
1463 case 2:
1464 type = DefaultWindow;
1465 break;
1469 if (data) {
1470 XFree(data);
1472 return type;
1473 /* if (result != Success) {
1474 return false;
1476 if (actualType == tag) {
1477 return true;
1479 //managed windows will have a pesky frame we have to bypass
1480 XWindowAttributes attr;
1481 XGetWindowAttributes(display, id, &attr);
1482 if (!attr.override_redirect) {
1483 //check the real client window
1484 if (Window client = XmuClientWindow(display, id)) {
1485 result = XGetWindowProperty(display, client, tag, 0, 0, False, tag, &actualType,
1486 &actualFormat, &nitems, &remaining, &data);
1487 kDebug() << (result == Success) << (actualType == tag);
1488 if (data) {
1489 XFree(data);
1491 return (result == Success) && (actualType == tag);
1494 return false;*/
1497 void LockProcess::stayOnTop()
1499 if(!(mDialogs.isEmpty() && mForeignWindows.isEmpty()))
1501 // this restacking is written in a way so that
1502 // if the stacking positions actually don't change,
1503 // all restacking operations will be no-op,
1504 // and no ConfigureNotify will be generated,
1505 // thus avoiding possible infinite loops
1506 Window* stack = new Window[ mDialogs.count() + mForeignWindows.count() + 1 ];
1507 int count = 0;
1508 if (!mDialogs.isEmpty()) {
1509 XRaiseWindow( QX11Info::display(), mDialogs.first()->winId()); // raise topmost
1510 // and stack others below it
1511 for( QVector< QWidget* >::ConstIterator it = mDialogs.begin();
1512 it != mDialogs.end();
1513 ++it )
1514 stack[ count++ ] = (*it)->winId();
1515 } else {
1516 XRaiseWindow( QX11Info::display(), mForeignWindows.first()); // raise topmost
1518 //now the plasma stuff below the dialogs
1519 foreach (const WId w, mForeignWindows) {
1520 stack[count++] = w;
1522 //finally, the saver window
1523 stack[ count++ ] = winId();
1524 XRestackWindows( x11Info().display(), stack, count );
1525 //kDebug() << "restacked" << count;
1526 delete[] stack;
1527 } else {
1528 XRaiseWindow(QX11Info::display(), winId());
1532 void LockProcess::checkDPMSActive()
1534 #ifdef HAVE_DPMS
1535 BOOL on;
1536 CARD16 state;
1537 DPMSInfo(QX11Info::display(), &state, &on);
1538 //kDebug() << "checkDPMSActive " << on << " " << state;
1539 if (state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff)
1541 suspend();
1542 } else if ( mSuspended )
1544 resume( true );
1546 #endif
1549 #if defined(HAVE_XF86MISC) && defined(HAVE_XF86MISCSETGRABKEYSSTATE)
1550 // see http://cvsweb.xfree86.org/cvsweb/xc/programs/Xserver/hw/xfree86/common/xf86Events.c#rev3.113
1551 // This allows enabling the "Allow{Deactivate/Closedown}Grabs" options in XF86Config,
1552 // and krunner_lock will still lock the session.
1553 static enum { Unknown, Yes, No } can_do_xf86_lock = Unknown;
1554 void LockProcess::lockXF86()
1556 if( can_do_xf86_lock == Unknown )
1558 int major, minor;
1559 if( XF86MiscQueryVersion( QX11Info::display(), &major, &minor )
1560 && (major > 0 || minor >= 5) )
1561 can_do_xf86_lock = Yes;
1562 else
1563 can_do_xf86_lock = No;
1565 if( can_do_xf86_lock != Yes )
1566 return;
1567 if( mRestoreXF86Lock )
1568 return;
1569 if( XF86MiscSetGrabKeysState( QX11Info::display(), False ) != MiscExtGrabStateSuccess )
1570 return;
1571 // success
1572 mRestoreXF86Lock = true;
1575 void LockProcess::unlockXF86()
1577 if( can_do_xf86_lock != Yes )
1578 return;
1579 if( !mRestoreXF86Lock )
1580 return;
1581 XF86MiscSetGrabKeysState( QX11Info::display(), True );
1582 mRestoreXF86Lock = false;
1584 #else
1585 void LockProcess::lockXF86()
1589 void LockProcess::unlockXF86()
1592 #endif
1594 void LockProcess::msgBox( QWidget *parent, QMessageBox::Icon type, const QString &txt )
1596 QDialog box( parent, Qt::X11BypassWindowManagerHint );
1597 box.setModal( true );
1599 QLabel *label1 = new QLabel( &box );
1600 label1->setPixmap( QMessageBox::standardIcon( type ) );
1601 QLabel *label2 = new QLabel( txt, &box );
1602 KPushButton *button = new KPushButton( KStandardGuiItem::ok(), &box );
1603 button->setDefault( true );
1604 button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) );
1605 connect( button, SIGNAL( clicked() ), &box, SLOT( accept() ) );
1607 QGridLayout *grid = new QGridLayout( &box );
1608 grid->setSpacing( 10 );
1609 grid->addWidget( label1, 0, 0, Qt::AlignCenter );
1610 grid->addWidget( label2, 0, 1, Qt::AlignCenter );
1611 grid->addWidget( button, 1, 0, 1, 2, Qt::AlignCenter );
1613 execDialog( &box );
1616 #include "lockprocess.moc"