//===========================================================================
//
// 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 "infodlg.h"
#include "querydlg.h"
#include "autologout.h"
#include "kdesktopsettings.h"

#include <dmctl.h>
#include <dcopref.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 <kwin.h>
#include <kwinmodule.h>

#include <tqframe.h>
#include <tqlabel.h>
#include <tqlayout.h>
#include <tqcursor.h>
#include <tqtimer.h>
#include <tqfile.h>
#include <tqsocketnotifier.h>
#include <tqvaluevector.h>
#include <tqtooltip.h>

#include <tqdatetime.h>

#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#ifdef HAVE_SETPRIORITY
#include <sys/time.h>
#include <sys/resource.h>
#endif
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>

#include <kcrash.h>

#include <linux/stat.h>

#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

// These lines are taken on 10/2009 from X.org (X11/XF86keysym.h), defining some special multimedia keys
#define XF86XK_AudioMute 0x1008FF12
#define XF86XK_AudioRaiseVolume 0x1008FF13
#define XF86XK_AudioLowerVolume 0x1008FF11
#define XF86XK_Display 0x1008FF59

static Window gVRoot = 0;
static Window gVRootData = 0;
static Atom   gXA_VROOT;
static Atom   gXA_SCREENSAVER_VERSION;

static void segv_handler(int)
{
	printf("[kdesktop_lock] WARNING: A fatal exception was encountered.  Trapping and ignoring it so as not to compromise desktop security...\n\r");
	sleep(1);
}

extern Atom qt_wm_state;

//===========================================================================
//
// Screen saver handling process.  Handles screensaver window,
// starting screensaver hacks, and password entry.f
//
LockProcess::LockProcess(bool child, bool useBlankOnly)
    : TQWidget(0L, "saver window", WX11BypassWM),
      mOpenGLVisual(0),
      child_saver(child),
      mParent(0),
      mUseBlankOnly(useBlankOnly),
      mSuspended(false),
      mVisibility(false),
      mRestoreXF86Lock(false),
      mForbidden(false),
      mAutoLogout(false),
      mVkbdProcess(NULL),
      mKWinModule(NULL),
      mPipeOpen(false),
      mPipeOpen_out(false),
      mInfoMessageDisplayed(false),
      mForceReject(false),
      mDialogControlLock(false),
      currentDialog(NULL),
      resizeTimer(NULL)
{
    setupSignals();
    setupPipe();

    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
    TQWidget w;
    TQToolTip::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
    TQString 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, TQT_SIGNAL(processExited(KProcess *)),
                        TQT_SLOT(hackExited(KProcess *)));

    connect(&mSuspendTimer, TQT_SIGNAL(timeout()), TQT_SLOT(suspend()));

    TQStringList dmopt =
        TQStringList::split(TQChar(','),
                            TQString::tqfromLatin1( ::getenv( "XDM_MANAGED" )));
    for (TQStringList::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, TQT_SIGNAL(timeout()), TQT_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

#if (QT_VERSION-0 >= 0x030200) // XRANDR support
  connect( kapp->desktop(), TQT_SIGNAL( resized( int )), TQT_SLOT( desktopResized()));
#endif

    greetPlugin.library = 0;

    KCrash::setCrashHandler(segv_handler);
}

//---------------------------------------------------------------------------
//
// Destructor - usual cleanups.
//
LockProcess::~LockProcess()
{
    if (greetPlugin.library) {
        if (greetPlugin.info->done)
            greetPlugin.info->done();
        greetPlugin.library->unload();
    }

    mPipeOpen = false;
    mPipeOpen_out = false;
}

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(TQTimerEvent *ev)
{
	if (mAutoLogout && ev->timerId() == mAutoLogoutTimerId)
	{
		killTimer(mAutoLogoutTimerId);
		AutoLogout autologout(this);
		execDialog(&autologout);
	}
}

