diff options
Diffstat (limited to 'kdesktop/lock/lockprocess.cc')
-rw-r--r-- | kdesktop/lock/lockprocess.cc | 1172 |
1 files changed, 1172 insertions, 0 deletions
diff --git a/kdesktop/lock/lockprocess.cc b/kdesktop/lock/lockprocess.cc new file mode 100644 index 000000000..a813b6899 --- /dev/null +++ b/kdesktop/lock/lockprocess.cc @@ -0,0 +1,1172 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright (c) 1999 Martin R. Jones <mjones@kde.org> +// Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> +// + +//kdesktop keeps running and checks user inactivity +//when it should show screensaver (and maybe lock the session), +//it starts kdesktop_lock, who does all the locking and who +//actually starts the screensaver + +//It's done this way to prevent screen unlocking when kdesktop +//crashes (e.g. because it's set to multiple wallpapers and +//some image will be corrupted). + +#include <config.h> + +#include "lockprocess.h" +#include "lockdlg.h" +#include "autologout.h" +#include "kdesktopsettings.h" + +#include <dmctl.h> + +#include <kstandarddirs.h> +#include <kapplication.h> +#include <kservicegroup.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <kglobalsettings.h> +#include <klocale.h> +#include <klibloader.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kpixmapeffect.h> +#include <kpixmap.h> + +#include <qframe.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qcursor.h> +#include <qtimer.h> +#include <qfile.h> +#include <qsocketnotifier.h> +#include <qvaluevector.h> +#include <qtooltip.h> + +#include <qdatetime.h> + +#include <stdlib.h> +#include <assert.h> +#include <signal.h> +#ifdef HAVE_SETPRIORITY +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Intrinsic.h> + +#ifdef HAVE_DPMS +extern "C" { +#include <X11/Xmd.h> +#ifndef Bool +#define Bool BOOL +#endif +#include <X11/extensions/dpms.h> + +#ifndef HAVE_DPMSINFO_PROTO +Status DPMSInfo ( Display *, CARD16 *, BOOL * ); +#endif +} +#endif + +#ifdef HAVE_XF86MISC +#include <X11/extensions/xf86misc.h> +#endif + +#ifdef HAVE_GLXCHOOSEVISUAL +#include <GL/glx.h> +#endif + +#define LOCK_GRACE_DEFAULT 5000 +#define AUTOLOGOUT_DEFAULT 600 + +static Window gVRoot = 0; +static Window gVRootData = 0; +static Atom gXA_VROOT; +static Atom gXA_SCREENSAVER_VERSION; + +//=========================================================================== +// +// Screen saver handling process. Handles screensaver window, +// starting screensaver hacks, and password entry.f +// +LockProcess::LockProcess(bool child, bool useBlankOnly) + : QWidget(0L, "saver window", WX11BypassWM), + mOpenGLVisual(0), + child_saver(child), + mParent(0), + mUseBlankOnly(useBlankOnly), + mSuspended(false), + mVisibility(false), + mRestoreXF86Lock(false), + mForbidden(false), + mAutoLogout(false) +{ + setupSignals(); + + kapp->installX11EventFilter(this); + + // Get root window size + XWindowAttributes rootAttr; + XGetWindowAttributes(qt_xdisplay(), RootWindow(qt_xdisplay(), + qt_xscreen()), &rootAttr); + mRootWidth = rootAttr.width; + mRootHeight = rootAttr.height; + { // trigger creation of QToolTipManager, it does XSelectInput() on the root window + QWidget w; + QToolTip::add( &w, "foo" ); + } + XSelectInput( qt_xdisplay(), qt_xrootwin(), + SubstructureNotifyMask | rootAttr.your_event_mask ); + + // Add non-KDE path + KGlobal::dirs()->addResourceType("scrsav", + KGlobal::dirs()->kde_default("apps") + + "System/ScreenSavers/"); + + // Add KDE specific screensaver path + QString relPath="System/ScreenSavers/"; + KServiceGroup::Ptr servGroup = KServiceGroup::baseGroup( "screensavers"); + if (servGroup) + { + relPath=servGroup->relPath(); + kdDebug(1204) << "relPath=" << relPath << endl; + } + KGlobal::dirs()->addResourceType("scrsav", + KGlobal::dirs()->kde_default("apps") + + relPath); + + // virtual root property + gXA_VROOT = XInternAtom (qt_xdisplay(), "__SWM_VROOT", False); + gXA_SCREENSAVER_VERSION = XInternAtom (qt_xdisplay(), "_SCREENSAVER_VERSION", False); + + connect(&mHackProc, SIGNAL(processExited(KProcess *)), + SLOT(hackExited(KProcess *))); + + connect(&mSuspendTimer, SIGNAL(timeout()), SLOT(suspend())); + + QStringList dmopt = + QStringList::split(QChar(','), + QString::fromLatin1( ::getenv( "XDM_MANAGED" ))); + for (QStringList::ConstIterator it = dmopt.begin(); it != dmopt.end(); ++it) + if ((*it).startsWith("method=")) + mMethod = (*it).mid(7); + + configure(); + +#ifdef HAVE_DPMS + if (mDPMSDepend) { + BOOL on; + CARD16 state; + DPMSInfo(qt_xdisplay(), &state, &on); + if (on) + { + connect(&mCheckDPMS, SIGNAL(timeout()), SLOT(checkDPMSActive())); + // we can save CPU if we stop it as quickly as possible + // but we waste CPU if we check too often -> so take 10s + mCheckDPMS.start(10000); + } + } +#endif + + greetPlugin.library = 0; +} + +//--------------------------------------------------------------------------- +// +// Destructor - usual cleanups. +// +LockProcess::~LockProcess() +{ + if (greetPlugin.library) { + if (greetPlugin.info->done) + greetPlugin.info->done(); + greetPlugin.library->unload(); + } +} + +static int signal_pipe[2]; + +static void sigterm_handler(int) +{ + char tmp = 'T'; + ::write( signal_pipe[1], &tmp, 1); +} + +static void sighup_handler(int) +{ + char tmp = 'H'; + ::write( signal_pipe[1], &tmp, 1); +} + +void LockProcess::timerEvent(QTimerEvent *ev) +{ + if (mAutoLogout && ev->timerId() == mAutoLogoutTimerId) + { + killTimer(mAutoLogoutTimerId); + AutoLogout autologout(this); + execDialog(&autologout); + } +} + +void LockProcess::setupSignals() +{ + struct sigaction act; + // ignore SIGINT + act.sa_handler=SIG_IGN; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGINT); + act.sa_flags = 0; + sigaction(SIGINT, &act, 0L); + // ignore SIGQUIT + act.sa_handler=SIG_IGN; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGQUIT); + act.sa_flags = 0; + sigaction(SIGQUIT, &act, 0L); + // exit cleanly on SIGTERM + act.sa_handler= sigterm_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGTERM); + act.sa_flags = 0; + sigaction(SIGTERM, &act, 0L); + // SIGHUP forces lock + act.sa_handler= sighup_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGHUP); + act.sa_flags = 0; + sigaction(SIGHUP, &act, 0L); + + pipe(signal_pipe); + QSocketNotifier* notif = new QSocketNotifier(signal_pipe[0], + QSocketNotifier::Read, this ); + connect( notif, SIGNAL(activated(int)), SLOT(signalPipeSignal())); +} + + +void LockProcess::signalPipeSignal() +{ + char tmp; + ::read( signal_pipe[0], &tmp, 1); + if( tmp == 'T' ) + quitSaver(); + else if( tmp == 'H' ) { + if( !mLocked ) + startLock(); + } +} + +//--------------------------------------------------------------------------- +bool LockProcess::lock() +{ + if (startSaver()) { + // In case of a forced lock we don't react to events during + // the dead-time to give the screensaver some time to activate. + // That way we don't accidentally show the password dialog before + // the screensaver kicks in because the user moved the mouse after + // selecting "lock screen", that looks really untidy. + mBusy = true; + if (startLock()) + { + QTimer::singleShot(1000, this, SLOT(slotDeadTimePassed())); + return true; + } + stopSaver(); + mBusy = false; + } + return false; +} +//--------------------------------------------------------------------------- +void LockProcess::slotDeadTimePassed() +{ + mBusy = false; +} + +//--------------------------------------------------------------------------- +bool LockProcess::defaultSave() +{ + mLocked = false; + if (startSaver()) { + if (mLockGrace >= 0) + QTimer::singleShot(mLockGrace, this, SLOT(startLock())); + return true; + } + return false; +} + +//--------------------------------------------------------------------------- +bool LockProcess::dontLock() +{ + mLocked = false; + return startSaver(); +} + +//--------------------------------------------------------------------------- +void LockProcess::quitSaver() +{ + stopSaver(); + kapp->quit(); +} + +//--------------------------------------------------------------------------- +// +// Read and apply configuration. +// +void LockProcess::configure() +{ + // the configuration is stored in kdesktop's config file + if( KDesktopSettings::lock() ) + { + mLockGrace = KDesktopSettings::lockGrace(); + if (mLockGrace < 0) + mLockGrace = 0; + else if (mLockGrace > 300000) + mLockGrace = 300000; // 5 minutes, keep the value sane + } + else + mLockGrace = -1; + + if ( KDesktopSettings::autoLogout() ) + { + mAutoLogout = true; + mAutoLogoutTimeout = KDesktopSettings::autoLogoutTimeout(); + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); // in milliseconds + } + +#ifdef HAVE_DPMS + //if the user decided that the screensaver should run independent from + //dpms, we shouldn't check for it, aleXXX + mDPMSDepend = KDesktopSettings::dpmsDependent(); +#endif + + mPriority = KDesktopSettings::priority(); + if (mPriority < 0) mPriority = 0; + if (mPriority > 19) mPriority = 19; + + mSaver = KDesktopSettings::saver(); + if (mSaver.isEmpty() || mUseBlankOnly) + mSaver = "KBlankscreen.desktop"; + + readSaver(); + + mPlugins = KDesktopSettings::pluginsUnlock(); + if (mPlugins.isEmpty()) + mPlugins = QStringList("classic"); + mPluginOptions = KDesktopSettings::pluginOptions(); +} + +//--------------------------------------------------------------------------- +// +// Read the command line needed to run the screensaver given a .desktop file. +// +void LockProcess::readSaver() +{ + if (!mSaver.isEmpty()) + { + QString file = locate("scrsav", mSaver); + + bool opengl = kapp->authorize("opengl_screensavers"); + bool manipulatescreen = kapp->authorize("manipulatescreen_screensavers"); + KDesktopFile config(file, true); + if (config.readEntry("X-KDE-Type").utf8()) + { + QString saverType = config.readEntry("X-KDE-Type").utf8(); + QStringList saverTypes = QStringList::split(";", saverType); + for (uint i = 0; i < saverTypes.count(); i++) + { + if ((saverTypes[i] == "ManipulateScreen") && !manipulatescreen) + { + kdDebug(1204) << "Screensaver is type ManipulateScreen and ManipulateScreen is forbidden" << endl; + mForbidden = true; + } + if ((saverTypes[i] == "OpenGL") && !opengl) + { + kdDebug(1204) << "Screensaver is type OpenGL and OpenGL is forbidden" << endl; + mForbidden = true; + } + if (saverTypes[i] == "OpenGL") + { + mOpenGLVisual = true; + } + } + } + + kdDebug(1204) << "mForbidden: " << (mForbidden ? "true" : "false") << endl; + + if (config.hasActionGroup("Root")) + { + config.setActionGroup("Root"); + mSaverExec = config.readPathEntry("Exec"); + } + } +} + +//--------------------------------------------------------------------------- +// +// Create a window to draw our screen saver on. +// +void LockProcess::createSaverWindow() +{ + Visual* visual = CopyFromParent; + XSetWindowAttributes attrs; + int flags = CWOverrideRedirect; +#ifdef HAVE_GLXCHOOSEVISUAL + if( mOpenGLVisual ) + { + static int attribs[][ 15 ] = + { + #define R GLX_RED_SIZE + #define G GLX_GREEN_SIZE + #define B GLX_BLUE_SIZE + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, None } + #undef R + #undef G + #undef B + }; + for( unsigned int i = 0; + i < sizeof( attribs ) / sizeof( attribs[ 0 ] ); + ++i ) + { + if( XVisualInfo* info = glXChooseVisual( x11Display(), x11Screen(), attribs[ i ] )) + { + visual = info->visual; + static Colormap colormap = 0; + if( colormap != 0 ) + XFreeColormap( x11Display(), colormap ); + colormap = XCreateColormap( x11Display(), RootWindow( x11Display(), x11Screen()), visual, AllocNone ); + attrs.colormap = colormap; + flags |= CWColormap; + XFree( info ); + break; + } + } + } +#endif + + attrs.override_redirect = 1; + hide(); + Window w = XCreateWindow( x11Display(), RootWindow( x11Display(), x11Screen()), + x(), y(), width(), height(), 0, x11Depth(), InputOutput, visual, flags, &attrs ); + create( w ); + + // Some xscreensaver hacks check for this property + const char *version = "KDE 2.0"; + XChangeProperty (qt_xdisplay(), winId(), + gXA_SCREENSAVER_VERSION, XA_STRING, 8, PropModeReplace, + (unsigned char *) version, strlen(version)); + + XSetWindowAttributes attr; + attr.event_mask = KeyPressMask | ButtonPressMask | PointerMotionMask | + VisibilityChangeMask | ExposureMask; + XChangeWindowAttributes(qt_xdisplay(), winId(), + CWEventMask, &attr); + + // erase(); + + // set NoBackground so that the saver can capture the current + // screen state if necessary + setBackgroundMode(QWidget::NoBackground); + + setCursor( blankCursor ); + setGeometry(0, 0, mRootWidth, mRootHeight); + + kdDebug(1204) << "Saver window Id: " << winId() << endl; +} + +//--------------------------------------------------------------------------- +// +// Hide the screensaver window +// +void LockProcess::hideSaverWindow() +{ + hide(); + lower(); + removeVRoot(winId()); + XDeleteProperty(qt_xdisplay(), winId(), gXA_SCREENSAVER_VERSION); + if ( gVRoot ) { + unsigned long vroot_data[1] = { gVRootData }; + XChangeProperty(qt_xdisplay(), gVRoot, gXA_VROOT, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)vroot_data, 1); + gVRoot = 0; + } + XSync(qt_xdisplay(), False); +} + +//--------------------------------------------------------------------------- +static int ignoreXError(Display *, XErrorEvent *) +{ + return 0; +} + +//--------------------------------------------------------------------------- +// +// Save the current virtual root window +// +void LockProcess::saveVRoot() +{ + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + Window root = RootWindowOfScreen(ScreenOfDisplay(qt_xdisplay(), qt_xscreen())); + + gVRoot = 0; + gVRootData = 0; + + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(ignoreXError); + + if (XQueryTree(qt_xdisplay(), root, &rootReturn, &parentReturn, + &children, &numChildren)) + { + for (unsigned int i = 0; i < numChildren; i++) + { + Atom actual_type; + int actual_format; + unsigned long nitems, bytesafter; + unsigned char *newRoot = 0; + + if ((XGetWindowProperty(qt_xdisplay(), children[i], gXA_VROOT, 0, 1, + False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytesafter, + &newRoot) == Success) && newRoot) + { + gVRoot = children[i]; + Window *dummy = (Window*)newRoot; + gVRootData = *dummy; + XFree ((char*) newRoot); + break; + } + } + if (children) + { + XFree((char *)children); + } + } + + XSetErrorHandler(oldHandler); +} + +//--------------------------------------------------------------------------- +// +// Set the virtual root property +// +void LockProcess::setVRoot(Window win, Window vr) +{ + if (gVRoot) + removeVRoot(gVRoot); + + unsigned long rw = RootWindowOfScreen(ScreenOfDisplay(qt_xdisplay(), qt_xscreen())); + unsigned long vroot_data[1] = { vr }; + + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + Window top = win; + while (1) { + XQueryTree(qt_xdisplay(), top , &rootReturn, &parentReturn, + &children, &numChildren); + if (children) + XFree((char *)children); + if (parentReturn == rw) { + break; + } else + top = parentReturn; + } + + XChangeProperty(qt_xdisplay(), top, gXA_VROOT, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)vroot_data, 1); +} + +//--------------------------------------------------------------------------- +// +// Remove the virtual root property +// +void LockProcess::removeVRoot(Window win) +{ + XDeleteProperty (qt_xdisplay(), win, gXA_VROOT); +} + +//--------------------------------------------------------------------------- +// +// Grab the keyboard. Returns true on success +// +bool LockProcess::grabKeyboard() +{ + int rv = XGrabKeyboard( qt_xdisplay(), QApplication::desktop()->winId(), + True, GrabModeAsync, GrabModeAsync, CurrentTime ); + + return (rv == GrabSuccess); +} + +#define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \ + EnterWindowMask | LeaveWindowMask + +//--------------------------------------------------------------------------- +// +// Grab the mouse. Returns true on success +// +bool LockProcess::grabMouse() +{ + int rv = XGrabPointer( qt_xdisplay(), QApplication::desktop()->winId(), + True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None, + blankCursor.handle(), CurrentTime ); + + return (rv == GrabSuccess); +} + +//--------------------------------------------------------------------------- +// +// Grab keyboard and mouse. Returns true on success. +// +bool LockProcess::grabInput() +{ + XSync(qt_xdisplay(), False); + + if (!grabKeyboard()) + { + sleep(1); + if (!grabKeyboard()) + { + return false; + } + } + + if (!grabMouse()) + { + sleep(1); + if (!grabMouse()) + { + XUngrabKeyboard(qt_xdisplay(), CurrentTime); + return false; + } + } + + lockXF86(); + + return true; +} + +//--------------------------------------------------------------------------- +// +// Release mouse an keyboard grab. +// +void LockProcess::ungrabInput() +{ + XUngrabKeyboard(qt_xdisplay(), CurrentTime); + XUngrabPointer(qt_xdisplay(), CurrentTime); + unlockXF86(); +} + +//--------------------------------------------------------------------------- +// +// Start the screen saver. +// +bool LockProcess::startSaver() +{ + if (!child_saver && !grabInput()) + { + kdWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" << endl; + return false; + } + mBusy = false; + + saveVRoot(); + + if (mParent) { + QSocketNotifier *notifier = new QSocketNotifier(mParent, QSocketNotifier::Read, this, "notifier"); + connect(notifier, SIGNAL( activated (int)), SLOT( quitSaver())); + } + createSaverWindow(); + move(0, 0); + show(); + setCursor( blankCursor ); + + raise(); + XSync(qt_xdisplay(), False); + setVRoot( winId(), winId() ); + startHack(); + return true; +} + +//--------------------------------------------------------------------------- +// +// Stop the screen saver. +// +void LockProcess::stopSaver() +{ + kdDebug(1204) << "LockProcess: stopping saver" << endl; + resume( true ); + stopHack(); + hideSaverWindow(); + mVisibility = false; + if (!child_saver) { + if (mLocked) + DM().setLock( false ); + ungrabInput(); + const char *out = "GOAWAY!"; + for (QValueList<int>::ConstIterator it = child_sockets.begin(); it != child_sockets.end(); ++it) + write(*it, out, sizeof(out)); + } +} + +// private static +QVariant LockProcess::getConf(void *ctx, const char *key, const QVariant &dflt) +{ + LockProcess *that = (LockProcess *)ctx; + QString fkey = QString::fromLatin1( key ) + '='; + for (QStringList::ConstIterator it = that->mPluginOptions.begin(); + it != that->mPluginOptions.end(); ++it) + if ((*it).startsWith( fkey )) + return (*it).mid( fkey.length() ); + return dflt; +} + +void LockProcess::cantLock( const QString &txt) +{ + msgBox( QMessageBox::Critical, i18n("Will not lock the session, as unlocking would be impossible:\n") + txt ); +} + +#if 0 // placeholders for later +i18n("Cannot start <i>kcheckpass</i>."); +i18n("<i>kcheckpass</i> is unable to operate. Possibly it is not SetUID root."); +#endif + +//--------------------------------------------------------------------------- +// +// Make the screen saver password protected. +// +bool LockProcess::startLock() +{ + for (QStringList::ConstIterator it = mPlugins.begin(); it != mPlugins.end(); ++it) { + GreeterPluginHandle plugin; + QString path = KLibLoader::self()->findLibrary( + ((*it)[0] == '/' ? *it : "kgreet_" + *it ).latin1() ); + if (path.isEmpty()) { + kdWarning(1204) << "GreeterPlugin " << *it << " does not exist" << endl; + continue; + } + if (!(plugin.library = KLibLoader::self()->library( path.latin1() ))) { + kdWarning(1204) << "Cannot load GreeterPlugin " << *it << " (" << path << ")" << endl; + continue; + } + if (!plugin.library->hasSymbol( "kgreeterplugin_info" )) { + kdWarning(1204) << "GreeterPlugin " << *it << " (" << path << ") is no valid greet widget plugin" << endl; + plugin.library->unload(); + continue; + } + plugin.info = (kgreeterplugin_info*)plugin.library->symbol( "kgreeterplugin_info" ); + if (plugin.info->method && !mMethod.isEmpty() && mMethod != plugin.info->method) { + kdDebug(1204) << "GreeterPlugin " << *it << " (" << path << ") serves " << plugin.info->method << ", not " << mMethod << endl; + plugin.library->unload(); + continue; + } + if (!plugin.info->init( mMethod, getConf, this )) { + kdDebug(1204) << "GreeterPlugin " << *it << " (" << path << ") refuses to serve " << mMethod << endl; + plugin.library->unload(); + continue; + } + kdDebug(1204) << "GreeterPlugin " << *it << " (" << plugin.info->method << ", " << plugin.info->name << ") loaded" << endl; + greetPlugin = plugin; + mLocked = true; + DM().setLock( true ); + return true; + } + cantLock( i18n("No appropriate greeter plugin configured.") ); + return false; +} + +//--------------------------------------------------------------------------- +// + + +bool LockProcess::startHack() +{ + if (mSaverExec.isEmpty()) + { + return false; + } + + if (mHackProc.isRunning()) + { + stopHack(); + } + + mHackProc.clearArguments(); + + QTextStream ts(&mSaverExec, IO_ReadOnly); + QString word; + ts >> word; + QString path = KStandardDirs::findExe(word); + + if (!path.isEmpty()) + { + mHackProc << path; + + kdDebug(1204) << "Starting hack: " << path << endl; + + while (!ts.atEnd()) + { + ts >> word; + if (word == "%w") + { + word = word.setNum(winId()); + } + mHackProc << word; + } + + if (!mForbidden) + { + + if (mHackProc.start() == true) + { +#ifdef HAVE_SETPRIORITY + setpriority(PRIO_PROCESS, mHackProc.pid(), mPriority); +#endif + //bitBlt(this, 0, 0, &mOriginal); + return true; + } + } + else + // we aren't allowed to start the specified screensaver either because it didn't run for some reason + // according to the kiosk restrictions forbid it + { + setBackgroundColor(black); + } + } + return false; +} + +//--------------------------------------------------------------------------- +// +void LockProcess::stopHack() +{ + if (mHackProc.isRunning()) + { + mHackProc.kill(); + if (!mHackProc.wait(10)) + { + mHackProc.kill(SIGKILL); + } + } +} + +//--------------------------------------------------------------------------- +// +void LockProcess::hackExited(KProcess *) +{ + // Hack exited while we're supposed to be saving the screen. + // Make sure the saver window is black. + setBackgroundColor(black); +} + +void LockProcess::suspend() +{ + if(!mSuspended) + { + mHackProc.kill(SIGSTOP); + QApplication::syncX(); + mSavedScreen = QPixmap::grabWindow( winId()); + } + mSuspended = true; +} + +void LockProcess::resume( bool force ) +{ + if( !force && (!mDialogs.isEmpty() || !mVisibility )) + return; // no resuming with dialog visible or when not visible + if(mSuspended) + { + XForceScreenSaver(qt_xdisplay(), ScreenSaverReset ); + bitBlt( this, 0, 0, &mSavedScreen ); + QApplication::syncX(); + mHackProc.kill(SIGCONT); + } + mSuspended = false; +} + +//--------------------------------------------------------------------------- +// +// Show the password dialog +// This is called only in the master process +// +bool LockProcess::checkPass() +{ + if (mAutoLogout) + killTimer(mAutoLogoutTimerId); + + PasswordDlg passDlg( this, &greetPlugin); + + int ret = execDialog( &passDlg ); + + XWindowAttributes rootAttr; + XGetWindowAttributes(qt_xdisplay(), RootWindow(qt_xdisplay(), + qt_xscreen()), &rootAttr); + if(( rootAttr.your_event_mask & SubstructureNotifyMask ) == 0 ) + { + kdWarning() << "ERROR: Something removed SubstructureNotifyMask from the root window!!!" << endl; + XSelectInput( qt_xdisplay(), qt_xrootwin(), + SubstructureNotifyMask | rootAttr.your_event_mask ); + } + + return ret == QDialog::Accepted; +} + +static void fakeFocusIn( WId window ) +{ + // We have keyboard grab, so this application will + // get keyboard events even without having focus. + // Fake FocusIn to make Qt realize it has the active + // window, so that it will correctly show cursor in the dialog. + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xfocus.display = qt_xdisplay(); + ev.xfocus.type = FocusIn; + ev.xfocus.window = window; + ev.xfocus.mode = NotifyNormal; + ev.xfocus.detail = NotifyAncestor; + XSendEvent( qt_xdisplay(), window, False, NoEventMask, &ev ); +} + +int LockProcess::execDialog( QDialog *dlg ) +{ + dlg->adjustSize(); + + QRect rect = dlg->geometry(); + rect.moveCenter(KGlobalSettings::desktopGeometry(QCursor::pos()).center()); + dlg->move( rect.topLeft() ); + + if (mDialogs.isEmpty()) + { + suspend(); + XChangeActivePointerGrab( qt_xdisplay(), GRABEVENTS, + arrowCursor.handle(), CurrentTime); + } + mDialogs.prepend( dlg ); + fakeFocusIn( dlg->winId()); + int rt = dlg->exec(); + mDialogs.remove( dlg ); + if( mDialogs.isEmpty() ) { + XChangeActivePointerGrab( qt_xdisplay(), GRABEVENTS, + blankCursor.handle(), CurrentTime); + resume( false ); + } else + fakeFocusIn( mDialogs.first()->winId()); + return rt; +} + +void LockProcess::preparePopup() +{ + QWidget *dlg = (QWidget *)sender(); + mDialogs.prepend( dlg ); + fakeFocusIn( dlg->winId() ); +} + +void LockProcess::cleanupPopup() +{ + QWidget *dlg = (QWidget *)sender(); + mDialogs.remove( dlg ); + fakeFocusIn( mDialogs.first()->winId() ); +} + +//--------------------------------------------------------------------------- +// +// X11 Event. +// +bool LockProcess::x11Event(XEvent *event) +{ + switch (event->type) + { + case KeyPress: + case ButtonPress: + case MotionNotify: + if (mBusy || !mDialogs.isEmpty()) + break; + mBusy = true; + if (!mLocked || checkPass()) + { + stopSaver(); + kapp->quit(); + } + else if (mAutoLogout) // we need to restart the auto logout countdown + { + killTimer(mAutoLogoutTimerId); + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout); + } + mBusy = false; + return true; + + case VisibilityNotify: + if( event->xvisibility.window == winId()) + { // mVisibility == false means the screensaver is not visible at all + // e.g. when switched to text console + mVisibility = !(event->xvisibility.state == VisibilityFullyObscured); + if(!mVisibility) + mSuspendTimer.start(2000, true); + else + { + mSuspendTimer.stop(); + resume( false ); + } + if (event->xvisibility.state != VisibilityUnobscured) + stayOnTop(); + } + break; + + case ConfigureNotify: // from SubstructureNotifyMask on the root window + if(event->xconfigure.event == qt_xrootwin()) + stayOnTop(); + break; + case MapNotify: // from SubstructureNotifyMask on the root window + if( event->xmap.event == qt_xrootwin()) + stayOnTop(); + break; + } + + // We have grab with the grab window being the root window. + // This results in key events being sent to the root window, + // but they should be sent to the dialog if it's visible. + // It could be solved by setFocus() call, but that would mess + // the focus after this process exits. + // Qt seems to be quite hard to persuade to redirect the event, + // so let's simply dupe it with correct destination window, + // and ignore the original one. + if(!mDialogs.isEmpty() && ( event->type == KeyPress || event->type == KeyRelease) + && event->xkey.window != mDialogs.first()->winId()) + { + XEvent ev2 = *event; + ev2.xkey.window = ev2.xkey.subwindow = mDialogs.first()->winId(); + qApp->x11ProcessEvent( &ev2 ); + return true; + } + + return false; +} + +void LockProcess::stayOnTop() +{ + if(!mDialogs.isEmpty()) + { + // this restacking is written in a way so that + // if the stacking positions actually don't change, + // all restacking operations will be no-op, + // and no ConfigureNotify will be generated, + // thus avoiding possible infinite loops + XRaiseWindow( qt_xdisplay(), mDialogs.first()->winId()); // raise topmost + // and stack others below it + Window* stack = new Window[ mDialogs.count() + 1 ]; + int count = 0; + for( QValueList< QWidget* >::ConstIterator it = mDialogs.begin(); + it != mDialogs.end(); + ++it ) + stack[ count++ ] = (*it)->winId(); + stack[ count++ ] = winId(); + XRestackWindows( x11Display(), stack, count ); + delete[] stack; + } + else + XRaiseWindow(qt_xdisplay(), winId()); +} + +void LockProcess::checkDPMSActive() +{ +#ifdef HAVE_DPMS + BOOL on; + CARD16 state; + DPMSInfo(qt_xdisplay(), &state, &on); + //kdDebug() << "checkDPMSActive " << on << " " << state << endl; + if (state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff) + { + suspend(); + } else if ( mSuspended ) + { + resume( true ); + } +#endif +} + +#if defined(HAVE_XF86MISC) && defined(HAVE_XF86MISCSETGRABKEYSSTATE) +// see http://cvsweb.xfree86.org/cvsweb/xc/programs/Xserver/hw/xfree86/common/xf86Events.c#rev3.113 +// This allows enabling the "Allow{Deactivate/Closedown}Grabs" options in XF86Config, +// and kdesktop_lock will still lock the session. +static enum { Unknown, Yes, No } can_do_xf86_lock = Unknown; +void LockProcess::lockXF86() +{ + if( can_do_xf86_lock == Unknown ) + { + int major, minor; + if( XF86MiscQueryVersion( qt_xdisplay(), &major, &minor ) + && major >= 0 && minor >= 5 ) + can_do_xf86_lock = Yes; + else + can_do_xf86_lock = No; + } + if( can_do_xf86_lock != Yes ) + return; + if( mRestoreXF86Lock ) + return; + if( XF86MiscSetGrabKeysState( qt_xdisplay(), False ) != MiscExtGrabStateSuccess ) + return; + // success + mRestoreXF86Lock = true; +} + +void LockProcess::unlockXF86() +{ + if( can_do_xf86_lock != Yes ) + return; + if( !mRestoreXF86Lock ) + return; + XF86MiscSetGrabKeysState( qt_xdisplay(), True ); + mRestoreXF86Lock = false; +} +#else +void LockProcess::lockXF86() +{ +} + +void LockProcess::unlockXF86() +{ +} +#endif + +void LockProcess::msgBox( QMessageBox::Icon type, const QString &txt ) +{ + QDialog box( 0, "messagebox", true, WX11BypassWM ); + QFrame *winFrame = new QFrame( &box ); + winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised ); + winFrame->setLineWidth( 2 ); + QLabel *label1 = new QLabel( winFrame ); + label1->setPixmap( QMessageBox::standardIcon( type ) ); + QLabel *label2 = new QLabel( txt, winFrame ); + KPushButton *button = new KPushButton( KStdGuiItem::ok(), winFrame ); + button->setDefault( true ); + button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); + connect( button, SIGNAL( clicked() ), &box, SLOT( accept() ) ); + + QVBoxLayout *vbox = new QVBoxLayout( &box ); + vbox->addWidget( winFrame ); + QGridLayout *grid = new QGridLayout( winFrame, 2, 2, 10 ); + grid->addWidget( label1, 0, 0, Qt::AlignCenter ); + grid->addWidget( label2, 0, 1, Qt::AlignCenter ); + grid->addMultiCellWidget( button, 1,1, 0,1, Qt::AlignCenter ); + + execDialog( &box ); +} + +#include "lockprocess.moc" |