diff options
Diffstat (limited to 'kdesktop/lock/lockprocess.cpp')
-rw-r--r-- | kdesktop/lock/lockprocess.cpp | 2940 |
1 files changed, 2940 insertions, 0 deletions
diff --git a/kdesktop/lock/lockprocess.cpp b/kdesktop/lock/lockprocess.cpp new file mode 100644 index 000000000..67492ea3c --- /dev/null +++ b/kdesktop/lock/lockprocess.cpp @@ -0,0 +1,2940 @@ +//=========================================================================== +// +// 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> +// Copyright (c) 2010-2013 Timothy Pearson <kb9vqf@pearsoncomputing.net> +// + +//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 "sakdlg.h" +#include "securedlg.h" +#include "autologout.h" +#include "kdesktopsettings.h" + +#include <dmctl.h> +#include <dcopref.h> + +#include <kstandarddirs.h> +#include <tdeapplication.h> +#include <kservicegroup.h> +#include <kdebug.h> +#include <tdemessagebox.h> +#include <tdeglobalsettings.h> +#include <tdelocale.h> +#include <klibloader.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kpixmapeffect.h> +#include <kpixmap.h> +#include <twin.h> +#include <twinmodule.h> +#include <kdialog.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 <tqimage.h> +#include <tqregexp.h> +#include <tqpainter.h> +#include <tqeventloop.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 <pthread.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 + +#define DESKTOP_WALLPAPER_OBTAIN_TIMEOUT_MS 3000 + +// Setting this define is INSECURE +// Use it for debugging purposes ONLY +// #define KEEP_MOUSE_UNGRABBED 1 + +// 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 + +// These lines are taken on 08/2013 from X.org (X11/XF86keysym.h), defining some special ACPI power keys +#define XF86XK_PowerOff 0x1008FF2A +#define XF86XK_Sleep 0x1008FF2F +#define XF86XK_Suspend 0x1008FFA7 +#define XF86XK_Hibernate 0x1008FFA8 + +#define DPMS_MONITOR_BLANKED(x) ((x == DPMSModeStandby) || (x == DPMSModeSuspend) || (x == DPMSModeOff)) + +static Window gVRoot = 0; +static Window gVRootData = 0; +static Atom gXA_VROOT; +static Atom gXA_SCREENSAVER_VERSION; + +Atom kde_wm_system_modal_notification = 0; +Atom kde_wm_transparent_to_desktop = 0; +Atom kde_wm_transparent_to_black = 0; + +static void segv_handler(int) +{ + kdError(1204) << "A fatal exception was encountered." + << " Trapping and ignoring it so as not to compromise desktop security..." + << kdBacktrace() << endl; + sleep(1); +} + +extern Atom tqt_wm_state; +extern bool trinity_desktop_lock_use_system_modal_dialogs; +extern bool trinity_desktop_lock_delay_screensaver_start; +extern bool trinity_desktop_lock_use_sak; +extern bool trinity_desktop_lock_hide_active_windows; +extern bool trinity_desktop_lock_hide_cancel_button; +extern bool trinity_desktop_lock_forced; + +extern LockProcess* trinity_desktop_lock_process; + +extern bool argb_visual; +extern pid_t kdesktop_pid; + +extern TQXLibWindowList trinity_desktop_lock_hidden_window_list; + +bool trinity_desktop_lock_autohide_lockdlg = TRUE; + +#define ENABLE_CONTINUOUS_LOCKDLG_DISPLAY \ +if (!mForceContinualLockDisplayTimer->isActive()) mForceContinualLockDisplayTimer->start(100, FALSE); \ +trinity_desktop_lock_autohide_lockdlg = FALSE; \ +mHackDelayStartupTimer->stop(); + +#define DISABLE_CONTINUOUS_LOCKDLG_DISPLAY \ +mForceContinualLockDisplayTimer->stop(); \ +trinity_desktop_lock_autohide_lockdlg = TRUE; \ +mHackDelayStartupTimer->stop(); + +//=========================================================================== +// +// Screen saver handling process. Handles screensaver window, +// starting screensaver hacks, and password entry. +// +LockProcess::LockProcess() + : TQWidget(0L, "saver window", ((WFlags)(WStyle_StaysOnTop|WStyle_Customize|WStyle_NoBorder))), + mOpenGLVisual(0), + mParent(0), + mShowLockDateTime(false), + mSuspended(false), + mVisibility(false), + mRestoreXF86Lock(false), + mForbidden(false), + mAutoLogout(false), + resizeTimer(NULL), + hackResumeTimer(NULL), + mVkbdProcess(NULL), + mKWinModule(NULL), + mPipeOpen(false), + mPipeOpen_out(false), + mInfoMessageDisplayed(false), + mDialogControlLock(false), + mForceReject(false), + currentDialog(NULL), + mEnsureScreenHiddenTimer(NULL), + mForceContinualLockDisplayTimer(NULL), + mEnsureVRootWindowSecurityTimer(NULL), + mHackDelayStartupTimer(NULL), + mHackDelayStartupTimeout(0), + mHackStartupEnabled(true), + mOverrideHackStartupEnabled(false), + mResizingDesktopLock(false), + mFullyOnlineSent(false), + mClosingWindows(false), + mInSecureDialog(false), + mHackActive(false), + m_rootPixmap(NULL), + mBackingStartupDelayTimer(0), + m_startupStatusDialog(NULL), + m_mouseDown(0), + m_mousePrevX(0), + m_mousePrevY(0), + m_dialogPrevX(0), + m_dialogPrevY(0), + m_notifyReadyRequested(false), + m_maskWidget(NULL), + m_saverRootWindow(0) +{ +#ifdef KEEP_MOUSE_UNGRABBED + setNFlags(WX11DisableMove|WX11DisableClose|WX11DisableShade|WX11DisableMinimize|WX11DisableMaximize); +#endif + + setupSignals(); + + // Set up atoms + kde_wm_system_modal_notification = XInternAtom(tqt_xdisplay(), "_TDE_WM_MODAL_SYS_NOTIFICATION", False); + kde_wm_transparent_to_desktop = XInternAtom(tqt_xdisplay(), "_TDE_TRANSPARENT_TO_DESKTOP", False); + kde_wm_transparent_to_black = XInternAtom(tqt_xdisplay(), "_TDE_TRANSPARENT_TO_BLACK", False); + + kapp->installX11EventFilter(this); + + mForceContinualLockDisplayTimer = new TQTimer( this ); + mHackDelayStartupTimer = new TQTimer( this ); + mEnsureVRootWindowSecurityTimer = new TQTimer( this ); + + if (!argb_visual) { + // Try to get the root pixmap + if (!m_rootPixmap) m_rootPixmap = new KRootPixmap(this); + connect(m_rootPixmap, TQT_SIGNAL(backgroundUpdated(const TQPixmap &)), this, TQT_SLOT(slotPaintBackground(const TQPixmap &))); + m_rootPixmap->setCustomPainting(true); + m_rootPixmap->start(); + } + + // Get root window attributes + XWindowAttributes rootAttr; + XGetWindowAttributes(tqt_xdisplay(), RootWindow(tqt_xdisplay(), tqt_xscreen()), &rootAttr); + { // trigger creation of QToolTipManager, it does XSelectInput() on the root window + TQWidget w; + TQToolTip::add( &w, "foo" ); + } + XSelectInput( tqt_xdisplay(), tqt_xrootwin(), SubstructureNotifyMask | rootAttr.your_event_mask ); + + // Add non-TDE path + TDEGlobal::dirs()->addResourceType("scrsav", + TDEGlobal::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; + } + TDEGlobal::dirs()->addResourceType("scrsav", + TDEGlobal::dirs()->kde_default("apps") + + relPath); + + // virtual root property + gXA_VROOT = XInternAtom (tqt_xdisplay(), "__SWM_VROOT", False); + gXA_SCREENSAVER_VERSION = XInternAtom (tqt_xdisplay(), "_SCREENSAVER_VERSION", False); + + TQStringList dmopt = + TQStringList::split(TQChar(','), + TQString::fromLatin1( ::getenv( "XDM_MANAGED" ))); + for (TQStringList::ConstIterator it = dmopt.begin(); it != dmopt.end(); ++it) + if ((*it).startsWith("method=")) + mMethod = (*it).mid(7); + +#ifdef KEEP_MOUSE_UNGRABBED + setEnabled(false); +#endif + + greetPlugin.library = 0; + + TDECrash::setCrashHandler(segv_handler); +} + +//--------------------------------------------------------------------------- +// +// Destructor - usual cleanups. +// +LockProcess::~LockProcess() +{ + mControlPipeHandler->terminateThread(); + mControlPipeHandlerThread->wait(); + delete mControlPipeHandler; +// delete mControlPipeHandlerThread; + + if (resizeTimer != NULL) { + resizeTimer->stop(); + delete resizeTimer; + } + if (hackResumeTimer != NULL) { + hackResumeTimer->stop(); + delete hackResumeTimer; + } + if (mEnsureScreenHiddenTimer != NULL) { + mEnsureScreenHiddenTimer->stop(); + delete mEnsureScreenHiddenTimer; + } + if (mForceContinualLockDisplayTimer != NULL) { + mForceContinualLockDisplayTimer->stop(); + delete mForceContinualLockDisplayTimer; + } + if (mHackDelayStartupTimer != NULL) { + mHackDelayStartupTimer->stop(); + delete mHackDelayStartupTimer; + } + if (mEnsureVRootWindowSecurityTimer != NULL) { + mEnsureVRootWindowSecurityTimer->stop(); + delete mEnsureVRootWindowSecurityTimer; + } + + if (greetPlugin.library) { + if (greetPlugin.info->done) + greetPlugin.info->done(); + greetPlugin.library->unload(); + } + + if (m_rootPixmap) { + m_rootPixmap->stop(); + delete m_rootPixmap; + } + + mPipeOpen = false; + mPipeOpen_out = false; +} + +//--------------------------------------------------------------------------- +// +// Initialization for startup +// This is where instance settings should be set--all objects should have already been created in the constructor above +// +void LockProcess::init(bool child, bool useBlankOnly) +{ + // Get root window size + XWindowAttributes rootAttr; + XGetWindowAttributes(tqt_xdisplay(), RootWindow(tqt_xdisplay(), tqt_xscreen()), &rootAttr); + mRootWidth = rootAttr.width; + mRootHeight = rootAttr.height; + generateBackingImages(); + + // Connect all signals + connect( mForceContinualLockDisplayTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(displayLockDialogIfNeeded()) ); + connect( mHackDelayStartupTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(closeDialogAndStartHack()) ); + connect( mEnsureVRootWindowSecurityTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(repaintRootWindowIfNeeded()) ); + connect(tqApp, TQT_SIGNAL(mouseInteraction(XEvent *)), TQT_SLOT(slotMouseActivity(XEvent *))); + connect(&mHackProc, TQT_SIGNAL(processExited(TDEProcess *)), TQT_SLOT(hackExited(TDEProcess *))); + connect(&mSuspendTimer, TQT_SIGNAL(timeout()), TQT_SLOT(suspend())); + +#ifdef HAVE_DPMS + //if the user decided that the screensaver should run independent from + //dpms, we shouldn't check for it, aleXXX + if (KDesktopSettings::dpmsDependent()) { + BOOL on; + CARD16 state; + if (DPMSInfo(tqt_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 (TQT_VERSION-0 >= 0x030200) // XRANDR support + connect( kapp->desktop(), TQT_SIGNAL( resized( int )), TQT_SLOT( desktopResized())); +#endif + + if (!trinity_desktop_lock_use_system_modal_dialogs) { + setWFlags((WFlags)WX11BypassWM); + } + + child_saver = child; + mUseBlankOnly = useBlankOnly; + + mShowLockDateTime = KDesktopSettings::showLockDateTime(); + mlockDateTime = TQDateTime::currentDateTime(); + + mHackDelayStartupTimeout = trinity_desktop_lock_delay_screensaver_start?KDesktopSettings::timeout()*1000:10*1000; + mHackStartupEnabled = trinity_desktop_lock_use_system_modal_dialogs?KDesktopSettings::screenSaverEnabled():true; + + configure(); + + mControlPipeHandlerThread = new TQEventLoopThread(); + mControlPipeHandler = new ControlPipeHandlerObject(); + mControlPipeHandler->mParent = this; + mControlPipeHandler->moveToThread(mControlPipeHandlerThread); + TQObject::connect(mControlPipeHandler, SIGNAL(processCommand(TQString)), this, SLOT(processInputPipeCommand(TQString))); + TQTimer::singleShot(0, mControlPipeHandler, SLOT(run())); + mControlPipeHandlerThread->start(); +} + +static int signal_pipe[2]; + +static void sigterm_handler(int) +{ + if ((!trinity_desktop_lock_process) || (!trinity_desktop_lock_process->inSecureDialog())) { + // Exit uncleanly + char tmp = 'U'; + if (::write( signal_pipe[1], &tmp, 1) == -1) { + // Error handler to shut up gcc warnings + } + } +} + +static void sighup_handler(int) +{ + char tmp = 'H'; + if (::write( signal_pipe[1], &tmp, 1) == -1) { + // Error handler to shut up gcc warnings + } +} + +bool LockProcess::closeCurrentWindow() +{ + mClosingWindows = TRUE; + if (currentDialog != NULL) { + mForceReject = true; + if (dynamic_cast<SAKDlg*>(currentDialog)) { + dynamic_cast<SAKDlg*>(currentDialog)->closeDialogForced(); + } + else if (dynamic_cast<SecureDlg*>(currentDialog)) { + dynamic_cast<SecureDlg*>(currentDialog)->closeDialogForced(); + } + else { + currentDialog->close(); + } + } + + if( mDialogs.isEmpty() ) { + mClosingWindows = FALSE; + mForceReject = false; + return false; + } + else { + mClosingWindows = TRUE; + return true; + } +} + +void LockProcess::timerEvent(TQTimerEvent *ev) +{ + if (mAutoLogout && ev->timerId() == mAutoLogoutTimerId) + { + killTimer(mAutoLogoutTimerId); + AutoLogout autologout(this); + execDialog(&autologout); + } +} + +void LockProcess::resizeEvent(TQResizeEvent *) +{ + // +} + +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 uncleanly 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); + + if (pipe(signal_pipe) == -1) { + // Error handler to shut up gcc warnings + } + 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; + if (::read( signal_pipe[0], &tmp, 1) == -1) { + // Error handler to shut up gcc warnings + } + if( tmp == 'T' ) { + quitSaver(); + } + else if( tmp == 'H' ) { + if( !mLocked ) + startLock(); + } + else if( tmp == 'U' ) { + // Exit uncleanly + quitSaver(); + exit(1); + } +} + +//--------------------------------------------------------------------------- +bool LockProcess::lock() +{ +#ifdef USE_SECURING_DESKTOP_NOTIFICATION + m_startupStatusDialog = new KSMModalDialog(this); + m_startupStatusDialog->setStatusMessage(i18n("Securing desktop session").append("...")); + m_startupStatusDialog->show(); + m_startupStatusDialog->setActiveWindow(); + tqApp->processEvents(); +#endif + + if (startSaver(true)) { + // 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; + mOverrideHackStartupEnabled = true; + if (startSaver()) { + if (mLockGrace >= 0) + TQTimer::singleShot(mLockGrace, this, TQT_SLOT(startLock())); + return true; + } + mOverrideHackStartupEnabled = false; + return false; +} + +//--------------------------------------------------------------------------- +bool LockProcess::dontLock() +{ + mLocked = false; + return startSaver(); +} + +//--------------------------------------------------------------------------- +void LockProcess::quitSaver() +{ + DISABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (closeCurrentWindow()) { + TQTimer::singleShot( 0, this, SLOT(quitSaver()) ); + return; + } + stopSaver(); + kapp->quit(); +} + +//--------------------------------------------------------------------------- +void LockProcess::startSecureDialog() +{ + if ((backingPixmap.isNull()) && (mBackingStartupDelayTimer < 100)) { + TQTimer::singleShot(10, this, TQT_SLOT(startSecureDialog())); + mBackingStartupDelayTimer++; + return; + } + + setGeometry(0, 0, mRootWidth, mRootHeight); + saverReadyIfNeeded(); + + int ret; + SecureDlg inDlg( this ); + inDlg.setRetInt(&ret); + mBusy = true; + execDialog( &inDlg ); + mBusy = false; + bool forcecontdisp = mForceContinualLockDisplayTimer->isActive(); + if (forcecontdisp) { + DISABLE_CONTINUOUS_LOCKDLG_DISPLAY + } + mInSecureDialog = false; + if (ret == 0) { + mClosingWindows = 1; + kapp->quit(); + } + if (ret == 1) { + // 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; + trinity_desktop_lock_forced = true; + // Make sure the cursor is not showing busy status + setCursor( tqarrowCursor ); + if (startLock()) + { + if (trinity_desktop_lock_delay_screensaver_start) { + mBusy = false; + } + else { + TQTimer::singleShot(1000, this, TQT_SLOT(slotDeadTimePassed())); + } + if (trinity_desktop_lock_delay_screensaver_start && trinity_desktop_lock_forced && trinity_desktop_lock_use_system_modal_dialogs) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + if (mHackStartupEnabled == true) { + startHack(); + } + else { + if (trinity_desktop_lock_use_system_modal_dialogs == true) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + startHack(); + } + } + } + return; + } + stopSaver(); + mBusy = false; + return; + } + if (ret == 2) { + mClosingWindows = 1; + if (system("ksysguard &") == -1) { + // Error handler to shut up gcc warnings + } + kapp->quit(); + } + if (ret == 3) { + mClosingWindows = 1; + DCOPRef("ksmserver","ksmserver").send("logout", (int)TDEApplication::ShutdownConfirmYes, (int)TDEApplication::ShutdownTypeNone, (int)TDEApplication::ShutdownModeInteractive); + kapp->quit(); + } + // FIXME + // Handle remaining case (switch user) + if (forcecontdisp) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + stopSaver(); +} + +bool LockProcess::runSecureDialog() +{ +#ifdef USE_SECURING_DESKTOP_NOTIFICATION + m_startupStatusDialog = new KSMModalDialog(this); + m_startupStatusDialog->setStatusMessage(i18n("Securing desktop session").append("...")); + m_startupStatusDialog->show(); + m_startupStatusDialog->setActiveWindow(); + tqApp->processEvents(); +#endif + + mInSecureDialog = true; + if (startSaver()) { + mBackingStartupDelayTimer = 0; + TQTimer::singleShot(0, this, TQT_SLOT(startSecureDialog())); + return true; + } + else { + mInSecureDialog = false; + return false; + } +} + +bool LockProcess::inSecureDialog() +{ + return mInSecureDialog; +} + +//--------------------------------------------------------------------------- +// +// 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 + } + + mPriority = KDesktopSettings::priority(); + if (mPriority < 0) mPriority = 0; + if (mPriority > 19) mPriority = 19; + + mSaver = KDesktopSettings::saver(); + if (mSaver.isEmpty() || mUseBlankOnly) { + mSaver = "KBlankscreen.desktop"; + } + if (!trinity_desktop_lock_use_system_modal_dialogs) { + if (KDesktopSettings::screenSaverEnabled() == false) { + mSaver = ""; + mSaverExec = ""; + } + } + + 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-TDE-Type").utf8() != 0) + { + TQString saverType = config.readEntry("X-TDE-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 (trinity_desktop_lock_use_system_modal_dialogs) { + if (config.hasActionGroup("InWindow")) + { + config.setActionGroup("InWindow"); + mSaverExec = config.readPathEntry("Exec"); + } + } + else { + 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; + XVisualInfo* info = NULL; + int flags = trinity_desktop_lock_use_system_modal_dialogs?0: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 ) { + int n_glxfb_configs; + GLXFBConfig *fbc = glXChooseFBConfig( x11Display(), x11Screen(), attribs[ i ], &n_glxfb_configs); + if (!fbc) { + n_glxfb_configs = 0; + } + for( int j = 0; j < n_glxfb_configs; j++ ) { + info = glXGetVisualFromFBConfig(x11Display(), fbc[j]); + if( info ) { + if (argb_visual) { + // Xorg can only use GPU compositing for ARGB32 8:8:8:8 visuals + // Ensure the selected visual is 8 bits per RGB + // Selecting a non-8-bit visual will result in stuttering and high + // CPU load, while Xorg tries to composite each frame on the CPU! + if ((info->depth < 32) || (info->bits_per_rgb != 8)) { + XFree( info ); + info = NULL; + continue; + } + } + 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; + break; + } + } + if (flags & CWColormap) { + break; + } + } + if ( !info ) + { + printf("[WARNING] Unable to locate matching X11 GLX Visual; this OpenGL application may not function correctly!\n"); + } + } +#endif + + attrs.override_redirect = 1; + hide(); + + if (argb_visual) { + // The GL visual selection can return a visual with invalid depth + // Check for this and use a fallback visual if needed + if (info && (info->depth < 32)) { + printf("[WARNING] Unable to locate matching X11 GLX Visual; this OpenGL application may not function correctly!\n"); + XFree( info ); + info = NULL; + flags &= ~CWColormap; + } + + attrs.background_pixel = 0; + attrs.border_pixel = 0; + flags |= CWBackPixel; + flags |= CWBorderPixel; + if (!(flags & CWColormap)) { + if (!info) { + info = new XVisualInfo; + if (!XMatchVisualInfo( x11Display(), x11Screen(), 32, TrueColor, info )) { + printf("[ERROR] Unable to locate matching X11 Visual; this application will not function correctly!\n"); + free(info); + info = NULL; + } + } + if (info) { + visual = info->visual; + attrs.colormap = XCreateColormap( x11Display(), RootWindow( x11Display(), x11Screen()), visual, AllocNone ); + flags |= CWColormap; + } + } + } + if (info) { + XFree( info ); + } + + m_saverRootWindow = XCreateWindow( x11Display(), RootWindow( x11Display(), x11Screen()), x(), y(), width(), height(), 0, x11Depth(), InputOutput, visual, flags, &attrs ); + create( m_saverRootWindow ); + + // Some xscreensaver hacks check for this property + const char *version = "KDE 2.0"; + XChangeProperty (tqt_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(tqt_xdisplay(), winId(), CWEventMask, &attr); + + // Signal that we want to be transparent to the desktop, not to windows behind us... + XChangeProperty(tqt_xdisplay(), m_saverRootWindow, kde_wm_transparent_to_desktop, XA_INTEGER, 32, PropModeReplace, (unsigned char *) "TRUE", 1L); + + // erase(); + + // set NoBackground so that the saver can capture the current + // screen state if necessary + // this is a security risk and has been deactivated--welcome to the 21st century folks! + // setBackgroundMode(TQWidget::NoBackground); + + setGeometry(0, 0, mRootWidth, mRootHeight); + saverReadyIfNeeded(); + + // HACK + // Hide all tooltips and notification windows + { + Window rootWindow = RootWindow(x11Display(), x11Screen()); + Window parent; + Window* children = NULL; + unsigned int noOfChildren = 0; + XWindowAttributes childAttr; + Window childTransient; + + if (XQueryTree(x11Display(), rootWindow, &rootWindow, &parent, &children, &noOfChildren) && noOfChildren>0 ) { + for (unsigned int i=0; i<noOfChildren; i++) { + if (XGetWindowAttributes(x11Display(), children[i], &childAttr) && XGetTransientForHint(x11Display(), children[i], &childTransient)) { + if ((childAttr.map_state == IsViewable) && (childAttr.override_redirect) && (childTransient)) { + if (!trinity_desktop_lock_hidden_window_list.contains(children[i])) { + trinity_desktop_lock_hidden_window_list.append(children[i]); + } + XLowerWindow(x11Display(), children[i]); + XFlush(x11Display()); + } + } + } + } + } + + kdDebug(1204) << "Saver window Id: " << winId() << endl; +} + +void LockProcess::desktopResized() +{ + // Get root window size + XWindowAttributes rootAttr; + XGetWindowAttributes(tqt_xdisplay(), RootWindow(tqt_xdisplay(), tqt_xscreen()), &rootAttr); + if ((rootAttr.width == mRootWidth) && (rootAttr.height == mRootHeight)) { + return; + } + mRootWidth = rootAttr.width; + mRootHeight = rootAttr.height; + generateBackingImages(); + + mBusy = true; + mHackDelayStartupTimer->stop(); + stopHack(); + DISABLE_CONTINUOUS_LOCKDLG_DISPLAY + mResizingDesktopLock = true; + + backingPixmap = TQPixmap(); + + if (trinity_desktop_lock_use_system_modal_dialogs) { + // Temporarily hide the entire screen with a new override redirect window + if (m_maskWidget) { + m_maskWidget->setGeometry(0, 0, mRootWidth, mRootHeight); + } + else { + m_maskWidget = new TQWidget(0, 0, TQt::WStyle_StaysOnTop | TQt::WX11BypassWM); + m_maskWidget->setGeometry(0, 0, mRootWidth, mRootHeight); + m_maskWidget->setBackgroundColor(TQt::black); + m_maskWidget->erase(); + m_maskWidget->show(); + } + XSync(tqt_xdisplay(), False); + saverReadyIfNeeded(); + + if (mEnsureScreenHiddenTimer) { + mEnsureScreenHiddenTimer->stop(); + } + else { + mEnsureScreenHiddenTimer = new TQTimer( this ); + connect( mEnsureScreenHiddenTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotForcePaintBackground()) ); + } + mEnsureScreenHiddenTimer->start(DESKTOP_WALLPAPER_OBTAIN_TIMEOUT_MS, true); + } + + // Resize the background widget + setGeometry(0, 0, mRootWidth, mRootHeight); + XSync(tqt_xdisplay(), False); + saverReadyIfNeeded(); + + // Black out the background widget to hide ugly resize tiling artifacts + if (argb_visual) { + setTransparentBackgroundARGB(); + } + else { + setBackgroundColor(black); + } + erase(); + + // 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() +{ + while (mDialogControlLock == true) { + usleep(100000); + } + mDialogControlLock = true; + if (closeCurrentWindow()) { + TQTimer::singleShot( 0, this, SLOT(doDesktopResizeFinish()) ); + mDialogControlLock = false; + return; + } + mDialogControlLock = false; + + // Restart the hack as the window size is now different + if (trinity_desktop_lock_delay_screensaver_start && trinity_desktop_lock_use_system_modal_dialogs) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + if (mHackStartupEnabled == true) { + startHack(); + } + else { + if (trinity_desktop_lock_use_system_modal_dialogs == true) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + startHack(); + } + } + } + + mResizingDesktopLock = false; + mBusy = false; +} + +//--------------------------------------------------------------------------- +// +// Hide the screensaver window +// +void LockProcess::hideSaverWindow() +{ + hide(); + lower(); + removeVRoot(winId()); + XDeleteProperty(tqt_xdisplay(), winId(), gXA_SCREENSAVER_VERSION); + if ( gVRoot ) { + unsigned long vroot_data[1] = { gVRootData }; + XChangeProperty(tqt_xdisplay(), gVRoot, gXA_VROOT, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)vroot_data, 1); + gVRoot = 0; + } + XSync(tqt_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(tqt_xdisplay(), tqt_xscreen())); + + gVRoot = 0; + gVRootData = 0; + + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(ignoreXError); + + if (XQueryTree(tqt_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(tqt_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(tqt_xdisplay(), tqt_xscreen())); + unsigned long vroot_data[1] = { vr }; + + Window rootReturn; + Window parentReturn; + Window *children = NULL; + unsigned int numChildren; + Window top = win; + while (1) { + if (XQueryTree(tqt_xdisplay(), top, &rootReturn, &parentReturn, &children, &numChildren) == 0) { + printf("[WARNING] XQueryTree() failed!\n"); fflush(stdout); + break; + } + if (children) { + XFree((char *)children); + } + if (parentReturn == rw) { + break; + } + else { + top = parentReturn; + } + } + + XChangeProperty(tqt_xdisplay(), top, gXA_VROOT, XA_WINDOW, 32, PropModeReplace, (unsigned char *)vroot_data, 1); +} + +//--------------------------------------------------------------------------- +// +// Remove the virtual root property +// +void LockProcess::removeVRoot(Window win) +{ + XDeleteProperty (tqt_xdisplay(), win, gXA_VROOT); +} + +//--------------------------------------------------------------------------- +// +// Grab the keyboard. Returns true on success +// +bool LockProcess::grabKeyboard() +{ + int rv = XGrabKeyboard( tqt_xdisplay(), TQApplication::desktop()->winId(), + True, GrabModeAsync, GrabModeAsync, CurrentTime ); + + if (rv != GrabSuccess) { + kdWarning(1204) << "LockProcess::grabKeyboard() failed: " << rv << endl; + } + return (rv == GrabSuccess); +} + +#define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \ + EnterWindowMask | LeaveWindowMask + +//--------------------------------------------------------------------------- +// +// Grab the mouse. Returns true on success +// +bool LockProcess::grabMouse() +{ + HANDLE cursorHandle; + if (mHackActive) { + cursorHandle = TQCursor(tqblankCursor).handle(); + } + else { + cursorHandle = TQCursor(tqbusyCursor).handle(); + } + int rv = XGrabPointer( tqt_xdisplay(), TQApplication::desktop()->winId(), + True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None, + cursorHandle, CurrentTime ); + + if (rv != GrabSuccess) { + kdWarning(1204) << "LockProcess::grabMouse() failed: " << rv << endl; + } + return (rv == GrabSuccess); +} + +//--------------------------------------------------------------------------- +// +// Grab keyboard and mouse. Returns true on success. +// +bool LockProcess::grabInput() +{ + XSync(tqt_xdisplay(), False); + + if (!grabKeyboard()) + { + usleep(100000); + if (!grabKeyboard()) + { + return false; + } + } + +#ifndef KEEP_MOUSE_UNGRABBED + if (!grabMouse()) + { + usleep(100000); + if (!grabMouse()) + { + XUngrabKeyboard(tqt_xdisplay(), CurrentTime); + return false; + } + } +#endif + + lockXF86(); + + return true; +} + +//--------------------------------------------------------------------------- +// +// Release mouse an keyboard grab. +// +void LockProcess::ungrabInput() +{ + XUngrabKeyboard(tqt_xdisplay(), CurrentTime); + XUngrabPointer(tqt_xdisplay(), CurrentTime); + unlockXF86(); +} + +//--------------------------------------------------------------------------- +// +// Generate requisite backing images for ARGB mode +// +void LockProcess::generateBackingImages() +{ + if (argb_visual) { + mArgbTransparentBackgroundPixmap.resize(mRootWidth, mRootHeight); + TQPainter p; + p.begin( &mArgbTransparentBackgroundPixmap ); + p.fillRect( 0, 0, mArgbTransparentBackgroundPixmap.width(), mArgbTransparentBackgroundPixmap.height(), TQBrush(tqRgba(0, 0, 0, 0)) ); + p.end(); + } +} + +//--------------------------------------------------------------------------- +// +// Set a fully transparent ARGB background image. +// +void LockProcess::setTransparentBackgroundARGB() +{ + // eliminate nasty flicker on first show + setBackgroundPixmap( mArgbTransparentBackgroundPixmap ); +} + +void LockProcess::saverReadyIfNeeded() +{ + if (m_notifyReadyRequested) { + // Make sure the desktop is hidden before notifying the desktop that the saver is running + m_notifyReadyRequested = false; + saverReady(); + fullyOnline(); + } +} + +//--------------------------------------------------------------------------- +// +// Start the screen saver. +// +bool LockProcess::startSaver(bool notify_ready) +{ + if (!child_saver && !grabInput()) + { + kdWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" << endl; + return false; + } + mBusy = false; + + // eliminate nasty flicker on first show + setTransparentBackgroundARGB(); + + 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(); + + raise(); + XSync(tqt_xdisplay(), False); + setVRoot( winId(), winId() ); + + if (!trinity_desktop_lock_hide_active_windows) { + if (m_rootPixmap) m_rootPixmap->stop(); + TQPixmap rootWinSnapShot = TQPixmap::grabWindow(TQApplication::desktop()->winId()); + slotPaintBackground(rootWinSnapShot); + } + + if (((!(trinity_desktop_lock_delay_screensaver_start && trinity_desktop_lock_forced)) && (!mInSecureDialog)) && (mHackStartupEnabled || mOverrideHackStartupEnabled)) { + if (argb_visual) { + setTransparentBackgroundARGB(); + } + else { + if (backingPixmap.isNull()) { + setBackgroundColor(black); + } + else { + setBackgroundPixmap(backingPixmap); + } + } + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + + if (notify_ready) { + m_notifyReadyRequested = false; + saverReady(); + fullyOnline(); + } + } + else { + if (notify_ready) { + m_notifyReadyRequested = true; + } + } + + if (mInSecureDialog == FALSE) { + if (trinity_desktop_lock_delay_screensaver_start && trinity_desktop_lock_forced && trinity_desktop_lock_use_system_modal_dialogs) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + if (mHackStartupEnabled || mOverrideHackStartupEnabled) { + mOverrideHackStartupEnabled = false; + startHack(); + } + else { + if (trinity_desktop_lock_use_system_modal_dialogs == true) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + startHack(); + } + } + } + } + + return true; +} + +//--------------------------------------------------------------------------- +// +// Stop the screen saver. +// +void LockProcess::stopSaver() +{ + kdDebug(1204) << "LockProcess: stopping saver" << endl; + mHackProc.kill(SIGCONT); + stopHack(); + mSuspended = false; + 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) + if (write(*it, out, sizeof(out)) == -1) { + // Error handler to shut up gcc warnings + } + } +} + +// private static +TQVariant LockProcess::getConf(void *ctx, const char *key, const TQVariant &dflt) +{ + LockProcess *that = (LockProcess *)ctx; + TQString fkey = TQString::fromLatin1( 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; +} + +//--------------------------------------------------------------------------- +// + +void LockProcess::closeDialogAndStartHack() +{ +#ifdef HAVE_DPMS + if (KDesktopSettings::dpmsDependent()) { + BOOL on; + CARD16 state; + if (DPMSInfo(tqt_xdisplay(), &state, &on)) { + //kdDebug() << "checkDPMSActive " << on << " " << state << endl; + if (DPMS_MONITOR_BLANKED(state)) { + // Make sure saver will attempt to start again after DPMS wakeup + // This is related to Bug 1475 + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + // Should not start saver here, because the DPMS check method below would turn it right back off! + // This is related to Bug 1475 + return; + } + } + } +#endif + + // Close any active dialogs + DISABLE_CONTINUOUS_LOCKDLG_DISPLAY + mSuspended = true; + if (closeCurrentWindow()) { + TQTimer::singleShot( 0, this, SLOT(closeDialogAndStartHack()) ); + } + else { + resume(true); + } +} + +void LockProcess::repaintRootWindowIfNeeded() +{ + if (trinity_desktop_lock_use_system_modal_dialogs) { + if (!mHackProc.isRunning()) { + if (argb_visual) { + setTransparentBackgroundARGB(); + erase(); + } + else { + if (backingPixmap.isNull()) { + setBackgroundColor(black); + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + } + else { + bitBlt(this, 0, 0, &backingPixmap); + } + } + } + if (currentDialog == NULL) { + raise(); + } + saverReadyIfNeeded(); + } +} + +bool LockProcess::startHack() +{ + mHackActive = TRUE; + + if ((mEnsureVRootWindowSecurityTimer) && (!mEnsureVRootWindowSecurityTimer->isActive())) mEnsureVRootWindowSecurityTimer->start(250, FALSE); + + if (currentDialog || (!mDialogs.isEmpty())) + { + // no resuming with dialog visible or when not visible + if (argb_visual) { + setTransparentBackgroundARGB(); + } + else { + if (backingPixmap.isNull()) { + setBackgroundColor(black); + } + else { + setBackgroundPixmap(backingPixmap); + } + } + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + saverReadyIfNeeded(); + return false; + } + + setCursor( tqblankCursor ); + XChangeActivePointerGrab( tqt_xdisplay(), GRABEVENTS, TQCursor(tqblankCursor).handle(), CurrentTime); + + if (mSaverExec.isEmpty()) + { + return false; + } + + if (mHackProc.isRunning()) + { + stopHack(); + } + + mHackProc.clearArguments(); + + TQTextStream ts(&mSaverExec, IO_ReadOnly); + TQString word; + ts >> word; + TQString path = TDEStandardDirs::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 (trinity_desktop_lock_use_system_modal_dialogs) { + // Make sure we have a nice clean display to start with! + if (argb_visual) { + // Signal that we want to be transparent to a black background... + if (m_saverRootWindow) { + XChangeProperty(tqt_xdisplay(), m_saverRootWindow, kde_wm_transparent_to_black, XA_INTEGER, 32, PropModeReplace, (unsigned char *) "TRUE", 1L); + XClearArea(tqt_xdisplay(), m_saverRootWindow, 0, 0, 0, 0, True); + } + setTransparentBackgroundARGB(); + } + else { + if (backingPixmap.isNull()) { + setBackgroundColor(black); + } + else { + setBackgroundPixmap(backingPixmap); + } + } + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + saverReadyIfNeeded(); + mSuspended = false; + } + + XChangeActivePointerGrab( tqt_xdisplay(), GRABEVENTS, TQCursor(tqblankCursor).handle(), CurrentTime); + if (mHackProc.start() == true) + { +#ifdef HAVE_SETPRIORITY + setpriority(PRIO_PROCESS, mHackProc.pid(), mPriority); +#endif + //bitBlt(this, 0, 0, &mOriginal); + DISABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (trinity_desktop_lock_delay_screensaver_start && trinity_desktop_lock_forced) { + // Close any active dialogs + if (closeCurrentWindow()) { + TQTimer::singleShot( 0, this, SLOT(closeCurrentWindow()) ); + } + } + if (m_startupStatusDialog) { m_startupStatusDialog->closeSMDialog(); m_startupStatusDialog=NULL; } + 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 + { + usleep(100); + TQApplication::syncX(); + if (!trinity_desktop_lock_use_system_modal_dialogs) { + if (argb_visual) { + setTransparentBackgroundARGB(); + } + else { + if (backingPixmap.isNull()) { + setBackgroundColor(black); + } + else { + setBackgroundPixmap(backingPixmap); + } + } + } + if (argb_visual) { + setTransparentBackgroundARGB(); + erase(); + } + else { + if (backingPixmap.isNull()) { + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + } + else { + bitBlt(this, 0, 0, &backingPixmap); + } + } + if (trinity_desktop_lock_use_system_modal_dialogs) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + saverReadyIfNeeded(); + } + } + if (m_startupStatusDialog) { m_startupStatusDialog->closeSMDialog(); m_startupStatusDialog=NULL; } + return false; +} + +//--------------------------------------------------------------------------- +// +void LockProcess::stopHack() +{ + if (mHackProc.isRunning()) + { + mHackProc.kill(); + if (!mHackProc.wait(10)) + { + mHackProc.kill(SIGKILL); + } + } + setCursor( tqarrowCursor ); + + mHackActive = FALSE; +} + +//--------------------------------------------------------------------------- +// +void LockProcess::hackExited(TDEProcess *) +{ + // Hack exited while we're supposed to be saving the screen. + // Make sure the saver window is black. + mHackActive = FALSE; + usleep(100); + TQApplication::syncX(); + if (!trinity_desktop_lock_use_system_modal_dialogs) { + if (argb_visual) { + setTransparentBackgroundARGB(); + } + else { + if (backingPixmap.isNull()) { + setBackgroundColor(black); + } + else { + setBackgroundPixmap(backingPixmap); + } + } + } + if (argb_visual) { + if (m_saverRootWindow) { + XDeleteProperty(tqt_xdisplay(), m_saverRootWindow, kde_wm_transparent_to_black); + XClearArea(tqt_xdisplay(), m_saverRootWindow, 0, 0, 0, 0, True); + } + setTransparentBackgroundARGB(); + } + else { + if (backingPixmap.isNull()) { + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + } + else { + bitBlt(this, 0, 0, &backingPixmap); + } + } + if (!mSuspended) { + if (trinity_desktop_lock_use_system_modal_dialogs) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + } + saverReadyIfNeeded(); +} + +void LockProcess::displayLockDialogIfNeeded() +{ + if (m_startupStatusDialog) { + m_startupStatusDialog->closeSMDialog(); + m_startupStatusDialog = NULL; + } + if (!mInSecureDialog) { + if (trinity_desktop_lock_use_system_modal_dialogs) { + if (!mBusy) { + mBusy = true; + if (mLocked) { + if (checkPass()) { + mClosingWindows = true; + stopSaver(); + kapp->quit(); + } + } + mBusy = false; + } + } + } +} + +void LockProcess::suspend() +{ + if(!mSuspended) + { + if (trinity_desktop_lock_use_system_modal_dialogs) { + mSuspended = true; + stopHack(); + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + TQString hackStatus; + mHackProc.kill(SIGSTOP); + mSuspended = true; +#if 0 + // wait for the stop signal to take effect + while (hackStatus != "T") { + char hackstat[8192]; + FILE *fp = fopen(TQString("/proc/%1/stat").arg(mHackProc.pid()).ascii(),"r"); + if (fp != NULL) { + fgets (hackstat, 8192, fp); + fclose (fp); + } + hackstat[8191] = 0; + hackStatus = hackstat; + hackStatus = hackStatus.remove(TQRegExp("(*) ", TRUE, TRUE)); + TQStringList hackStatusList = TQStringList::split(" ", hackStatus); + hackStatus = (*(hackStatusList.at(1))); + } +#endif + TQApplication::syncX(); + usleep(100000); // Allow certain bad graphics drivers (*cough* fglrx *cough*) time to actually sync up the display + } + TQApplication::syncX(); + mSavedScreen = TQPixmap::grabWindow( winId()); + } +} + +void LockProcess::resume( bool force ) +{ + if (trinity_desktop_lock_use_sak && (mHackDelayStartupTimer->isActive() || !mHackStartupEnabled)) { + return; + } + if( !force && (!mDialogs.isEmpty() || !mVisibility )) { + // no resuming with dialog visible or when not visible + if (trinity_desktop_lock_use_system_modal_dialogs) { + if (argb_visual) { + setTransparentBackgroundARGB(); + } + else { + if (backingPixmap.isNull()) { + setBackgroundColor(black); + } + else { + setBackgroundPixmap(backingPixmap); + } + } + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + } + else { + setGeometry(0, 0, mRootWidth, mRootHeight); + } + saverReadyIfNeeded(); + return; + } + if ((mSuspended) && (mHackProc.isRunning())) + { + XForceScreenSaver(tqt_xdisplay(), ScreenSaverReset ); + bitBlt( this, 0, 0, &mSavedScreen ); + TQApplication::syncX(); + mHackProc.kill(SIGCONT); + mSuspended = false; + } + else if (mSuspended && trinity_desktop_lock_use_system_modal_dialogs) { + startHack(); + } +} + +//--------------------------------------------------------------------------- +// +// Show the password dialog +// This is called only in the master process +// +bool LockProcess::checkPass() +{ + if (!mDialogs.isEmpty()) { + // Another dialog is already shown + // Abort! + return 0; + } + if (mInfoMessageDisplayed == false) { + if (mAutoLogout) { + killTimer(mAutoLogoutTimerId); + } + + // Make sure we never launch the SAK or login dialog if windows are being closed down + // Otherwise we can get stuck in an irrecoverable state where any attempt to show the login screen is instantly aborted + if (mClosingWindows) { + return 0; + } + + if (trinity_desktop_lock_use_sak) { + // Verify SAK operational status + TDEProcess* checkSAKProcess = new TDEProcess; + *checkSAKProcess << "tdmtsak" << "check"; + checkSAKProcess->start(TDEProcess::Block, TDEProcess::NoCommunication); + int retcode = checkSAKProcess->exitStatus(); + delete checkSAKProcess; + if (retcode != 0) { + trinity_desktop_lock_use_sak = false; + } + } + + if (trinity_desktop_lock_use_sak) { + // Wait for SAK press before continuing... + SAKDlg inDlg( this ); + execDialog( &inDlg ); + if (mClosingWindows) { + return 0; + } + } + + showVkbd(); + PasswordDlg passDlg( this, &greetPlugin, (mShowLockDateTime)?mlockDateTime:TQDateTime()); + int ret = execDialog( &passDlg ); + hideVkbd(); + + if (mForceReject == true) { + ret = TQDialog::Rejected; + } + mForceReject = false; + + XWindowAttributes rootAttr; + XGetWindowAttributes(tqt_xdisplay(), RootWindow(tqt_xdisplay(), + tqt_xscreen()), &rootAttr); + if(( rootAttr.your_event_mask & SubstructureNotifyMask ) == 0 ) + { + kdWarning() << "ERROR: Something removed SubstructureNotifyMask from the root window!!!" << endl; + XSelectInput( tqt_xdisplay(), tqt_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 = tqt_xdisplay(); + ev.xfocus.type = FocusIn; + ev.xfocus.window = window; + ev.xfocus.mode = NotifyNormal; + ev.xfocus.detail = NotifyAncestor; + XSendEvent( tqt_xdisplay(), window, False, NoEventMask, &ev ); +} + +void LockProcess::resumeUnforced() +{ + resume( false ); +} + +int LockProcess::execDialog( TQDialog *dlg ) +{ + currentDialog=dlg; + dlg->adjustSize(); + + TQRect rect = dlg->geometry(); + rect.moveCenter(TDEGlobalSettings::desktopGeometry(TQCursor::pos()).center()); + dlg->move( rect.topLeft() ); + + if (mDialogs.isEmpty()) + { + suspend(); + XChangeActivePointerGrab( tqt_xdisplay(), GRABEVENTS, TQCursor(tqarrowCursor).handle(), CurrentTime); + } + mDialogs.prepend( dlg ); + fakeFocusIn( dlg->winId()); + if (trinity_desktop_lock_use_system_modal_dialogs) { + if (backingPixmap.isNull()) { + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + } + else { + bitBlt(this, 0, 0, &backingPixmap); + } + saverReadyIfNeeded(); + } + // dlg->exec may generate BadMatch errors, so make sure they are silently ignored + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(ignoreXError); + int rt = dlg->exec(); + XSetErrorHandler(oldHandler); + while (mDialogControlLock == true) usleep(100000); + currentDialog = NULL; + mDialogs.remove( dlg ); + if( mDialogs.isEmpty() ) { + HANDLE cursorHandle; + if (mHackActive) { + cursorHandle = TQCursor(tqblankCursor).handle(); + } + else { + cursorHandle = TQCursor(tqbusyCursor).handle(); + } + XChangeActivePointerGrab( tqt_xdisplay(), GRABEVENTS, cursorHandle, CurrentTime); + if (trinity_desktop_lock_use_system_modal_dialogs) { + // Slight delay before screensaver resume to allow the dialog window to fully disappear + if (hackResumeTimer == NULL) { + hackResumeTimer = new TQTimer( this ); + connect( hackResumeTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(resumeUnforced()) ); + } + if (mResizingDesktopLock == false) { + hackResumeTimer->start( 10, TRUE ); + } + } + else { + resume( false ); + } + } else { + fakeFocusIn( mDialogs.first()->winId()); + currentDialog = dynamic_cast<TQDialog*>(mDialogs.first()); + } + return rt; +} + +void LockProcess::slotForcePaintBackground() +{ + TQPixmap blankPixmap(mRootWidth, mRootHeight); + blankPixmap.fill(Qt::black); + slotPaintBackground(blankPixmap); + printf("[WARNING] Unable to obtain desktop wallpaper in a timely manner. High system load or possibly a TDE bug!\n"); fflush(stdout); +} + +void LockProcess::slotPaintBackground(const TQPixmap &rpm) +{ + if (argb_visual) { + if (mEnsureScreenHiddenTimer) { + mEnsureScreenHiddenTimer->stop(); + } + return; + } + + if (mEnsureScreenHiddenTimer) { + mEnsureScreenHiddenTimer->stop(); + } + else { + mEnsureScreenHiddenTimer = new TQTimer( this ); + connect( mEnsureScreenHiddenTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotForcePaintBackground()) ); + } + + // Only remove the mask widget once the resize is 100% complete! + if (m_maskWidget) { + delete m_maskWidget; + m_maskWidget = NULL; + XSync(tqt_xdisplay(), False); + } + + TQPixmap pm = rpm; + + if (TQPaintDevice::x11AppDepth() == 32) { + // Remove the alpha components from the image + TQImage correctedImage = pm.convertToImage(); + correctedImage = correctedImage.convertDepth(32); + correctedImage.setAlphaBuffer(true); + int w = correctedImage.width(); + int h = correctedImage.height(); + for (int y = 0; y < h; ++y) { + TQRgb *ls = (TQRgb *)correctedImage.scanLine( y ); + for (int x = 0; x < w; ++x) { + TQRgb l = ls[x]; + int r = int( tqRed( l ) ); + int g = int( tqGreen( l ) ); + int b = int( tqBlue( l ) ); + int a = int( 255 ); + ls[x] = tqRgba( r, g, b, a ); + } + } + pm.convertFromImage(correctedImage); + } + + backingPixmap = pm; + if ((trinity_desktop_lock_delay_screensaver_start && trinity_desktop_lock_forced) || (!mHackStartupEnabled)) { + setBackgroundPixmap(backingPixmap); + setGeometry(0, 0, mRootWidth, mRootHeight); + erase(); + } +} + +void LockProcess::preparePopup() +{ + TQWidget *dlg = (TQWidget *)sender(); + mDialogs.prepend( dlg ); + fakeFocusIn( dlg->winId() ); +} + +void LockProcess::cleanupPopup() +{ + TQWidget *dlg = (TQWidget *)sender(); + mDialogs.remove( dlg ); + if ( mDialogs.isEmpty() ) { + fakeFocusIn( mDialogs.first()->winId() ); + } +} + +void LockProcess::doFunctionKeyBroadcast() { + // Provide a clean, pretty display switch by hiding the password dialog here + // This does NOT work with the SAK or system modal dialogs! + if ((!trinity_desktop_lock_use_system_modal_dialogs) && (!trinity_desktop_lock_use_sak)) { + mBusy=true; + TQTimer::singleShot(1000, this, TQT_SLOT(slotDeadTimePassed())); + if (mkeyCode == XKeysymToKeycode(tqt_xdisplay(), XF86XK_Display)) { + while (mDialogControlLock == true) usleep(100000); + mDialogControlLock = true; + currentDialog->close(); // DO NOT use closeCurrentWindow() here! + mDialogControlLock = false; + } + } + + 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 + // XF86XK_PowerOff If someone has access to the power button, they can hard power off the machine anyway + // XF86XK_Sleep Ditto + // XF86XK_Suspend Ditto + // XF86XK_Hibernate Ditto + + //if ((event->type == KeyPress) || (event->type == KeyRelease)) { + if (event->type == KeyPress) { + // Multimedia keys + 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; + } + // ACPI power keys + if ((event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_PowerOff)) || \ + (event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_Sleep)) || \ + (event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_Suspend)) || \ + (event->xkey.keycode == XKeysymToKeycode(event->xkey.display, XF86XK_Hibernate))) { + 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 ((mHackDelayStartupTimer) && (mHackDelayStartupTimer->isActive())) { + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + if (mBusy || !mDialogs.isEmpty()) + break; + mBusy = true; + if (trinity_desktop_lock_delay_screensaver_start) { + if (mLocked) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + if ((!mLocked) && (!mInSecureDialog)) + { + stopSaver(); + kapp->quit(); + } + if (mAutoLogout) // we need to restart the auto logout countdown + { + killTimer(mAutoLogoutTimerId); + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout); + } + } + else { + if (!mLocked || checkPass()) + { + mClosingWindows = true; + 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(); + if (mResizingDesktopLock == false) { + if (trinity_desktop_lock_delay_screensaver_start && trinity_desktop_lock_forced && trinity_desktop_lock_use_system_modal_dialogs) { + // Do nothing + } + else { + if (mHackStartupEnabled == true) { + resume( false ); + } + else { + if (trinity_desktop_lock_use_system_modal_dialogs == true) { + ENABLE_CONTINUOUS_LOCKDLG_DISPLAY + if (mHackStartupEnabled) mHackDelayStartupTimer->start(mHackDelayStartupTimeout, TRUE); + } + else { + resume( false ); + } + } + } + } + } + if (event->xvisibility.state != VisibilityUnobscured) { + stayOnTop(); + } + } + break; + + case ConfigureNotify: // from SubstructureNotifyMask on the root window + if(event->xconfigure.event == tqt_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 == tqt_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( tqt_xdisplay(), mVkbdWindows.first().id ); + else + XRaiseWindow( tqt_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(tqt_xdisplay(), winId()); +} + +void LockProcess::checkDPMSActive() +{ +#ifdef HAVE_DPMS + if (KDesktopSettings::dpmsDependent()) { + BOOL on; + CARD16 state; + if (DPMSInfo(tqt_xdisplay(), &state, &on)) { + //kdDebug() << "checkDPMSActive " << on << " " << state << endl; + if (DPMS_MONITOR_BLANKED(state)) { + suspend(); + } + else if (mSuspended) { + if (mResizingDesktopLock == false) { + 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( tqt_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( tqt_xdisplay(), False ) != MiscExtGrabStateSuccess ) + return; + // success + mRestoreXF86Lock = true; +} + +void LockProcess::unlockXF86() +{ + if( can_do_xf86_lock != Yes ) + return; + if( !mRestoreXF86Lock ) + return; + XF86MiscSetGrabKeysState( tqt_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, (trinity_desktop_lock_use_system_modal_dialogs?((WFlags)WStyle_StaysOnTop):((WFlags)WX11BypassWM)) ); + if (trinity_desktop_lock_use_system_modal_dialogs) { + // Signal that we do not want any window controls to be shown at all + XChangeProperty(tqt_xdisplay(), box.winId(), kde_wm_system_modal_notification, XA_INTEGER, 32, PropModeReplace, (unsigned char *) "TRUE", 1L); + } + box.setCaption(i18n("Authentication Subsystem Notice")); + TQFrame *winFrame = new TQFrame( &box ); + if (trinity_desktop_lock_use_system_modal_dialogs) + winFrame->setFrameStyle( TQFrame::NoFrame ); + else + 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->setSizePolicy( 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 ) { +#ifdef WITH_HAL + 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 + && !TDEStandardDirs::findExe( "xvkbd" ).isEmpty()) ? 1 : 0; +#else // WITH_HAL + run_vkbd = (!TDEStandardDirs::findExe( "xvkbd" ).isEmpty()); +#endif // WITH_HAL + } + 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 TDEProcess; + *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 may generate BadWindow errors, so make sure they are silently ignored + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(ignoreXError); + KWin::WindowInfo info = KWin::windowInfo( w, 0, NET::WM2WindowClass ); + XSetErrorHandler(oldHandler); + + 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( tqt_xdisplay(), w, tqt_xscreen()); + for(;;) { + Atom type; + int format; + unsigned long length, after; + unsigned char *data; + int r = XGetWindowProperty( tqt_xdisplay(), w, tqt_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( tqt_xdisplay(), w, StructureNotifyMask ); + XWindowAttributes attr_geom; + if( !XGetWindowAttributes( tqt_xdisplay(), w, &attr_geom )) + return; + int x = XDisplayWidth( tqt_xdisplay(), tqt_xscreen()) - attr_geom.width; + int y = XDisplayHeight( tqt_xdisplay(), tqt_xscreen()) - attr_geom.height; + if( managed ) { + XSetWindowAttributes attr; + if (!trinity_desktop_lock_use_system_modal_dialogs) { + attr.override_redirect = True; + XChangeWindowAttributes( tqt_xdisplay(), w, CWOverrideRedirect, &attr ); + } + XReparentWindow( tqt_xdisplay(), w, tqt_xrootwin(), x, y ); + XMapWindow( tqt_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).contains( 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( tqt_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( tqt_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 = tqt_xdisplay(); + e.xcrossing.window = mVkbdLastEventWindow; + e.xcrossing.root = tqt_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( tqt_xdisplay(), mVkbdLastEventWindow, False, 0, &e ); + } + mVkbdLastEventWindow = window; + if( mVkbdLastEventWindow != None ) { + XEvent e; + e.xcrossing.type = EnterNotify; + e.xcrossing.display = tqt_xdisplay(); + e.xcrossing.window = mVkbdLastEventWindow; + e.xcrossing.root = tqt_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( tqt_xdisplay(), mVkbdLastEventWindow, False, 0, &e ); + } +} + +void LockProcess::slotMouseActivity(XEvent *event) +{ + bool inFrame = 0; + bool inDialog = 0; + XButtonEvent *be = (XButtonEvent *) event; + XMotionEvent *me = (XMotionEvent *) event; + if ((event->type == ButtonPress) && (!mDialogs.isEmpty())) { + // Get geometry including window frame/titlebar + TQRect fgeom = mDialogs.first()->frameGeometry(); + TQRect wgeom = mDialogs.first()->geometry(); + + if (((be->x_root > fgeom.x()) && (be->y_root > fgeom.y())) && ((be->x_root < (fgeom.x()+fgeom.width())) && (be->y_root < (fgeom.y()+fgeom.height())))) { + inFrame = 1; + } + if (((be->x_root > wgeom.x()) && (be->y_root > wgeom.y())) && ((be->x_root < (wgeom.x()+wgeom.width())) && (be->y_root < (wgeom.y()+wgeom.height())))) { + inDialog = 1; + } + + // Clicked inside dialog; set focus + if (inFrame == TRUE) { + WId window = mDialogs.first()->winId(); + XSetInputFocus(tqt_xdisplay(), window, RevertToParent, CurrentTime); + fakeFocusIn(window); + // Why this needs to be repeated I have no idea... + XSetInputFocus(tqt_xdisplay(), window, RevertToParent, CurrentTime); + fakeFocusIn(window); + } + + // Clicked inside window handle (or border); drag window + if ((inFrame == TRUE) && (inDialog == FALSE)) { + TQPoint oldPoint = mDialogs.first()->pos(); + m_mouseDown = 1; + m_dialogPrevX = oldPoint.x(); + m_dialogPrevY = oldPoint.y(); + m_mousePrevX = be->x_root; + m_mousePrevY = be->y_root; + XChangeActivePointerGrab( tqt_xdisplay(), GRABEVENTS, TQCursor(tqsizeAllCursor).handle(), CurrentTime); + } + } + + // Drag the window... + if (event->type == MotionNotify) { + if (m_mouseDown == TRUE) { + int deltaX = me->x_root - m_mousePrevX; + int deltaY = me->y_root - m_mousePrevY; + m_dialogPrevX = m_dialogPrevX + deltaX; + m_dialogPrevY = m_dialogPrevY + deltaY; + if (!mDialogs.isEmpty()) mDialogs.first()->move(m_dialogPrevX, m_dialogPrevY); + + m_mousePrevX = me->x_root; + m_mousePrevY = me->y_root; + } + } + + if (event->type == ButtonRelease) { + m_mouseDown = 0; + XChangeActivePointerGrab( tqt_xdisplay(), GRABEVENTS, TQCursor(tqarrowCursor).handle(), CurrentTime); + } +} + +void LockProcess::processInputPipeCommand(TQString inputcommand) { + TQCString command(inputcommand.ascii()); + TQString to_display; + const char * pin_entry; + + if (command[0] == 'C') { + while (mDialogControlLock == true) usleep(100000); + mDialogControlLock = true; + if (mInfoMessageDisplayed || !trinity_desktop_lock_use_system_modal_dialogs) { + if (currentDialog != NULL) { + mForceReject = true; + closeCurrentWindow(); + } + } + mClosingWindows = false; + mInfoMessageDisplayed = false; + mDialogControlLock = false; + } + if (command[0] == 'T') { + to_display = command.data(); + to_display = to_display.remove(0,1); + // Lock out password dialogs and close any active dialog + while (mDialogControlLock == true) usleep(100000); + mDialogControlLock = true; + if (mInfoMessageDisplayed || !trinity_desktop_lock_use_system_modal_dialogs) { + if (currentDialog != NULL) { + mForceReject = true; + closeCurrentWindow(); + } + } + mInfoMessageDisplayed = true; + mDialogControlLock = false; + // Display info message dialog + InfoDlg inDlg( this ); + inDlg.updateLabel(to_display); + inDlg.setUnlockIcon(); + execDialog( &inDlg ); + mForceReject = false; + mClosingWindows = false; + return; + } + if ((command[0] == 'E') || (command[0] == 'W') || (command[0] == 'I') || (command[0] == 'K')) { + to_display = command.data(); + to_display = to_display.remove(0,1); + // Lock out password dialogs and close any active dialog + while (mDialogControlLock == true) usleep(100000); + mDialogControlLock = true; + if (mInfoMessageDisplayed || !trinity_desktop_lock_use_system_modal_dialogs) { + if (currentDialog != NULL) { + mForceReject = true; + closeCurrentWindow(); + } + } + mInfoMessageDisplayed = true; + mDialogControlLock = false; + // Display info message dialog + InfoDlg inDlg( this ); + inDlg.updateLabel(to_display); + if (command[0] == 'K') inDlg.setKDEIcon(); + if (command[0] == 'I') inDlg.setInfoIcon(); + if (command[0] == 'W') inDlg.setWarningIcon(); + if (command[0] == 'E') inDlg.setErrorIcon(); + execDialog( &inDlg ); + mForceReject = false; + mClosingWindows = false; + return; + } + if (command[0] == 'Q') { + to_display = command.data(); + to_display = to_display.remove(0,1); + // Lock out password dialogs and close any active dialog + while (mDialogControlLock == true) usleep(100000); + mDialogControlLock = true; + if (mInfoMessageDisplayed || !trinity_desktop_lock_use_system_modal_dialogs) { + if (currentDialog != NULL) { + mForceReject = true; + closeCurrentWindow(); + } + } + mInfoMessageDisplayed = true; + mDialogControlLock = false; + // Display query dialog + QueryDlg qryDlg( this ); + qryDlg.updateLabel(to_display); + qryDlg.setUnlockIcon(); + mForceReject = false; + execDialog( &qryDlg ); + if (mForceReject == false) { + pin_entry = qryDlg.getEntry(); + mInfoMessageDisplayed=false; + if (mPipeOpen_out == true) { + if (write(mPipe_fd_out, pin_entry, strlen(pin_entry)+1) == -1) { + // Error handler to shut up gcc warnings + } + if (write(mPipe_fd_out, "\n\r", 3) == -1) { + // Error handler to shut up gcc warnings + } + } + } + mForceReject = false; + mClosingWindows = false; + return; + } +} + +void LockProcess::fullyOnline() { + if (!mFullyOnlineSent) { + if (kdesktop_pid > 0) { + if (kill(kdesktop_pid, SIGUSR2) < 0) { + // The controlling kdesktop process probably died. Commit suicide... + // Exit uncleanly + exit(1); + } + else { + mFullyOnlineSent = true; + } + } + } +} + +void LockProcess::saverReady() { + DCOPRef ref( "kdesktop", "KScreensaverIface"); + ref.send( "saverLockReady" ); +} + +//=========================================================================== +// +// Control pipe handler +// +ControlPipeHandlerObject::ControlPipeHandlerObject() : TQObject() { + mParent = NULL; + mRunning = false; + mTerminate = false; + mThreadID = 0L; +} + +ControlPipeHandlerObject::~ControlPipeHandlerObject() { + // +} + +void ControlPipeHandlerObject::run(void) { + mThreadID = pthread_self(); + mRunning = true; + + sigset_t new_mask; + sigemptyset(&new_mask); + sigaddset(&new_mask, SIGUSR1); + + // Unblock SIGUSR1 + pthread_sigmask(SIG_UNBLOCK, &new_mask, NULL); + + int display_number = atoi(TQString(XDisplayString(tqt_xdisplay())).replace(":","").ascii()); + + if (display_number < 0) { + printf("[kdesktop_lock] Warning: unable to create control socket. Interactive logon modules may not function properly.\n"); + mRunning = false; + TQApplication::eventLoop()->exit(-1); + return; + } + + char fifo_file[PATH_MAX]; + char fifo_file_out[PATH_MAX]; + snprintf(fifo_file, PATH_MAX, FIFO_FILE, display_number); + snprintf(fifo_file_out, PATH_MAX, FIFO_FILE_OUT, display_number); + + /* 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); + + mParent->mPipe_fd = open(fifo_file, O_RDONLY | O_NONBLOCK); + if (mParent->mPipe_fd > -1) { + mParent->mPipeOpen = true; + } + + mknod(fifo_file_out, S_IFIFO|0600, 0); + chmod(fifo_file_out, 0600); + + mParent->mPipe_fd_out = open(fifo_file_out, O_RDWR | O_NONBLOCK); + if (mParent->mPipe_fd_out > -1) { + mParent->mPipeOpen_out = true; + } + + if (!mParent->mPipeOpen) { + printf("[kdesktop_lock] Warning: unable to create control socket '%s'. Interactive logon modules may not function properly.\n", fifo_file); + mRunning = false; + TQApplication::eventLoop()->exit(-1); + return; + } + + int numread; + int retval; + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(mParent->mPipe_fd, &rfds); + TQByteArray readbuf(128); + + while (mParent->mPipeOpen && !mTerminate) { + TQString inputcommand = ""; + + // Wait for mParent->mPipe_fd to receive input + retval = select(mParent->mPipe_fd + 1, &rfds, NULL, NULL, NULL); + if (retval < 0) { + // ERROR + } + else if (retval) { + // New data is available + readbuf[0]=' '; + numread = read(mParent->mPipe_fd, readbuf.data(), 128); + readbuf[numread] = 0; + if (numread > 0) { + inputcommand += readbuf; + emit processCommand(inputcommand); + } + } + } + + mRunning = false; + TQApplication::eventLoop()->exit(0); + return; +} + +void ControlPipeHandlerObject::terminateThread() { + if (mRunning) { + mTerminate = true; + pthread_kill(mThreadID, SIGUSR1); + } +} + +#include "lockprocess.moc" |