void LockProcess::setupPipe()
{
    /* Create the FIFOs if they do not exist */
    umask(0);
    mkdir(FIFO_DIR,0644);
    mknod(FIFO_FILE, S_IFIFO|0644, 0);
    chmod(FIFO_FILE, 0644);

    mPipe_fd = open(FIFO_FILE, O_RDONLY | O_NONBLOCK);
    if (mPipe_fd > -1) {
        mPipeOpen = true;
        TQTimer::singleShot( PIPE_CHECK_INTERVAL, this, TQT_SLOT(checkPipe()) );
    }

    mknod(FIFO_FILE_OUT, S_IFIFO|0600, 0);
    chmod(FIFO_FILE_OUT, 0600);

    mPipe_fd_out = open(FIFO_FILE_OUT, O_RDWR | O_NONBLOCK);
    if (mPipe_fd_out > -1) {
        mPipeOpen_out = true;
    }
}

void LockProcess::checkPipe()
{
    char readbuf[128];
    int numread;
    TQString to_display;
    const char * pin_entry;

    if (mPipeOpen == true) {
        readbuf[0]=' ';
        numread = read(mPipe_fd, readbuf, 128);
        readbuf[numread] = 0;
        if (numread > 0) {
            if (readbuf[0] == 'C') {
                mInfoMessageDisplayed=false;
                while (mDialogControlLock == true) sleep(1);
                mDialogControlLock = true;
                if (currentDialog != NULL) {
                    mForceReject = true;
                    currentDialog->close();
                }
                mDialogControlLock = false;
            }
            if (readbuf[0] == 'T') {
                to_display = readbuf;
                to_display = to_display.remove(0,1);
                // Lock out password dialogs and close any active dialog
                mInfoMessageDisplayed=true;
                while (mDialogControlLock == true) sleep(1);
                mDialogControlLock = true;
                if (currentDialog != NULL) {
                    mForceReject = true;
                    currentDialog->close();
                }
                mDialogControlLock = false;
                // Display info message dialog
                TQTimer::singleShot( PIPE_CHECK_INTERVAL, this, TQT_SLOT(checkPipe()) );
                InfoDlg inDlg( this );
                inDlg.updateLabel(to_display);
                inDlg.setUnlockIcon();
                int ret = execDialog( &inDlg );
                mForceReject = false;
                return;
            }
            if ((readbuf[0] == 'E') || (readbuf[0] == 'W') || (readbuf[0] == 'I') || (readbuf[0] == 'K')) {
                to_display = readbuf;
                to_display = to_display.remove(0,1);
                // Lock out password dialogs and close any active dialog
                mInfoMessageDisplayed=true;
                while (mDialogControlLock == true) sleep(1);
                mDialogControlLock = true;
                if (currentDialog != NULL) {
                    mForceReject = true;
                    currentDialog->close();
                }
                mDialogControlLock = false;
                // Display info message dialog
                TQTimer::singleShot( PIPE_CHECK_INTERVAL, this, TQT_SLOT(checkPipe()) );
                InfoDlg inDlg( this );
                inDlg.updateLabel(to_display);
                if (readbuf[0] == 'K') inDlg.setKDEIcon();
                if (readbuf[0] == 'I') inDlg.setInfoIcon();
                if (readbuf[0] == 'W') inDlg.setWarningIcon();
                if (readbuf[0] == 'E') inDlg.setErrorIcon();
                int ret = execDialog( &inDlg );
                mForceReject = false;
                return;
            }
            if (readbuf[0] == 'Q') {
                to_display = readbuf;
                to_display = to_display.remove(0,1);
                // Lock out password dialogs and close any active dialog
                mInfoMessageDisplayed=true;
                while (mDialogControlLock == true) sleep(1);
                mDialogControlLock = true;
                if (currentDialog != NULL) {
                    mForceReject = true;
                    currentDialog->close();
                }
                mDialogControlLock = false;
                // Display query dialog
                TQTimer::singleShot( PIPE_CHECK_INTERVAL, this, TQT_SLOT(checkPipe()) );
                QueryDlg qryDlg( this );
                qryDlg.updateLabel(to_display);
                qryDlg.setUnlockIcon();
                mForceReject = false;
                int ret = execDialog( &qryDlg );
                if (mForceReject == false) {
                    pin_entry = qryDlg.getEntry();
                    mInfoMessageDisplayed=false;
                    if (mPipeOpen_out == true) {
                        write(mPipe_fd_out, pin_entry, strlen(pin_entry)+1);
                        write(mPipe_fd_out, "\n\r", 3);
                    }
                }
                mForceReject = false;
                return;
            }
        }
        TQTimer::singleShot( PIPE_CHECK_INTERVAL, this, TQT_SLOT(checkPipe()) );
    }
}

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);
    TQSocketNotifier* notif = new TQSocketNotifier(signal_pipe[0],
	TQSocketNotifier::Read, TQT_TQOBJECT(this) );
    connect( notif, TQT_SIGNAL(activated(int)), TQT_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())
        {
            TQTimer::singleShot(1000, this, TQT_SLOT(slotDeadTimePassed()));
            return true;
        }
        stopSaver();
        mBusy = false;
    }
    return false;
}
//---------------------------------------------------------------------------
void LockProcess::slotDeadTimePassed()
{
    mBusy = false;
}

//---------------------------------------------------------------------------
bool LockProcess::defaultSave()
{
    mLocked = false;
    if (startSaver()) {
        if (mLockGrace >= 0)
            TQTimer::singleShot(mLockGrace, this, TQT_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 = TQStringList("classic");
    mPluginOptions = KDesktopSettings::pluginOptions();
}

//---------------------------------------------------------------------------
//
// Read the command line needed to run the screensaver given a .desktop file.
//
void LockProcess::readSaver()
{
    if (!mSaver.isEmpty())
    {
        TQString 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() != 0)
	{
		TQString saverType = config.readEntry("X-KDE-Type").utf8();
		TQStringList saverTypes = TQStringList::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(TQWidget::NoBackground);

    setCursor( tqblankCursor );
    setGeometry(0, 0, mRootWidth, mRootHeight);

    kdDebug(1204) << "Saver window Id: " << winId() << endl;
}

void LockProcess::desktopResized()
{
    mBusy = true;
    suspend();
    setCursor( tqblankCursor );

    // Get root window size
    XWindowAttributes rootAttr;
    XGetWindowAttributes(qt_xdisplay(), RootWindow(qt_xdisplay(), qt_xscreen()), &rootAttr);
    mRootWidth = rootAttr.width;
    mRootHeight = rootAttr.height;

    setGeometry(0, 0, mRootWidth, mRootHeight);

    // This slot needs to be able to execute very rapidly so as to prevent the user's desktop from ever
    // being displayed, so we finish the hack restarting/display prettying operations in a separate timed slot
    if (resizeTimer == NULL) {
        resizeTimer = new TQTimer( this );
        connect( resizeTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(doDesktopResizeFinish()) );
    }
    resizeTimer->start( 100, TRUE ); // 100 millisecond single shot timer; should allow display switching operations to finish before hack is started
}

void LockProcess::doDesktopResizeFinish()
{
    stopHack();

    while (mDialogControlLock == true) sleep(1);
    mDialogControlLock = true;
    if (currentDialog != NULL) {
        mForceReject = true;
        currentDialog->close();
    }
    mDialogControlLock = false;

    // Restart the hack as the window size is now different
    startHack();

    mBusy = false;
}

//---------------------------------------------------------------------------
//
// 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(), TQApplication::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(), TQApplication::desktop()->winId(),
            True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None,
            TQCursor(tqblankCursor).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) {
        TQSocketNotifier *notifier = new TQSocketNotifier(mParent, TQSocketNotifier::Read, TQT_TQOBJECT(this), "notifier");
        connect(notifier, TQT_SIGNAL( activated (int)), TQT_SLOT( quitSaver()));
    }
    createSaverWindow();
    move(0, 0);
    show();
    setCursor( tqblankCursor );

    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 (TQValueList<int>::ConstIterator it = child_sockets.begin(); it != child_sockets.end(); ++it)
            write(*it, out, sizeof(out));
    }
}

// private static
TQVariant LockProcess::getConf(void *ctx, const char *key, const TQVariant &dflt)
{
    LockProcess *that = (LockProcess *)ctx;
    TQString fkey = TQString::tqfromLatin1( key ) + '=';
    for (TQStringList::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 TQString &txt)
{
    msgBox( TQMessageBox::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 (TQStringList::ConstIterator it = mPlugins.begin(); it != mPlugins.end(); ++it) {
        GreeterPluginHandle plugin;
        TQString 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();

    TQTextStream ts(&mSaverExec, IO_ReadOnly);
    TQString word;
    ts >> word;
    TQString 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);
        TQApplication::syncX();
        mSavedScreen = TQPixmap::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 );
        TQApplication::syncX();
        mHackProc.kill(SIGCONT);
    }
    mSuspended = false;
}

//---------------------------------------------------------------------------
//
// Show the password dialog
// This is called only in the master process
//
bool LockProcess::checkPass()
{
    if (mInfoMessageDisplayed == false) {
        if (mAutoLogout)
            killTimer(mAutoLogoutTimerId);

        showVkbd();
        PasswordDlg passDlg( this, &greetPlugin);
        int ret = execDialog( &passDlg );
        hideVkbd();

        if (mForceReject == true) {
            ret = TQDialog::Rejected;
        }
        mForceReject = false;

        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 == TQDialog::Accepted;
    }
    else {
        return 0;
    }
}

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( TQDialog *dlg )
{
    currentDialog=dlg;
    dlg->adjustSize();

    TQRect rect = dlg->geometry();
    rect.moveCenter(KGlobalSettings::desktopGeometry(TQCursor::pos()).center());
    dlg->move( rect.topLeft() );

    if (mDialogs.isEmpty())
    {
        suspend();
        XChangeActivePointerGrab( qt_xdisplay(), GRABEVENTS,
                TQCursor(tqarrowCursor).handle(), CurrentTime);
    }
    mDialogs.prepend( dlg );
    fakeFocusIn( dlg->winId());
    int rt = dlg->exec();
    while (mDialogControlLock == true) sleep(1);
    currentDialog = NULL;
    mDialogs.remove( dlg );
    if( mDialogs.isEmpty() ) {
        XChangeActivePointerGrab( qt_xdisplay(), GRABEVENTS,
                TQCursor(tqblankCursor).handle(), CurrentTime);
        resume( false );
    } else
        fakeFocusIn( mDialogs.first()->winId());
    return rt;
}

void LockProcess::preparePopup()
{
    TQWidget *dlg = (TQWidget *)sender();
    mDialogs.prepend( dlg );
    fakeFocusIn( dlg->winId() );
}

void LockProcess::cleanupPopup()
{
    TQWidget *dlg = (TQWidget *)sender();
    mDialogs.remove( dlg );
    fakeFocusIn( mDialogs.first()->winId() );
}

void LockProcess::doFunctionKeyBroadcast() {
    // Provide a clean, pretty display switch by hiding the password dialog here
    mBusy=true;
    TQTimer::singleShot(1000, this, TQT_SLOT(slotDeadTimePassed()));
    if (mkeyCode == XKeysymToKeycode(qt_xdisplay(), XF86XK_Display)) {
        while (mDialogControlLock == true) sleep(1);
        mDialogControlLock = true;
        if (currentDialog != NULL) {
            mForceReject = true;
            currentDialog->close();
        }
        mDialogControlLock = false;
    }
    setCursor( tqblankCursor );

    DCOPRef ref( "*", "MainApplication-Interface");
    ref.send("sendFakeKey", DCOPArg(mkeyCode , "unsigned int"));
}

//---------------------------------------------------------------------------
//
// X11 Event.
//
bool LockProcess::x11Event(XEvent *event)
{
    // Allow certain very specific keypresses through
    // Key:			Reason:
    // XF86Display		You need to be able to see the screen when unlocking your computer
    // XF86AudioMute		Would be nice to be able to shut your computer up in an emergency while it is locked
    // XF86AudioRaiseVolume	Ditto
    // XF86AudioLowerVolume	Ditto

    //if ((event->type == KeyPress) || (event->type == KeyRelease)) {
    if (event->type == KeyPress) {
        if ((event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_Display)) || \
        (event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_AudioMute)) || \
        (event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_AudioRaiseVolume)) || \
        (event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_AudioLowerVolume))) {
            mkeyCode = event->xkey.keycode;
            TQTimer::singleShot( 100, this, TQT_SLOT(doFunctionKeyBroadcast()) );
            return true;
        }
    }

    switch (event->type)
    {
        case ButtonPress:
        case MotionNotify:
        case ButtonRelease:
            if( forwardVkbdEvent( event ))
                return true; // filter out
            // fall through
        case KeyPress:
            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();
            for( TQValueList< VkbdWindow >::Iterator it = mVkbdWindows.begin();
                 it != mVkbdWindows.end();
                 ++it ) {
                if( (*it).id == event->xconfigure.window ) {
                    (*it).rect = TQRect( event->xconfigure.x, event->xconfigure.y,
                        event->xconfigure.width, event->xconfigure.height );
                    break;
                }
            }
            break;
        case MapNotify: // from SubstructureNotifyMask on the root window
            windowAdded( event->xmap.window, false );
            if( event->xmap.event == qt_xrootwin())
                stayOnTop();
            break;
        case DestroyNotify:
            for( TQValueList< VkbdWindow >::Iterator it = mVkbdWindows.begin();
                 it != mVkbdWindows.end();
                 ++it )
                if( (*it).id == event->xdestroywindow.window ) {
                    mVkbdWindows.remove( it );
                    break;
            }
            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();
        tqApp->x11ProcessEvent( &ev2 );
        return true;
    }

    return false;
}

void LockProcess::stayOnTop()
{
    if(!mDialogs.isEmpty() || !mVkbdWindows.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
        if( !mVkbdWindows.isEmpty())
            XRaiseWindow( qt_xdisplay(), mVkbdWindows.first().id );
        else
            XRaiseWindow( qt_xdisplay(), mDialogs.first()->winId()); // raise topmost
        // and stack others below it
        Window* stack = new Window[ mDialogs.count() + mVkbdWindows.count() + 1 ];
        int count = 0;
        for( TQValueList< VkbdWindow >::ConstIterator it = mVkbdWindows.begin();
             it != mVkbdWindows.end();
             ++it )
            stack[ count++ ] = (*it).id;
        for( TQValueList< TQWidget* >::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( TQMessageBox::Icon type, const TQString &txt )
{
    TQDialog box( 0, "messagebox", true, (WFlags)WX11BypassWM );
    TQFrame *winFrame = new TQFrame( &box );
    winFrame->setFrameStyle( TQFrame::WinPanel | TQFrame::Raised );
    winFrame->setLineWidth( 2 );
    TQLabel *label1 = new TQLabel( winFrame );
    label1->setPixmap( TQMessageBox::standardIcon( type ) );
    TQLabel *label2 = new TQLabel( txt, winFrame );
    KPushButton *button = new KPushButton( KStdGuiItem::ok(), winFrame );
    button->setDefault( true );
    button->tqsetSizePolicy( TQSizePolicy( TQSizePolicy::Preferred, TQSizePolicy::Preferred ) );
    connect( button, TQT_SIGNAL( clicked() ), &box, TQT_SLOT( accept() ) );

    TQVBoxLayout *vbox = new TQVBoxLayout( &box );
    vbox->addWidget( winFrame );
    TQGridLayout *grid = new TQGridLayout( 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 );
}

static int run_vkbd = -1;
void LockProcess::showVkbd()
{
    if( run_vkbd == - 1 ) {
        int status = system( "hal-find-by-property --key system.formfactor.subtype --string tabletpc" );
//        status = 0; // enable for testing
        run_vkbd = ( WIFEXITED( status ) && WEXITSTATUS( status ) == 0
            && !KStandardDirs::findExe( "xvkbd" ).isEmpty()) ? 1 : 0;
    }
    if( run_vkbd ) {
        mVkbdWindows.clear();
        mVkbdLastEventWindow = None;
        mKWinModule = new KWinModule( NULL, KWinModule::INFO_WINDOWS );
        connect( mKWinModule, TQT_SIGNAL( windowAdded( WId )), TQT_SLOT( windowAdded( WId )));
        mVkbdProcess = new KProcess;
        *mVkbdProcess << "xvkbd" << "-compact" << "-geometry" << "-0-0" << "-xdm";
        mVkbdProcess->start();
    }
}

void LockProcess::hideVkbd()
{
    if( mVkbdProcess != NULL ) {
        mVkbdProcess->kill();
        delete mVkbdProcess;
        mVkbdProcess = NULL;
        delete mKWinModule;
        mKWinModule = NULL;
        mVkbdWindows.clear();
    }
}

void LockProcess::windowAdded( WId w )
{
    windowAdded( w, true );
}

void LockProcess::windowAdded( WId w, bool managed )
{
    KWin::WindowInfo info = KWin::windowInfo( w, 0, NET::WM2WindowClass );
    if( info.windowClassClass().lower() != "xvkbd" )
        return;
    // Unmanaged windows (i.e. popups) don't currently work anyway, since they
    // don't have WM_CLASS set anyway. I could perhaps try tricks with X id
    // ranges if really needed.
    if( managed ) {
        // withdraw the window, wait for it to be withdrawn, reparent it directly
        // to root at the right position
        XWithdrawWindow( qt_xdisplay(), w, qt_xscreen());
        for(;;) {
            Atom type;
            int format;
            unsigned long length, after;
            unsigned char *data;
            int r = XGetWindowProperty( qt_xdisplay(), w, qt_wm_state, 0, 2,
                                        false, AnyPropertyType, &type, &format,
                                        &length, &after, &data );
            bool withdrawn = true;
            if ( r == Success && data && format == 32 ) {
                TQ_UINT32 *wstate = (TQ_UINT32*)data;
                withdrawn  = (*wstate == WithdrawnState );
                XFree( (char *)data );
            }
            if( withdrawn )
                break;
        }
    }
    XSelectInput( qt_xdisplay(), w, StructureNotifyMask );
    XWindowAttributes attr_geom;
    if( !XGetWindowAttributes( qt_xdisplay(), w, &attr_geom ))
        return;
    int x = XDisplayWidth( qt_xdisplay(), qt_xscreen()) - attr_geom.width;
    int y = XDisplayHeight( qt_xdisplay(), qt_xscreen()) - attr_geom.height;
    if( managed ) {
        XSetWindowAttributes attr;
        attr.override_redirect = True;
        XChangeWindowAttributes( qt_xdisplay(), w, CWOverrideRedirect, &attr );
        XReparentWindow( qt_xdisplay(), w, qt_xrootwin(), x, y );
        XMapWindow( qt_xdisplay(), w );
    }
    VkbdWindow data;
    data.id = w;
    data.rect = TQRect( x, y, attr_geom.width, attr_geom.height );
    mVkbdWindows.prepend( data );
}

bool LockProcess::forwardVkbdEvent( XEvent* event )
{
    if( mVkbdProcess == NULL )
        return false;
    TQPoint pos;
    Time time;
    switch( event->type )
    {
        case ButtonPress:
        case ButtonRelease:
            pos = TQPoint( event->xbutton.x, event->xbutton.y );
            time = event->xbutton.time;
            break;
        case MotionNotify:
            pos = TQPoint( event->xmotion.x, event->xmotion.y );
            time = event->xmotion.time;
            break;
        default:
            return false;
    }
    // vkbd windows are kept topmost, so just find the first one in the position
    for( TQValueList< VkbdWindow >::ConstIterator it = mVkbdWindows.begin();
         it != mVkbdWindows.end();
         ++it ) {
        if( TQT_TQRECT_OBJECT((*it).rect).tqcontains( pos )) {
            // Find the subwindow where the event should actually go.
            // Not exactly cheap in the number of X roundtrips but oh well.
            Window window = (*it).id;
            Window root, child;
            int root_x, root_y, x, y;
            unsigned int mask;
            for(;;) {
                if( !XQueryPointer( qt_xdisplay(), window, &root, &child, &root_x, &root_y, &x, &y, &mask ))
                    return false;
                if( child == None )
                    break;
                window = child;
            }
            switch( event->type )
            {
                case ButtonPress:
                case ButtonRelease:
                    event->xbutton.x = x;
                    event->xbutton.y = y;
                    event->xbutton.subwindow = None;
                    break;
                case MotionNotify:
                    event->xmotion.x = x;
                    event->xmotion.y = y;
                    event->xmotion.subwindow = None;
                    break;
            }
            event->xany.window = window;
            sendVkbdFocusInOut( window, time );
            XSendEvent( qt_xdisplay(), window, False, 0, event );
            return true;
        }
    }
    sendVkbdFocusInOut( None, time );
    return false;
}

// Fake EnterNotify/LeaveNotify events as the mouse moves. They're not sent by X
// because of the grab and having them makes xvkbd highlight the buttons (but
// not needed otherwise it seems).
void LockProcess::sendVkbdFocusInOut( WId window, Time t )
{
    if( mVkbdLastEventWindow == window )
        return;
    if( mVkbdLastEventWindow != None ) {
        XEvent e;
        e.xcrossing.type = LeaveNotify;
        e.xcrossing.display = qt_xdisplay();
        e.xcrossing.window = mVkbdLastEventWindow;
        e.xcrossing.root = qt_xrootwin();
        e.xcrossing.subwindow = None;
        e.xcrossing.time = t;
        e.xcrossing.x = 0;
        e.xcrossing.y = 0;
        e.xcrossing.x_root = -1;
        e.xcrossing.y_root = -1;
        e.xcrossing.mode = NotifyNormal;
        e.xcrossing.detail = NotifyAncestor;
        e.xcrossing.same_screen = True;
        e.xcrossing.focus = False;
        e.xcrossing.state = 0;
        XSendEvent( qt_xdisplay(), mVkbdLastEventWindow, False, 0, &e );
    }
    mVkbdLastEventWindow = window;
    if( mVkbdLastEventWindow != None ) {
        XEvent e;
        e.xcrossing.type = EnterNotify;
        e.xcrossing.display = qt_xdisplay();
        e.xcrossing.window = mVkbdLastEventWindow;
        e.xcrossing.root = qt_xrootwin();
        e.xcrossing.subwindow = None;
        e.xcrossing.time = t;
        e.xcrossing.x = 0;
        e.xcrossing.y = 0;
        e.xcrossing.x_root = 0;
        e.xcrossing.y_root = 0;
        e.xcrossing.mode = NotifyNormal;
        e.xcrossing.detail = NotifyAncestor;
        e.xcrossing.same_screen = True;
        e.xcrossing.focus = False;
        e.xcrossing.state = 0;
        XSendEvent( qt_xdisplay(), mVkbdLastEventWindow, False, 0, &e );
    }
}

#include "lockprocess.moc"