diff options
Diffstat (limited to 'kdesktop/lock')
-rw-r--r-- | kdesktop/lock/Makefile.am | 24 | ||||
-rw-r--r-- | kdesktop/lock/autologout.cc | 115 | ||||
-rw-r--r-- | kdesktop/lock/autologout.h | 51 | ||||
-rw-r--r-- | kdesktop/lock/configure.in.in | 37 | ||||
-rw-r--r-- | kdesktop/lock/lockdlg.cc | 720 | ||||
-rw-r--r-- | kdesktop/lock/lockdlg.h | 92 | ||||
-rw-r--r-- | kdesktop/lock/lockprocess.cc | 1172 | ||||
-rw-r--r-- | kdesktop/lock/lockprocess.h | 131 | ||||
-rw-r--r-- | kdesktop/lock/main.cc | 174 | ||||
-rw-r--r-- | kdesktop/lock/main.h | 39 |
10 files changed, 2555 insertions, 0 deletions
diff --git a/kdesktop/lock/Makefile.am b/kdesktop/lock/Makefile.am new file mode 100644 index 000000000..4af8d4fae --- /dev/null +++ b/kdesktop/lock/Makefile.am @@ -0,0 +1,24 @@ +## Makefile.am of kdebase/kdesktop/lock + +INCLUDES = -I.. -I$(top_srcdir)/kcheckpass -I$(top_srcdir)/kdmlib $(GLINC) $(all_includes) +kdesktop_lock_LDFLAGS = $(all_libraries) $(KDE_RPATH) +kdesktop_lock_LDADD = ../libkdesktopsettings.la ../../kdmlib/libdmctl.la $(LIB_KIO) $(LIB_XF86MISC) $(GLLIB) + +####### Files + +bin_PROGRAMS = kdesktop_lock + +kdesktop_lock_SOURCES = lockprocess.cc lockdlg.cc autologout.cc main.cc + +noinst_HEADERS = lockprocess.h lockdlg.h autologout.h main.h + +METASOURCES = AUTO + +lockprocess.o: ../kdesktopsettings.h + +####### Build rules + +PAM = @KSCREENSAVER_PAM_SERVICE@ + +install-data-local: + -@test -n "$(DESTDIR)" || test -z "$(PAM)" || $(top_srcdir)/mkpamserv $(PAM) diff --git a/kdesktop/lock/autologout.cc b/kdesktop/lock/autologout.cc new file mode 100644 index 000000000..f351fe2e7 --- /dev/null +++ b/kdesktop/lock/autologout.cc @@ -0,0 +1,115 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright (c) 2004 Chris Howells <howells@kde.org> + +#include "lockprocess.h" +#include "autologout.h" + +#include <kapplication.h> +#include <klocale.h> +#include <kglobalsettings.h> +#include <kconfig.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <dcopref.h> +#include <kmessagebox.h> +#include <kdialog.h> + +#include <qlayout.h> +#include <qmessagebox.h> +#include <qlabel.h> +#include <qstyle.h> +#include <qapplication.h> +#include <qdialog.h> +#include <qprogressbar.h> + +#define COUNTDOWN 30 + +AutoLogout::AutoLogout(LockProcess *parent) : QDialog(parent, "password dialog", true, WX11BypassWM) +{ + frame = new QFrame(this); + frame->setFrameStyle(QFrame::Panel | QFrame::Raised); + frame->setLineWidth(2); + + QLabel *pixLabel = new QLabel( frame, "pixlabel" ); + pixLabel->setPixmap(DesktopIcon("exit")); + + QLabel *greetLabel = new QLabel(i18n("<nobr><qt><b>Automatic Log Out</b></qt><nobr>"), frame); + QLabel *infoLabel = new QLabel(i18n("<qt>To prevent being logged out, resume using this session by moving the mouse or pressing a key.</qt>"), frame); + + mStatusLabel = new QLabel("<b> </b>", frame); + mStatusLabel->setAlignment(QLabel::AlignCenter); + + QLabel *mProgressLabel = new QLabel("Time Remaining:", frame); + mProgressRemaining = new QProgressBar(frame); + mProgressRemaining->setPercentageVisible(false); + + QVBoxLayout *unlockDialogLayout = new QVBoxLayout( this ); + unlockDialogLayout->addWidget( frame ); + + frameLayout = new QGridLayout(frame, 1, 1, KDialog::marginHint(), KDialog::spacingHint()); + frameLayout->addMultiCellWidget(pixLabel, 0, 2, 0, 0, Qt::AlignCenter | Qt::AlignTop); + frameLayout->addWidget(greetLabel, 0, 1); + frameLayout->addWidget(mStatusLabel, 1, 1); + frameLayout->addWidget(infoLabel, 2, 1); + frameLayout->addWidget(mProgressLabel, 3, 1); + frameLayout->addWidget(mProgressRemaining, 4, 1); + + // get the time remaining in seconds for the status label + mRemaining = COUNTDOWN * 25; + + mProgressRemaining->setTotalSteps(COUNTDOWN * 25); + + updateInfo(mRemaining); + + mCountdownTimerId = startTimer(1000/25); + + connect(qApp, SIGNAL(activity()), SLOT(slotActivity())); +} + +AutoLogout::~AutoLogout() +{ + hide(); +} + +void AutoLogout::updateInfo(int timeout) +{ + mStatusLabel->setText(i18n("<nobr><qt>You will be automatically logged out in 1 second</qt></nobr>", + "<nobr><qt>You will be automatically logged out in %n seconds</qt></nobr>", + timeout / 25) ); + mProgressRemaining->setProgress(timeout); +} + +void AutoLogout::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == mCountdownTimerId) + { + updateInfo(mRemaining); + --mRemaining; + if (mRemaining < 0) + { + logout(); + } + } +} + +void AutoLogout::slotActivity() +{ + accept(); +} + +void AutoLogout::logout() +{ + killTimers(); + DCOPRef("ksmserver","ksmserver").send("logout", 0, 0, 0); +} + +void AutoLogout::show() +{ + QDialog::show(); + QApplication::flushX(); +} + +#include "autologout.moc" diff --git a/kdesktop/lock/autologout.h b/kdesktop/lock/autologout.h new file mode 100644 index 000000000..e48716575 --- /dev/null +++ b/kdesktop/lock/autologout.h @@ -0,0 +1,51 @@ +//=========================================================================== +// +// 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> +// Coypright (c) 2004 Chris Howells <howells@kde.org> + +#ifndef __TIMEOUT_H__ +#define __TIMEOUT_H__ + +#include <qstringlist.h> + +#include <qlayout.h> + +class LockProcess; +class QFrame; +class QGridLayout; +class QLabel; +class QDialog; +class QProgressBar; + +class AutoLogout : public QDialog +{ + Q_OBJECT + +public: + AutoLogout(LockProcess *parent); + ~AutoLogout(); + virtual void show(); + +protected: + virtual void timerEvent(QTimerEvent *); + +private slots: + void slotActivity(); + +private: + void updateInfo(int); + QFrame *frame; + QGridLayout *frameLayout; + QLabel *mStatusLabel; + int mCountdownTimerId; + int mRemaining; + QTimer countDownTimer; + QProgressBar *mProgressRemaining; + void logout(); +}; + +#endif + diff --git a/kdesktop/lock/configure.in.in b/kdesktop/lock/configure.in.in new file mode 100644 index 000000000..d5d61a5f9 --- /dev/null +++ b/kdesktop/lock/configure.in.in @@ -0,0 +1,37 @@ +xss_save_ldflags="$LDFLAGS" +LDFLAGS="$X_LDFLAGS" + +LIB_XF86MISC= + +KDE_CHECK_HEADER(X11/extensions/xf86misc.h, + [ + AC_CHECK_LIB(Xxf86misc,XF86MiscQueryVersion, + [ + AC_DEFINE(HAVE_XF86MISC, 1, [Define if you have the xf86misc extension]) + LIB_XF86MISC="-lXxf86misc" + ], + [], [ $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS ]) + ],[], + [ + #include <X11/Xlib.h> + ]) +AC_SUBST(LIB_XF86MISC) + +if test -n "$LIB_XF86MISC"; then + AC_CHECK_LIB(Xxf86misc,XF86MiscSetGrabKeysState, + [ + AC_DEFINE(HAVE_XF86MISCSETGRABKEYSSTATE, 1, [Define if you have XF86MiscSetGrabKeysState()]) + ], + [], [ $X_PRE_LIBS -lXext -lX11 $X_EXTRA_LIBS ]) +fi + +LDFLAGS="$xss_save_ldflags" + +AC_HAVE_GL( + [ + AC_CHECK_LIB(GL,glXChooseVisual, + [ + AC_DEFINE(HAVE_GLXCHOOSEVISUAL, 1, [Define if you have glXChooseVisual()]) + ]) + ],[] + ) diff --git a/kdesktop/lock/lockdlg.cc b/kdesktop/lock/lockdlg.cc new file mode 100644 index 000000000..8006cf29d --- /dev/null +++ b/kdesktop/lock/lockdlg.cc @@ -0,0 +1,720 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright (c) 1999 Martin R. Jones <mjones@kde.org> +// Copyright (c) 2003 Chris Howells <howells@kde.org> +// Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> + +#include <config.h> + +#include "lockprocess.h" +#include "lockdlg.h" + +#include <kcheckpass.h> +#include <dmctl.h> + +#include <kapplication.h> +#include <klocale.h> +#include <kpushbutton.h> +#include <kseparator.h> +#include <kstandarddirs.h> +#include <kglobalsettings.h> +#include <kconfig.h> +#include <kiconloader.h> +#include <kdesu/defaults.h> +#include <kpassdlg.h> +#include <kdebug.h> +#include <kuser.h> +#include <dcopref.h> +#include <kmessagebox.h> + +#include <qlayout.h> +#include <qpushbutton.h> +#include <qmessagebox.h> +#include <qsimplerichtext.h> +#include <qlabel.h> +#include <qstringlist.h> +#include <qfontmetrics.h> +#include <qstyle.h> +#include <qapplication.h> +#include <qlistview.h> +#include <qheader.h> +#include <qcheckbox.h> + +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <pwd.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <fixx11h.h> + +#ifndef AF_LOCAL +# define AF_LOCAL AF_UNIX +#endif + +#define PASSDLG_HIDE_TIMEOUT 10000 + +//=========================================================================== +// +// Simple dialog for entering a password. +// +PasswordDlg::PasswordDlg(LockProcess *parent, GreeterPluginHandle *plugin) + : QDialog(parent, "password dialog", true, WX11BypassWM), + mPlugin( plugin ), + mCapsLocked(-1), + mUnlockingFailed(false) +{ + frame = new QFrame( this ); + frame->setFrameStyle( QFrame::Panel | QFrame::Raised ); + frame->setLineWidth( 2 ); + + QLabel *pixLabel = new QLabel( frame, "pixlabel" ); + pixLabel->setPixmap(DesktopIcon("lock")); + + KUser user; + QLabel *greetLabel = new QLabel( user.fullName().isEmpty() ? + i18n("<nobr><b>The session is locked</b><br>") : + i18n("<nobr><b>The session was locked by %1</b><br>").arg( user.fullName() ), frame ); + + mStatusLabel = new QLabel( "<b> </b>", frame ); + mStatusLabel->setAlignment( QLabel::AlignCenter ); + + mLayoutButton = new QPushButton( frame ); + mLayoutButton->setFlat( true ); + + KSeparator *sep = new KSeparator( KSeparator::HLine, frame ); + + mNewSessButton = new KPushButton( KGuiItem(i18n("Sw&itch User..."), "fork"), frame ); + ok = new KPushButton( i18n("Unl&ock"), frame ); + cancel = new KPushButton( KStdGuiItem::cancel(), frame ); + + greet = plugin->info->create( this, 0, this, mLayoutButton, QString::null, + KGreeterPlugin::Authenticate, KGreeterPlugin::ExUnlock ); + + + QVBoxLayout *unlockDialogLayout = new QVBoxLayout( this ); + unlockDialogLayout->addWidget( frame ); + + QHBoxLayout *layStatus = new QHBoxLayout( 0, 0, KDialog::spacingHint()); + layStatus->addWidget( mStatusLabel ); + layStatus->addWidget( mLayoutButton ); + + QHBoxLayout *layButtons = new QHBoxLayout( 0, 0, KDialog::spacingHint()); + layButtons->addWidget( mNewSessButton ); + layButtons->addStretch(); + layButtons->addWidget( ok ); + layButtons->addWidget( cancel ); + + frameLayout = new QGridLayout( frame, 1, 1, KDialog::marginHint(), KDialog::spacingHint() ); + frameLayout->addMultiCellWidget( pixLabel, 0, 2, 0, 0, AlignTop ); + frameLayout->addWidget( greetLabel, 0, 1 ); + frameLayout->addItem( greet->getLayoutItem(), 1, 1 ); + frameLayout->addLayout( layStatus, 2, 1 ); + frameLayout->addMultiCellWidget( sep, 3, 3, 0, 1 ); + frameLayout->addMultiCellLayout( layButtons, 4, 4, 0, 1 ); + + setTabOrder( ok, cancel ); + setTabOrder( cancel, mNewSessButton ); + setTabOrder( mNewSessButton, mLayoutButton ); + + connect(mLayoutButton, SIGNAL(clicked()), this, SLOT(layoutClicked())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + connect(ok, SIGNAL(clicked()), SLOT(slotOK())); + connect(mNewSessButton, SIGNAL(clicked()), SLOT(slotSwitchUser())); + + if (!DM().isSwitchable() || !kapp->authorize("switch_user")) + mNewSessButton->hide(); + + installEventFilter(this); + + mFailedTimerId = 0; + mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT); + connect(qApp, SIGNAL(activity()), SLOT(slotActivity()) ); + + greet->start(); + + DCOPRef kxkb("kxkb", "kxkb"); + if( !kxkb.isNull() ) { + layoutsList = kxkb.call("getLayoutsList"); + QString currentLayout = kxkb.call("getCurrentLayout"); + if( !currentLayout.isEmpty() && layoutsList.count() > 1 ) { + currLayout = layoutsList.find(currentLayout); + if (currLayout == layoutsList.end()) + setLayoutText("err"); + else + setLayoutText(*currLayout); + } else + mLayoutButton->hide(); + } else { + mLayoutButton->hide(); // no kxkb running + } + capsLocked(); +} + +PasswordDlg::~PasswordDlg() +{ + hide(); + frameLayout->removeItem( greet->getLayoutItem() ); + delete greet; +} + +void PasswordDlg::layoutClicked() +{ + + if( ++currLayout == layoutsList.end() ) + currLayout = layoutsList.begin(); + + DCOPRef kxkb("kxkb", "kxkb"); + setLayoutText( kxkb.call("setLayout", *currLayout) ? *currLayout : "err" ); + +} + +void PasswordDlg::setLayoutText( const QString &txt ) +{ + mLayoutButton->setText( txt ); + QSize sz = mLayoutButton->fontMetrics().size( 0, txt ); + int mrg = mLayoutButton->style().pixelMetric( QStyle::PM_ButtonMargin ) * 2; + mLayoutButton->setFixedSize( sz.width() + mrg, sz.height() + mrg ); +} + +void PasswordDlg::updateLabel() +{ + if (mUnlockingFailed) + { + mStatusLabel->setPaletteForegroundColor(Qt::black); + mStatusLabel->setText(i18n("<b>Unlocking failed</b>")); + } + else + if (mCapsLocked) + { + mStatusLabel->setPaletteForegroundColor(Qt::red); + mStatusLabel->setText(i18n("<b>Warning: Caps Lock on</b>")); + } + else + { + mStatusLabel->setText("<b> </b>"); + } +} + +//--------------------------------------------------------------------------- +// +// Handle timer events. +// +void PasswordDlg::timerEvent(QTimerEvent *ev) +{ + if (ev->timerId() == mTimeoutTimerId) + { + reject(); + } + else if (ev->timerId() == mFailedTimerId) + { + killTimer(mFailedTimerId); + mFailedTimerId = 0; + // Show the normal password prompt. + mUnlockingFailed = false; + updateLabel(); + ok->setEnabled(true); + cancel->setEnabled(true); + mNewSessButton->setEnabled( true ); + greet->revive(); + greet->start(); + } +} + +bool PasswordDlg::eventFilter(QObject *, QEvent *ev) +{ + if (ev->type() == QEvent::KeyPress || ev->type() == QEvent::KeyRelease) + capsLocked(); + return false; +} + +void PasswordDlg::slotActivity() +{ + if (mTimeoutTimerId) { + killTimer(mTimeoutTimerId); + mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT); + } +} + +////// kckeckpass interface code + +int PasswordDlg::Reader (void *buf, int count) +{ + int ret, rlen; + + for (rlen = 0; rlen < count; ) { + dord: + ret = ::read (sFd, (void *)((char *)buf + rlen), count - rlen); + if (ret < 0) { + if (errno == EINTR) + goto dord; + if (errno == EAGAIN) + break; + return -1; + } + if (!ret) + break; + rlen += ret; + } + return rlen; +} + +bool PasswordDlg::GRead (void *buf, int count) +{ + return Reader (buf, count) == count; +} + +bool PasswordDlg::GWrite (const void *buf, int count) +{ + return ::write (sFd, buf, count) == count; +} + +bool PasswordDlg::GSendInt (int val) +{ + return GWrite (&val, sizeof(val)); +} + +bool PasswordDlg::GSendStr (const char *buf) +{ + int len = buf ? ::strlen (buf) + 1 : 0; + return GWrite (&len, sizeof(len)) && GWrite (buf, len); +} + +bool PasswordDlg::GSendArr (int len, const char *buf) +{ + return GWrite (&len, sizeof(len)) && GWrite (buf, len); +} + +bool PasswordDlg::GRecvInt (int *val) +{ + return GRead (val, sizeof(*val)); +} + +bool PasswordDlg::GRecvArr (char **ret) +{ + int len; + char *buf; + + if (!GRecvInt(&len)) + return false; + if (!len) { + *ret = 0; + return true; + } + if (!(buf = (char *)::malloc (len))) + return false; + *ret = buf; + return GRead (buf, len); +} + +void PasswordDlg::reapVerify() +{ + ::close( sFd ); + int status; + ::waitpid( sPid, &status, 0 ); + if (WIFEXITED(status)) + switch (WEXITSTATUS(status)) { + case AuthOk: + greet->succeeded(); + accept(); + return; + case AuthBad: + greet->failed(); + mUnlockingFailed = true; + updateLabel(); + mFailedTimerId = startTimer(1500); + ok->setEnabled(false); + cancel->setEnabled(false); + mNewSessButton->setEnabled( false ); + return; + case AuthAbort: + return; + } + cantCheck(); +} + +void PasswordDlg::handleVerify() +{ + int ret; + char *arr; + + while (GRecvInt( &ret )) { + switch (ret) { + case ConvGetBinary: + if (!GRecvArr( &arr )) + break; + greet->binaryPrompt( arr, false ); + if (arr) + ::free( arr ); + return; + case ConvGetNormal: + if (!GRecvArr( &arr )) + break; + greet->textPrompt( arr, true, false ); + if (arr) + ::free( arr ); + return; + case ConvGetHidden: + if (!GRecvArr( &arr )) + break; + greet->textPrompt( arr, false, false ); + if (arr) + ::free( arr ); + return; + case ConvPutInfo: + if (!GRecvArr( &arr )) + break; + if (!greet->textMessage( arr, false )) + static_cast< LockProcess* >(parent())->msgBox( QMessageBox::Information, QString::fromLocal8Bit( arr ) ); + ::free( arr ); + continue; + case ConvPutError: + if (!GRecvArr( &arr )) + break; + if (!greet->textMessage( arr, true )) + static_cast< LockProcess* >(parent())->msgBox( QMessageBox::Warning, QString::fromLocal8Bit( arr ) ); + ::free( arr ); + continue; + } + break; + } + reapVerify(); +} + +////// greeter plugin callbacks + +void PasswordDlg::gplugReturnText( const char *text, int tag ) +{ + GSendStr( text ); + if (text) + GSendInt( tag ); + handleVerify(); +} + +void PasswordDlg::gplugReturnBinary( const char *data ) +{ + if (data) { + unsigned const char *up = (unsigned const char *)data; + int len = up[3] | (up[2] << 8) | (up[1] << 16) | (up[0] << 24); + if (!len) + GSendArr( 4, data ); + else + GSendArr( len, data ); + } else + GSendArr( 0, 0 ); + handleVerify(); +} + +void PasswordDlg::gplugSetUser( const QString & ) +{ + // ignore ... +} + +void PasswordDlg::cantCheck() +{ + greet->failed(); + static_cast< LockProcess* >(parent())->msgBox( QMessageBox::Critical, + i18n("Cannot unlock the session because the authentication system failed to work;\n" + "you must kill kdesktop_lock (pid %1) manually.").arg(getpid()) ); + greet->revive(); +} + +//--------------------------------------------------------------------------- +// +// Starts the kcheckpass process to check the user's password. +// +void PasswordDlg::gplugStart() +{ + int sfd[2]; + char fdbuf[16]; + + if (::socketpair(AF_LOCAL, SOCK_STREAM, 0, sfd)) { + cantCheck(); + return; + } + if ((sPid = ::fork()) < 0) { + ::close(sfd[0]); + ::close(sfd[1]); + cantCheck(); + return; + } + if (!sPid) { + ::close(sfd[0]); + sprintf(fdbuf, "%d", sfd[1]); + execlp("kcheckpass", "kcheckpass", +#ifdef HAVE_PAM + "-c", KSCREENSAVER_PAM_SERVICE, +#endif + "-m", mPlugin->info->method, + "-S", fdbuf, + (char *)0); + exit(20); + } + ::close(sfd[1]); + sFd = sfd[0]; + handleVerify(); +} + +void PasswordDlg::gplugActivity() +{ + slotActivity(); +} + +void PasswordDlg::gplugMsgBox( QMessageBox::Icon type, const QString &text ) +{ + QDialog dialog( this, 0, true, WX11BypassWM ); + QFrame *winFrame = new QFrame( &dialog ); + winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised ); + winFrame->setLineWidth( 2 ); + QVBoxLayout *vbox = new QVBoxLayout( &dialog ); + vbox->addWidget( winFrame ); + + QLabel *label1 = new QLabel( winFrame ); + label1->setPixmap( QMessageBox::standardIcon( type ) ); + QLabel *label2 = new QLabel( text, winFrame ); + KPushButton *button = new KPushButton( KStdGuiItem::ok(), winFrame ); + button->setDefault( true ); + button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); + connect( button, SIGNAL( clicked() ), SLOT( accept() ) ); + + QGridLayout *grid = new QGridLayout( winFrame, 2, 2, 10 ); + grid->addWidget( label1, 0, 0, Qt::AlignCenter ); + grid->addWidget( label2, 0, 1, Qt::AlignCenter ); + grid->addMultiCellWidget( button, 1,1, 0,1, Qt::AlignCenter ); + + static_cast< LockProcess* >(parent())->execDialog( &dialog ); +} + +void PasswordDlg::slotOK() +{ + greet->next(); +} + + +void PasswordDlg::show() +{ + QDialog::show(); + QApplication::flushX(); +} + +void PasswordDlg::slotStartNewSession() +{ + if (!KMessageBox::shouldBeShownContinue( ":confirmNewSession" )) { + DM().startReserve(); + return; + } + + killTimer(mTimeoutTimerId); + mTimeoutTimerId = 0; + + QDialog *dialog = new QDialog( this, "warnbox", true, WX11BypassWM ); + QFrame *winFrame = new QFrame( dialog ); + winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised ); + winFrame->setLineWidth( 2 ); + QVBoxLayout *vbox = new QVBoxLayout( dialog ); + vbox->addWidget( winFrame ); + + QLabel *label1 = new QLabel( winFrame ); + label1->setPixmap( QMessageBox::standardIcon( QMessageBox::Warning ) ); + QString qt_text = + i18n("You have chosen to open another desktop session " + "instead of resuming the current one.<br>" + "The current session will be hidden " + "and a new login screen will be displayed.<br>" + "An F-key is assigned to each session; " + "F%1 is usually assigned to the first session, " + "F%2 to the second session and so on. " + "You can switch between sessions by pressing " + "Ctrl, Alt and the appropriate F-key at the same time. " + "Additionally, the KDE Panel and Desktop menus have " + "actions for switching between sessions.") + .arg(7).arg(8); + QLabel *label2 = new QLabel( qt_text, winFrame ); + KPushButton *okbutton = new KPushButton( KGuiItem(i18n("&Start New Session"), "fork"), winFrame ); + okbutton->setDefault( true ); + connect( okbutton, SIGNAL( clicked() ), dialog, SLOT( accept() ) ); + KPushButton *cbutton = new KPushButton( KStdGuiItem::cancel(), winFrame ); + connect( cbutton, SIGNAL( clicked() ), dialog, SLOT( reject() ) ); + + QBoxLayout *mbox = new QVBoxLayout( winFrame, KDialog::marginHint(), KDialog::spacingHint() ); + + QGridLayout *grid = new QGridLayout( mbox, 2, 2, 2 * KDialog::spacingHint() ); + grid->setMargin( KDialog::marginHint() ); + grid->addWidget( label1, 0, 0, Qt::AlignCenter ); + grid->addWidget( label2, 0, 1, Qt::AlignCenter ); + QCheckBox *cb = new QCheckBox( i18n("&Do not ask again"), winFrame ); + grid->addMultiCellWidget( cb, 1,1, 0,1 ); + + QBoxLayout *hbox = new QHBoxLayout( mbox, KDialog::spacingHint() ); + hbox->addStretch( 1 ); + hbox->addWidget( okbutton ); + hbox->addStretch( 1 ); + hbox->addWidget( cbutton ); + hbox->addStretch( 1 ); + + // stolen from kmessagebox + int pref_width = 0; + int pref_height = 0; + // Calculate a proper size for the text. + { + QSimpleRichText rt(qt_text, dialog->font()); + QRect rect = KGlobalSettings::desktopGeometry(dialog); + + pref_width = rect.width() / 3; + rt.setWidth(pref_width); + int used_width = rt.widthUsed(); + pref_height = rt.height(); + if (used_width <= pref_width) + { + while(true) + { + int new_width = (used_width * 9) / 10; + rt.setWidth(new_width); + int new_height = rt.height(); + if (new_height > pref_height) + break; + used_width = rt.widthUsed(); + if (used_width > new_width) + break; + } + pref_width = used_width; + } + else + { + if (used_width > (pref_width *2)) + pref_width = pref_width *2; + else + pref_width = used_width; + } + } + label2->setFixedSize(QSize(pref_width+10, pref_height)); + + int ret = static_cast< LockProcess* >( parent())->execDialog( dialog ); + + delete dialog; + + if (ret == QDialog::Accepted) { + if (cb->isChecked()) + KMessageBox::saveDontShowAgainContinue( ":confirmNewSession" ); + DM().startReserve(); + } + + mTimeoutTimerId = startTimer(PASSDLG_HIDE_TIMEOUT); +} + +class LockListViewItem : public QListViewItem { +public: + LockListViewItem( QListView *parent, + const QString &sess, const QString &loc, int _vt ) + : QListViewItem( parent ) + , vt( _vt ) + { + setText( 0, sess ); + setText( 1, loc ); + } + + int vt; +}; + +void PasswordDlg::slotSwitchUser() +{ + int p = 0; + DM dm; + + QDialog dialog( this, "sessbox", true, WX11BypassWM ); + QFrame *winFrame = new QFrame( &dialog ); + winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised ); + winFrame->setLineWidth( 2 ); + QBoxLayout *vbox = new QVBoxLayout( &dialog ); + vbox->addWidget( winFrame ); + + QBoxLayout *hbox = new QHBoxLayout( winFrame, KDialog::marginHint(), KDialog::spacingHint() ); + + QBoxLayout *vbox1 = new QVBoxLayout( hbox, KDialog::spacingHint() ); + QBoxLayout *vbox2 = new QVBoxLayout( hbox, KDialog::spacingHint() ); + + KPushButton *btn; + + SessList sess; + if (dm.localSessions( sess )) { + + lv = new QListView( winFrame ); + connect( lv, SIGNAL(doubleClicked(QListViewItem *, const QPoint&, int)), SLOT(slotSessionActivated()) ); + connect( lv, SIGNAL(doubleClicked(QListViewItem *, const QPoint&, int)), &dialog, SLOT(reject()) ); + lv->setAllColumnsShowFocus( true ); + lv->addColumn( i18n("Session") ); + lv->addColumn( i18n("Location") ); + lv->setColumnWidthMode( 0, QListView::Maximum ); + lv->setColumnWidthMode( 1, QListView::Maximum ); + QListViewItem *itm = 0; + QString user, loc; + int ns = 0; + for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + DM::sess2Str2( *it, user, loc ); + itm = new LockListViewItem( lv, user, loc, (*it).vt ); + if (!(*it).vt) + itm->setEnabled( false ); + if ((*it).self) { + lv->setCurrentItem( itm ); + itm->setSelected( true ); + } + ns++; + } + int fw = lv->frameWidth() * 2; + QSize hds( lv->header()->sizeHint() ); + lv->setMinimumWidth( fw + hds.width() + + (ns > 10 ? style().pixelMetric(QStyle::PM_ScrollBarExtent) : 0 ) ); + lv->setFixedHeight( fw + hds.height() + + itm->height() * (ns < 6 ? 6 : ns > 10 ? 10 : ns) ); + lv->header()->adjustHeaderSize(); + vbox1->addWidget( lv ); + + btn = new KPushButton( KGuiItem(i18n("session", "&Activate"), "fork"), winFrame ); + connect( btn, SIGNAL(clicked()), SLOT(slotSessionActivated()) ); + connect( btn, SIGNAL(clicked()), &dialog, SLOT(reject()) ); + vbox2->addWidget( btn ); + vbox2->addStretch( 2 ); + } + + if (kapp->authorize("start_new_session") && (p = dm.numReserve()) >= 0) + { + btn = new KPushButton( KGuiItem(i18n("Start &New Session"), "fork"), winFrame ); + connect( btn, SIGNAL(clicked()), SLOT(slotStartNewSession()) ); + connect( btn, SIGNAL(clicked()), &dialog, SLOT(reject()) ); + if (!p) + btn->setEnabled( false ); + vbox2->addWidget( btn ); + vbox2->addStretch( 1 ); + } + + btn = new KPushButton( KStdGuiItem::cancel(), winFrame ); + connect( btn, SIGNAL(clicked()), &dialog, SLOT(reject()) ); + vbox2->addWidget( btn ); + + static_cast< LockProcess* >(parent())->execDialog( &dialog ); +} + +void PasswordDlg::slotSessionActivated() +{ + LockListViewItem *itm = (LockListViewItem *)lv->currentItem(); + if (itm && itm->vt > 0) + DM().switchVT( itm->vt ); +} + +void PasswordDlg::capsLocked() +{ + unsigned int lmask; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + XQueryPointer(qt_xdisplay(), DefaultRootWindow( qt_xdisplay() ), &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &lmask); + mCapsLocked = lmask & LockMask; + updateLabel(); +} + +#include "lockdlg.moc" diff --git a/kdesktop/lock/lockdlg.h b/kdesktop/lock/lockdlg.h new file mode 100644 index 000000000..4bb468c03 --- /dev/null +++ b/kdesktop/lock/lockdlg.h @@ -0,0 +1,92 @@ +//=========================================================================== +// +// 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> +// + +#ifndef __LOCKDLG_H__ +#define __LOCKDLG_H__ + +#include <kgreeterplugin.h> + +#include <qdialog.h> +#include <qstringlist.h> + +struct GreeterPluginHandle; +class LockProcess; +class QFrame; +class QGridLayout; +class QLabel; +class KPushButton; +class QListView; + +//=========================================================================== +// +// Simple dialog for entering a password. +// It does not handle password validation. +// +class PasswordDlg : public QDialog, public KGreeterPluginHandler +{ + Q_OBJECT + +public: + PasswordDlg(LockProcess *parent, GreeterPluginHandle *plugin); + ~PasswordDlg(); + virtual void show(); + + // from KGreetPluginHandler + virtual void gplugReturnText( const char *text, int tag ); + virtual void gplugReturnBinary( const char *data ); + virtual void gplugSetUser( const QString & ); + virtual void gplugStart(); + virtual void gplugActivity(); + virtual void gplugMsgBox( QMessageBox::Icon type, const QString &text ); + +protected: + virtual void timerEvent(QTimerEvent *); + virtual bool eventFilter(QObject *, QEvent *); + +private slots: + void slotSwitchUser(); + void slotSessionActivated(); + void slotStartNewSession(); + void slotOK(); + void layoutClicked(); + void slotActivity(); + +private: + void setLayoutText( const QString &txt ); + void capsLocked(); + void updateLabel(); + int Reader (void *buf, int count); + bool GRead (void *buf, int count); + bool GWrite (const void *buf, int count); + bool GSendInt (int val); + bool GSendStr (const char *buf); + bool GSendArr (int len, const char *buf); + bool GRecvInt (int *val); + bool GRecvArr (char **buf); + void handleVerify(); + void reapVerify(); + void cantCheck(); + GreeterPluginHandle *mPlugin; + KGreeterPlugin *greet; + QFrame *frame; + QGridLayout *frameLayout; + QLabel *mStatusLabel; + KPushButton *mNewSessButton, *ok, *cancel; + QPushButton *mLayoutButton; + int mFailedTimerId; + int mTimeoutTimerId; + int mCapsLocked; + bool mUnlockingFailed; + QStringList layoutsList; + QStringList::iterator currLayout; + int sPid, sFd; + QListView *lv; +}; + +#endif + diff --git a/kdesktop/lock/lockprocess.cc b/kdesktop/lock/lockprocess.cc new file mode 100644 index 000000000..a813b6899 --- /dev/null +++ b/kdesktop/lock/lockprocess.cc @@ -0,0 +1,1172 @@ +//=========================================================================== +// +// This file is part of the KDE project +// +// Copyright (c) 1999 Martin R. Jones <mjones@kde.org> +// Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> +// + +//kdesktop keeps running and checks user inactivity +//when it should show screensaver (and maybe lock the session), +//it starts kdesktop_lock, who does all the locking and who +//actually starts the screensaver + +//It's done this way to prevent screen unlocking when kdesktop +//crashes (e.g. because it's set to multiple wallpapers and +//some image will be corrupted). + +#include <config.h> + +#include "lockprocess.h" +#include "lockdlg.h" +#include "autologout.h" +#include "kdesktopsettings.h" + +#include <dmctl.h> + +#include <kstandarddirs.h> +#include <kapplication.h> +#include <kservicegroup.h> +#include <kdebug.h> +#include <kmessagebox.h> +#include <kglobalsettings.h> +#include <klocale.h> +#include <klibloader.h> +#include <kpushbutton.h> +#include <kstdguiitem.h> +#include <kpixmapeffect.h> +#include <kpixmap.h> + +#include <qframe.h> +#include <qlabel.h> +#include <qlayout.h> +#include <qcursor.h> +#include <qtimer.h> +#include <qfile.h> +#include <qsocketnotifier.h> +#include <qvaluevector.h> +#include <qtooltip.h> + +#include <qdatetime.h> + +#include <stdlib.h> +#include <assert.h> +#include <signal.h> +#ifdef HAVE_SETPRIORITY +#include <sys/time.h> +#include <sys/resource.h> +#endif + +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Intrinsic.h> + +#ifdef HAVE_DPMS +extern "C" { +#include <X11/Xmd.h> +#ifndef Bool +#define Bool BOOL +#endif +#include <X11/extensions/dpms.h> + +#ifndef HAVE_DPMSINFO_PROTO +Status DPMSInfo ( Display *, CARD16 *, BOOL * ); +#endif +} +#endif + +#ifdef HAVE_XF86MISC +#include <X11/extensions/xf86misc.h> +#endif + +#ifdef HAVE_GLXCHOOSEVISUAL +#include <GL/glx.h> +#endif + +#define LOCK_GRACE_DEFAULT 5000 +#define AUTOLOGOUT_DEFAULT 600 + +static Window gVRoot = 0; +static Window gVRootData = 0; +static Atom gXA_VROOT; +static Atom gXA_SCREENSAVER_VERSION; + +//=========================================================================== +// +// Screen saver handling process. Handles screensaver window, +// starting screensaver hacks, and password entry.f +// +LockProcess::LockProcess(bool child, bool useBlankOnly) + : QWidget(0L, "saver window", WX11BypassWM), + mOpenGLVisual(0), + child_saver(child), + mParent(0), + mUseBlankOnly(useBlankOnly), + mSuspended(false), + mVisibility(false), + mRestoreXF86Lock(false), + mForbidden(false), + mAutoLogout(false) +{ + setupSignals(); + + kapp->installX11EventFilter(this); + + // Get root window size + XWindowAttributes rootAttr; + XGetWindowAttributes(qt_xdisplay(), RootWindow(qt_xdisplay(), + qt_xscreen()), &rootAttr); + mRootWidth = rootAttr.width; + mRootHeight = rootAttr.height; + { // trigger creation of QToolTipManager, it does XSelectInput() on the root window + QWidget w; + QToolTip::add( &w, "foo" ); + } + XSelectInput( qt_xdisplay(), qt_xrootwin(), + SubstructureNotifyMask | rootAttr.your_event_mask ); + + // Add non-KDE path + KGlobal::dirs()->addResourceType("scrsav", + KGlobal::dirs()->kde_default("apps") + + "System/ScreenSavers/"); + + // Add KDE specific screensaver path + QString relPath="System/ScreenSavers/"; + KServiceGroup::Ptr servGroup = KServiceGroup::baseGroup( "screensavers"); + if (servGroup) + { + relPath=servGroup->relPath(); + kdDebug(1204) << "relPath=" << relPath << endl; + } + KGlobal::dirs()->addResourceType("scrsav", + KGlobal::dirs()->kde_default("apps") + + relPath); + + // virtual root property + gXA_VROOT = XInternAtom (qt_xdisplay(), "__SWM_VROOT", False); + gXA_SCREENSAVER_VERSION = XInternAtom (qt_xdisplay(), "_SCREENSAVER_VERSION", False); + + connect(&mHackProc, SIGNAL(processExited(KProcess *)), + SLOT(hackExited(KProcess *))); + + connect(&mSuspendTimer, SIGNAL(timeout()), SLOT(suspend())); + + QStringList dmopt = + QStringList::split(QChar(','), + QString::fromLatin1( ::getenv( "XDM_MANAGED" ))); + for (QStringList::ConstIterator it = dmopt.begin(); it != dmopt.end(); ++it) + if ((*it).startsWith("method=")) + mMethod = (*it).mid(7); + + configure(); + +#ifdef HAVE_DPMS + if (mDPMSDepend) { + BOOL on; + CARD16 state; + DPMSInfo(qt_xdisplay(), &state, &on); + if (on) + { + connect(&mCheckDPMS, SIGNAL(timeout()), SLOT(checkDPMSActive())); + // we can save CPU if we stop it as quickly as possible + // but we waste CPU if we check too often -> so take 10s + mCheckDPMS.start(10000); + } + } +#endif + + greetPlugin.library = 0; +} + +//--------------------------------------------------------------------------- +// +// Destructor - usual cleanups. +// +LockProcess::~LockProcess() +{ + if (greetPlugin.library) { + if (greetPlugin.info->done) + greetPlugin.info->done(); + greetPlugin.library->unload(); + } +} + +static int signal_pipe[2]; + +static void sigterm_handler(int) +{ + char tmp = 'T'; + ::write( signal_pipe[1], &tmp, 1); +} + +static void sighup_handler(int) +{ + char tmp = 'H'; + ::write( signal_pipe[1], &tmp, 1); +} + +void LockProcess::timerEvent(QTimerEvent *ev) +{ + if (mAutoLogout && ev->timerId() == mAutoLogoutTimerId) + { + killTimer(mAutoLogoutTimerId); + AutoLogout autologout(this); + execDialog(&autologout); + } +} + +void LockProcess::setupSignals() +{ + struct sigaction act; + // ignore SIGINT + act.sa_handler=SIG_IGN; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGINT); + act.sa_flags = 0; + sigaction(SIGINT, &act, 0L); + // ignore SIGQUIT + act.sa_handler=SIG_IGN; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGQUIT); + act.sa_flags = 0; + sigaction(SIGQUIT, &act, 0L); + // exit cleanly on SIGTERM + act.sa_handler= sigterm_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGTERM); + act.sa_flags = 0; + sigaction(SIGTERM, &act, 0L); + // SIGHUP forces lock + act.sa_handler= sighup_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGHUP); + act.sa_flags = 0; + sigaction(SIGHUP, &act, 0L); + + pipe(signal_pipe); + QSocketNotifier* notif = new QSocketNotifier(signal_pipe[0], + QSocketNotifier::Read, this ); + connect( notif, SIGNAL(activated(int)), SLOT(signalPipeSignal())); +} + + +void LockProcess::signalPipeSignal() +{ + char tmp; + ::read( signal_pipe[0], &tmp, 1); + if( tmp == 'T' ) + quitSaver(); + else if( tmp == 'H' ) { + if( !mLocked ) + startLock(); + } +} + +//--------------------------------------------------------------------------- +bool LockProcess::lock() +{ + if (startSaver()) { + // In case of a forced lock we don't react to events during + // the dead-time to give the screensaver some time to activate. + // That way we don't accidentally show the password dialog before + // the screensaver kicks in because the user moved the mouse after + // selecting "lock screen", that looks really untidy. + mBusy = true; + if (startLock()) + { + QTimer::singleShot(1000, this, SLOT(slotDeadTimePassed())); + return true; + } + stopSaver(); + mBusy = false; + } + return false; +} +//--------------------------------------------------------------------------- +void LockProcess::slotDeadTimePassed() +{ + mBusy = false; +} + +//--------------------------------------------------------------------------- +bool LockProcess::defaultSave() +{ + mLocked = false; + if (startSaver()) { + if (mLockGrace >= 0) + QTimer::singleShot(mLockGrace, this, SLOT(startLock())); + return true; + } + return false; +} + +//--------------------------------------------------------------------------- +bool LockProcess::dontLock() +{ + mLocked = false; + return startSaver(); +} + +//--------------------------------------------------------------------------- +void LockProcess::quitSaver() +{ + stopSaver(); + kapp->quit(); +} + +//--------------------------------------------------------------------------- +// +// Read and apply configuration. +// +void LockProcess::configure() +{ + // the configuration is stored in kdesktop's config file + if( KDesktopSettings::lock() ) + { + mLockGrace = KDesktopSettings::lockGrace(); + if (mLockGrace < 0) + mLockGrace = 0; + else if (mLockGrace > 300000) + mLockGrace = 300000; // 5 minutes, keep the value sane + } + else + mLockGrace = -1; + + if ( KDesktopSettings::autoLogout() ) + { + mAutoLogout = true; + mAutoLogoutTimeout = KDesktopSettings::autoLogoutTimeout(); + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout * 1000); // in milliseconds + } + +#ifdef HAVE_DPMS + //if the user decided that the screensaver should run independent from + //dpms, we shouldn't check for it, aleXXX + mDPMSDepend = KDesktopSettings::dpmsDependent(); +#endif + + mPriority = KDesktopSettings::priority(); + if (mPriority < 0) mPriority = 0; + if (mPriority > 19) mPriority = 19; + + mSaver = KDesktopSettings::saver(); + if (mSaver.isEmpty() || mUseBlankOnly) + mSaver = "KBlankscreen.desktop"; + + readSaver(); + + mPlugins = KDesktopSettings::pluginsUnlock(); + if (mPlugins.isEmpty()) + mPlugins = QStringList("classic"); + mPluginOptions = KDesktopSettings::pluginOptions(); +} + +//--------------------------------------------------------------------------- +// +// Read the command line needed to run the screensaver given a .desktop file. +// +void LockProcess::readSaver() +{ + if (!mSaver.isEmpty()) + { + QString file = locate("scrsav", mSaver); + + bool opengl = kapp->authorize("opengl_screensavers"); + bool manipulatescreen = kapp->authorize("manipulatescreen_screensavers"); + KDesktopFile config(file, true); + if (config.readEntry("X-KDE-Type").utf8()) + { + QString saverType = config.readEntry("X-KDE-Type").utf8(); + QStringList saverTypes = QStringList::split(";", saverType); + for (uint i = 0; i < saverTypes.count(); i++) + { + if ((saverTypes[i] == "ManipulateScreen") && !manipulatescreen) + { + kdDebug(1204) << "Screensaver is type ManipulateScreen and ManipulateScreen is forbidden" << endl; + mForbidden = true; + } + if ((saverTypes[i] == "OpenGL") && !opengl) + { + kdDebug(1204) << "Screensaver is type OpenGL and OpenGL is forbidden" << endl; + mForbidden = true; + } + if (saverTypes[i] == "OpenGL") + { + mOpenGLVisual = true; + } + } + } + + kdDebug(1204) << "mForbidden: " << (mForbidden ? "true" : "false") << endl; + + if (config.hasActionGroup("Root")) + { + config.setActionGroup("Root"); + mSaverExec = config.readPathEntry("Exec"); + } + } +} + +//--------------------------------------------------------------------------- +// +// Create a window to draw our screen saver on. +// +void LockProcess::createSaverWindow() +{ + Visual* visual = CopyFromParent; + XSetWindowAttributes attrs; + int flags = CWOverrideRedirect; +#ifdef HAVE_GLXCHOOSEVISUAL + if( mOpenGLVisual ) + { + static int attribs[][ 15 ] = + { + #define R GLX_RED_SIZE + #define G GLX_GREEN_SIZE + #define B GLX_BLUE_SIZE + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, R, 8, G, 8, B, 8, GLX_DEPTH_SIZE, 8, None }, + { GLX_RGBA, R, 4, G, 4, B, 4, GLX_DEPTH_SIZE, 4, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_DOUBLEBUFFER, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, GLX_STENCIL_SIZE, 1, None }, + { GLX_RGBA, GLX_DEPTH_SIZE, 8, None } + #undef R + #undef G + #undef B + }; + for( unsigned int i = 0; + i < sizeof( attribs ) / sizeof( attribs[ 0 ] ); + ++i ) + { + if( XVisualInfo* info = glXChooseVisual( x11Display(), x11Screen(), attribs[ i ] )) + { + visual = info->visual; + static Colormap colormap = 0; + if( colormap != 0 ) + XFreeColormap( x11Display(), colormap ); + colormap = XCreateColormap( x11Display(), RootWindow( x11Display(), x11Screen()), visual, AllocNone ); + attrs.colormap = colormap; + flags |= CWColormap; + XFree( info ); + break; + } + } + } +#endif + + attrs.override_redirect = 1; + hide(); + Window w = XCreateWindow( x11Display(), RootWindow( x11Display(), x11Screen()), + x(), y(), width(), height(), 0, x11Depth(), InputOutput, visual, flags, &attrs ); + create( w ); + + // Some xscreensaver hacks check for this property + const char *version = "KDE 2.0"; + XChangeProperty (qt_xdisplay(), winId(), + gXA_SCREENSAVER_VERSION, XA_STRING, 8, PropModeReplace, + (unsigned char *) version, strlen(version)); + + XSetWindowAttributes attr; + attr.event_mask = KeyPressMask | ButtonPressMask | PointerMotionMask | + VisibilityChangeMask | ExposureMask; + XChangeWindowAttributes(qt_xdisplay(), winId(), + CWEventMask, &attr); + + // erase(); + + // set NoBackground so that the saver can capture the current + // screen state if necessary + setBackgroundMode(QWidget::NoBackground); + + setCursor( blankCursor ); + setGeometry(0, 0, mRootWidth, mRootHeight); + + kdDebug(1204) << "Saver window Id: " << winId() << endl; +} + +//--------------------------------------------------------------------------- +// +// Hide the screensaver window +// +void LockProcess::hideSaverWindow() +{ + hide(); + lower(); + removeVRoot(winId()); + XDeleteProperty(qt_xdisplay(), winId(), gXA_SCREENSAVER_VERSION); + if ( gVRoot ) { + unsigned long vroot_data[1] = { gVRootData }; + XChangeProperty(qt_xdisplay(), gVRoot, gXA_VROOT, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)vroot_data, 1); + gVRoot = 0; + } + XSync(qt_xdisplay(), False); +} + +//--------------------------------------------------------------------------- +static int ignoreXError(Display *, XErrorEvent *) +{ + return 0; +} + +//--------------------------------------------------------------------------- +// +// Save the current virtual root window +// +void LockProcess::saveVRoot() +{ + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + Window root = RootWindowOfScreen(ScreenOfDisplay(qt_xdisplay(), qt_xscreen())); + + gVRoot = 0; + gVRootData = 0; + + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(ignoreXError); + + if (XQueryTree(qt_xdisplay(), root, &rootReturn, &parentReturn, + &children, &numChildren)) + { + for (unsigned int i = 0; i < numChildren; i++) + { + Atom actual_type; + int actual_format; + unsigned long nitems, bytesafter; + unsigned char *newRoot = 0; + + if ((XGetWindowProperty(qt_xdisplay(), children[i], gXA_VROOT, 0, 1, + False, XA_WINDOW, &actual_type, &actual_format, &nitems, &bytesafter, + &newRoot) == Success) && newRoot) + { + gVRoot = children[i]; + Window *dummy = (Window*)newRoot; + gVRootData = *dummy; + XFree ((char*) newRoot); + break; + } + } + if (children) + { + XFree((char *)children); + } + } + + XSetErrorHandler(oldHandler); +} + +//--------------------------------------------------------------------------- +// +// Set the virtual root property +// +void LockProcess::setVRoot(Window win, Window vr) +{ + if (gVRoot) + removeVRoot(gVRoot); + + unsigned long rw = RootWindowOfScreen(ScreenOfDisplay(qt_xdisplay(), qt_xscreen())); + unsigned long vroot_data[1] = { vr }; + + Window rootReturn, parentReturn, *children; + unsigned int numChildren; + Window top = win; + while (1) { + XQueryTree(qt_xdisplay(), top , &rootReturn, &parentReturn, + &children, &numChildren); + if (children) + XFree((char *)children); + if (parentReturn == rw) { + break; + } else + top = parentReturn; + } + + XChangeProperty(qt_xdisplay(), top, gXA_VROOT, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)vroot_data, 1); +} + +//--------------------------------------------------------------------------- +// +// Remove the virtual root property +// +void LockProcess::removeVRoot(Window win) +{ + XDeleteProperty (qt_xdisplay(), win, gXA_VROOT); +} + +//--------------------------------------------------------------------------- +// +// Grab the keyboard. Returns true on success +// +bool LockProcess::grabKeyboard() +{ + int rv = XGrabKeyboard( qt_xdisplay(), QApplication::desktop()->winId(), + True, GrabModeAsync, GrabModeAsync, CurrentTime ); + + return (rv == GrabSuccess); +} + +#define GRABEVENTS ButtonPressMask | ButtonReleaseMask | PointerMotionMask | \ + EnterWindowMask | LeaveWindowMask + +//--------------------------------------------------------------------------- +// +// Grab the mouse. Returns true on success +// +bool LockProcess::grabMouse() +{ + int rv = XGrabPointer( qt_xdisplay(), QApplication::desktop()->winId(), + True, GRABEVENTS, GrabModeAsync, GrabModeAsync, None, + blankCursor.handle(), CurrentTime ); + + return (rv == GrabSuccess); +} + +//--------------------------------------------------------------------------- +// +// Grab keyboard and mouse. Returns true on success. +// +bool LockProcess::grabInput() +{ + XSync(qt_xdisplay(), False); + + if (!grabKeyboard()) + { + sleep(1); + if (!grabKeyboard()) + { + return false; + } + } + + if (!grabMouse()) + { + sleep(1); + if (!grabMouse()) + { + XUngrabKeyboard(qt_xdisplay(), CurrentTime); + return false; + } + } + + lockXF86(); + + return true; +} + +//--------------------------------------------------------------------------- +// +// Release mouse an keyboard grab. +// +void LockProcess::ungrabInput() +{ + XUngrabKeyboard(qt_xdisplay(), CurrentTime); + XUngrabPointer(qt_xdisplay(), CurrentTime); + unlockXF86(); +} + +//--------------------------------------------------------------------------- +// +// Start the screen saver. +// +bool LockProcess::startSaver() +{ + if (!child_saver && !grabInput()) + { + kdWarning(1204) << "LockProcess::startSaver() grabInput() failed!!!!" << endl; + return false; + } + mBusy = false; + + saveVRoot(); + + if (mParent) { + QSocketNotifier *notifier = new QSocketNotifier(mParent, QSocketNotifier::Read, this, "notifier"); + connect(notifier, SIGNAL( activated (int)), SLOT( quitSaver())); + } + createSaverWindow(); + move(0, 0); + show(); + setCursor( blankCursor ); + + raise(); + XSync(qt_xdisplay(), False); + setVRoot( winId(), winId() ); + startHack(); + return true; +} + +//--------------------------------------------------------------------------- +// +// Stop the screen saver. +// +void LockProcess::stopSaver() +{ + kdDebug(1204) << "LockProcess: stopping saver" << endl; + resume( true ); + stopHack(); + hideSaverWindow(); + mVisibility = false; + if (!child_saver) { + if (mLocked) + DM().setLock( false ); + ungrabInput(); + const char *out = "GOAWAY!"; + for (QValueList<int>::ConstIterator it = child_sockets.begin(); it != child_sockets.end(); ++it) + write(*it, out, sizeof(out)); + } +} + +// private static +QVariant LockProcess::getConf(void *ctx, const char *key, const QVariant &dflt) +{ + LockProcess *that = (LockProcess *)ctx; + QString fkey = QString::fromLatin1( key ) + '='; + for (QStringList::ConstIterator it = that->mPluginOptions.begin(); + it != that->mPluginOptions.end(); ++it) + if ((*it).startsWith( fkey )) + return (*it).mid( fkey.length() ); + return dflt; +} + +void LockProcess::cantLock( const QString &txt) +{ + msgBox( QMessageBox::Critical, i18n("Will not lock the session, as unlocking would be impossible:\n") + txt ); +} + +#if 0 // placeholders for later +i18n("Cannot start <i>kcheckpass</i>."); +i18n("<i>kcheckpass</i> is unable to operate. Possibly it is not SetUID root."); +#endif + +//--------------------------------------------------------------------------- +// +// Make the screen saver password protected. +// +bool LockProcess::startLock() +{ + for (QStringList::ConstIterator it = mPlugins.begin(); it != mPlugins.end(); ++it) { + GreeterPluginHandle plugin; + QString path = KLibLoader::self()->findLibrary( + ((*it)[0] == '/' ? *it : "kgreet_" + *it ).latin1() ); + if (path.isEmpty()) { + kdWarning(1204) << "GreeterPlugin " << *it << " does not exist" << endl; + continue; + } + if (!(plugin.library = KLibLoader::self()->library( path.latin1() ))) { + kdWarning(1204) << "Cannot load GreeterPlugin " << *it << " (" << path << ")" << endl; + continue; + } + if (!plugin.library->hasSymbol( "kgreeterplugin_info" )) { + kdWarning(1204) << "GreeterPlugin " << *it << " (" << path << ") is no valid greet widget plugin" << endl; + plugin.library->unload(); + continue; + } + plugin.info = (kgreeterplugin_info*)plugin.library->symbol( "kgreeterplugin_info" ); + if (plugin.info->method && !mMethod.isEmpty() && mMethod != plugin.info->method) { + kdDebug(1204) << "GreeterPlugin " << *it << " (" << path << ") serves " << plugin.info->method << ", not " << mMethod << endl; + plugin.library->unload(); + continue; + } + if (!plugin.info->init( mMethod, getConf, this )) { + kdDebug(1204) << "GreeterPlugin " << *it << " (" << path << ") refuses to serve " << mMethod << endl; + plugin.library->unload(); + continue; + } + kdDebug(1204) << "GreeterPlugin " << *it << " (" << plugin.info->method << ", " << plugin.info->name << ") loaded" << endl; + greetPlugin = plugin; + mLocked = true; + DM().setLock( true ); + return true; + } + cantLock( i18n("No appropriate greeter plugin configured.") ); + return false; +} + +//--------------------------------------------------------------------------- +// + + +bool LockProcess::startHack() +{ + if (mSaverExec.isEmpty()) + { + return false; + } + + if (mHackProc.isRunning()) + { + stopHack(); + } + + mHackProc.clearArguments(); + + QTextStream ts(&mSaverExec, IO_ReadOnly); + QString word; + ts >> word; + QString path = KStandardDirs::findExe(word); + + if (!path.isEmpty()) + { + mHackProc << path; + + kdDebug(1204) << "Starting hack: " << path << endl; + + while (!ts.atEnd()) + { + ts >> word; + if (word == "%w") + { + word = word.setNum(winId()); + } + mHackProc << word; + } + + if (!mForbidden) + { + + if (mHackProc.start() == true) + { +#ifdef HAVE_SETPRIORITY + setpriority(PRIO_PROCESS, mHackProc.pid(), mPriority); +#endif + //bitBlt(this, 0, 0, &mOriginal); + return true; + } + } + else + // we aren't allowed to start the specified screensaver either because it didn't run for some reason + // according to the kiosk restrictions forbid it + { + setBackgroundColor(black); + } + } + return false; +} + +//--------------------------------------------------------------------------- +// +void LockProcess::stopHack() +{ + if (mHackProc.isRunning()) + { + mHackProc.kill(); + if (!mHackProc.wait(10)) + { + mHackProc.kill(SIGKILL); + } + } +} + +//--------------------------------------------------------------------------- +// +void LockProcess::hackExited(KProcess *) +{ + // Hack exited while we're supposed to be saving the screen. + // Make sure the saver window is black. + setBackgroundColor(black); +} + +void LockProcess::suspend() +{ + if(!mSuspended) + { + mHackProc.kill(SIGSTOP); + QApplication::syncX(); + mSavedScreen = QPixmap::grabWindow( winId()); + } + mSuspended = true; +} + +void LockProcess::resume( bool force ) +{ + if( !force && (!mDialogs.isEmpty() || !mVisibility )) + return; // no resuming with dialog visible or when not visible + if(mSuspended) + { + XForceScreenSaver(qt_xdisplay(), ScreenSaverReset ); + bitBlt( this, 0, 0, &mSavedScreen ); + QApplication::syncX(); + mHackProc.kill(SIGCONT); + } + mSuspended = false; +} + +//--------------------------------------------------------------------------- +// +// Show the password dialog +// This is called only in the master process +// +bool LockProcess::checkPass() +{ + if (mAutoLogout) + killTimer(mAutoLogoutTimerId); + + PasswordDlg passDlg( this, &greetPlugin); + + int ret = execDialog( &passDlg ); + + XWindowAttributes rootAttr; + XGetWindowAttributes(qt_xdisplay(), RootWindow(qt_xdisplay(), + qt_xscreen()), &rootAttr); + if(( rootAttr.your_event_mask & SubstructureNotifyMask ) == 0 ) + { + kdWarning() << "ERROR: Something removed SubstructureNotifyMask from the root window!!!" << endl; + XSelectInput( qt_xdisplay(), qt_xrootwin(), + SubstructureNotifyMask | rootAttr.your_event_mask ); + } + + return ret == QDialog::Accepted; +} + +static void fakeFocusIn( WId window ) +{ + // We have keyboard grab, so this application will + // get keyboard events even without having focus. + // Fake FocusIn to make Qt realize it has the active + // window, so that it will correctly show cursor in the dialog. + XEvent ev; + memset(&ev, 0, sizeof(ev)); + ev.xfocus.display = qt_xdisplay(); + ev.xfocus.type = FocusIn; + ev.xfocus.window = window; + ev.xfocus.mode = NotifyNormal; + ev.xfocus.detail = NotifyAncestor; + XSendEvent( qt_xdisplay(), window, False, NoEventMask, &ev ); +} + +int LockProcess::execDialog( QDialog *dlg ) +{ + dlg->adjustSize(); + + QRect rect = dlg->geometry(); + rect.moveCenter(KGlobalSettings::desktopGeometry(QCursor::pos()).center()); + dlg->move( rect.topLeft() ); + + if (mDialogs.isEmpty()) + { + suspend(); + XChangeActivePointerGrab( qt_xdisplay(), GRABEVENTS, + arrowCursor.handle(), CurrentTime); + } + mDialogs.prepend( dlg ); + fakeFocusIn( dlg->winId()); + int rt = dlg->exec(); + mDialogs.remove( dlg ); + if( mDialogs.isEmpty() ) { + XChangeActivePointerGrab( qt_xdisplay(), GRABEVENTS, + blankCursor.handle(), CurrentTime); + resume( false ); + } else + fakeFocusIn( mDialogs.first()->winId()); + return rt; +} + +void LockProcess::preparePopup() +{ + QWidget *dlg = (QWidget *)sender(); + mDialogs.prepend( dlg ); + fakeFocusIn( dlg->winId() ); +} + +void LockProcess::cleanupPopup() +{ + QWidget *dlg = (QWidget *)sender(); + mDialogs.remove( dlg ); + fakeFocusIn( mDialogs.first()->winId() ); +} + +//--------------------------------------------------------------------------- +// +// X11 Event. +// +bool LockProcess::x11Event(XEvent *event) +{ + switch (event->type) + { + case KeyPress: + case ButtonPress: + case MotionNotify: + if (mBusy || !mDialogs.isEmpty()) + break; + mBusy = true; + if (!mLocked || checkPass()) + { + stopSaver(); + kapp->quit(); + } + else if (mAutoLogout) // we need to restart the auto logout countdown + { + killTimer(mAutoLogoutTimerId); + mAutoLogoutTimerId = startTimer(mAutoLogoutTimeout); + } + mBusy = false; + return true; + + case VisibilityNotify: + if( event->xvisibility.window == winId()) + { // mVisibility == false means the screensaver is not visible at all + // e.g. when switched to text console + mVisibility = !(event->xvisibility.state == VisibilityFullyObscured); + if(!mVisibility) + mSuspendTimer.start(2000, true); + else + { + mSuspendTimer.stop(); + resume( false ); + } + if (event->xvisibility.state != VisibilityUnobscured) + stayOnTop(); + } + break; + + case ConfigureNotify: // from SubstructureNotifyMask on the root window + if(event->xconfigure.event == qt_xrootwin()) + stayOnTop(); + break; + case MapNotify: // from SubstructureNotifyMask on the root window + if( event->xmap.event == qt_xrootwin()) + stayOnTop(); + break; + } + + // We have grab with the grab window being the root window. + // This results in key events being sent to the root window, + // but they should be sent to the dialog if it's visible. + // It could be solved by setFocus() call, but that would mess + // the focus after this process exits. + // Qt seems to be quite hard to persuade to redirect the event, + // so let's simply dupe it with correct destination window, + // and ignore the original one. + if(!mDialogs.isEmpty() && ( event->type == KeyPress || event->type == KeyRelease) + && event->xkey.window != mDialogs.first()->winId()) + { + XEvent ev2 = *event; + ev2.xkey.window = ev2.xkey.subwindow = mDialogs.first()->winId(); + qApp->x11ProcessEvent( &ev2 ); + return true; + } + + return false; +} + +void LockProcess::stayOnTop() +{ + if(!mDialogs.isEmpty()) + { + // this restacking is written in a way so that + // if the stacking positions actually don't change, + // all restacking operations will be no-op, + // and no ConfigureNotify will be generated, + // thus avoiding possible infinite loops + XRaiseWindow( qt_xdisplay(), mDialogs.first()->winId()); // raise topmost + // and stack others below it + Window* stack = new Window[ mDialogs.count() + 1 ]; + int count = 0; + for( QValueList< QWidget* >::ConstIterator it = mDialogs.begin(); + it != mDialogs.end(); + ++it ) + stack[ count++ ] = (*it)->winId(); + stack[ count++ ] = winId(); + XRestackWindows( x11Display(), stack, count ); + delete[] stack; + } + else + XRaiseWindow(qt_xdisplay(), winId()); +} + +void LockProcess::checkDPMSActive() +{ +#ifdef HAVE_DPMS + BOOL on; + CARD16 state; + DPMSInfo(qt_xdisplay(), &state, &on); + //kdDebug() << "checkDPMSActive " << on << " " << state << endl; + if (state == DPMSModeStandby || state == DPMSModeSuspend || state == DPMSModeOff) + { + suspend(); + } else if ( mSuspended ) + { + resume( true ); + } +#endif +} + +#if defined(HAVE_XF86MISC) && defined(HAVE_XF86MISCSETGRABKEYSSTATE) +// see http://cvsweb.xfree86.org/cvsweb/xc/programs/Xserver/hw/xfree86/common/xf86Events.c#rev3.113 +// This allows enabling the "Allow{Deactivate/Closedown}Grabs" options in XF86Config, +// and kdesktop_lock will still lock the session. +static enum { Unknown, Yes, No } can_do_xf86_lock = Unknown; +void LockProcess::lockXF86() +{ + if( can_do_xf86_lock == Unknown ) + { + int major, minor; + if( XF86MiscQueryVersion( qt_xdisplay(), &major, &minor ) + && major >= 0 && minor >= 5 ) + can_do_xf86_lock = Yes; + else + can_do_xf86_lock = No; + } + if( can_do_xf86_lock != Yes ) + return; + if( mRestoreXF86Lock ) + return; + if( XF86MiscSetGrabKeysState( qt_xdisplay(), False ) != MiscExtGrabStateSuccess ) + return; + // success + mRestoreXF86Lock = true; +} + +void LockProcess::unlockXF86() +{ + if( can_do_xf86_lock != Yes ) + return; + if( !mRestoreXF86Lock ) + return; + XF86MiscSetGrabKeysState( qt_xdisplay(), True ); + mRestoreXF86Lock = false; +} +#else +void LockProcess::lockXF86() +{ +} + +void LockProcess::unlockXF86() +{ +} +#endif + +void LockProcess::msgBox( QMessageBox::Icon type, const QString &txt ) +{ + QDialog box( 0, "messagebox", true, WX11BypassWM ); + QFrame *winFrame = new QFrame( &box ); + winFrame->setFrameStyle( QFrame::WinPanel | QFrame::Raised ); + winFrame->setLineWidth( 2 ); + QLabel *label1 = new QLabel( winFrame ); + label1->setPixmap( QMessageBox::standardIcon( type ) ); + QLabel *label2 = new QLabel( txt, winFrame ); + KPushButton *button = new KPushButton( KStdGuiItem::ok(), winFrame ); + button->setDefault( true ); + button->setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); + connect( button, SIGNAL( clicked() ), &box, SLOT( accept() ) ); + + QVBoxLayout *vbox = new QVBoxLayout( &box ); + vbox->addWidget( winFrame ); + QGridLayout *grid = new QGridLayout( winFrame, 2, 2, 10 ); + grid->addWidget( label1, 0, 0, Qt::AlignCenter ); + grid->addWidget( label2, 0, 1, Qt::AlignCenter ); + grid->addMultiCellWidget( button, 1,1, 0,1, Qt::AlignCenter ); + + execDialog( &box ); +} + +#include "lockprocess.moc" diff --git a/kdesktop/lock/lockprocess.h b/kdesktop/lock/lockprocess.h new file mode 100644 index 000000000..d7043e64d --- /dev/null +++ b/kdesktop/lock/lockprocess.h @@ -0,0 +1,131 @@ +//=========================================================================== +// +// 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> +// + +#ifndef __LOCKENG_H__ +#define __LOCKENG_H__ + +#include <kgreeterplugin.h> + +#include <kprocess.h> +#include <kpixmap.h> + +#include <qwidget.h> +#include <qtimer.h> +#include <qvaluestack.h> +#include <qmessagebox.h> +#include <qpixmap.h> + +#include <X11/Xlib.h> + +class KLibrary; + +struct GreeterPluginHandle { + KLibrary *library; + kgreeterplugin_info *info; +}; + +//=========================================================================== +// +// Screen saver handling process. Handles screensaver window, +// starting screensaver hacks, and password entry. +// +class LockProcess + : public QWidget +{ + Q_OBJECT +public: + LockProcess(bool child_saver = false, bool useBlankOnly = false); + ~LockProcess(); + + bool lock(); + + bool defaultSave(); + + bool dontLock(); + + void setChildren(QValueList<int> children) { child_sockets = children; } + void setParent(int fd) { mParent = fd; } + + void msgBox( QMessageBox::Icon type, const QString &txt ); + int execDialog( QDialog* dlg ); + +public slots: + void quitSaver(); + void preparePopup(); + void cleanupPopup(); + +protected: + virtual bool x11Event(XEvent *); + virtual void timerEvent(QTimerEvent *); + +private slots: + void hackExited(KProcess *); + void signalPipeSignal(); + bool startLock(); + void suspend(); + void checkDPMSActive(); + void slotDeadTimePassed(); + +private: + void configure(); + void readSaver(); + void createSaverWindow(); + void hideSaverWindow(); + void saveVRoot(); + void setVRoot(Window win, Window rw); + void removeVRoot(Window win); + bool grabKeyboard(); + bool grabMouse(); + bool grabInput(); + void ungrabInput(); + void cantLock(const QString &reason); + bool startSaver(); + void stopSaver(); + bool startHack(); + void stopHack(); + void setupSignals(); + bool checkPass(); + void stayOnTop(); + void lockXF86(); + void unlockXF86(); + void resume( bool force ); + static QVariant getConf(void *ctx, const char *key, const QVariant &dflt); + + bool mLocked; + int mLockGrace; + int mPriority; + bool mBusy; + KProcess mHackProc; + int mRootWidth; + int mRootHeight; + QString mSaverExec; + QString mSaver; + bool mOpenGLVisual; + bool child_saver; + QValueList<int> child_sockets; + int mParent; + bool mUseBlankOnly; + bool mSuspended; + QTimer mSuspendTimer; + bool mVisibility; + bool mDPMSDepend; + QTimer mCheckDPMS; + QValueStack< QWidget* > mDialogs; + bool mRestoreXF86Lock; + bool mForbidden; + QStringList mPlugins, mPluginOptions; + QString mMethod; + GreeterPluginHandle greetPlugin; + QPixmap mSavedScreen; + int mAutoLogoutTimerId; + int mAutoLogoutTimeout; + bool mAutoLogout; +}; + +#endif + diff --git a/kdesktop/lock/main.cc b/kdesktop/lock/main.cc new file mode 100644 index 000000000..b55e67ea6 --- /dev/null +++ b/kdesktop/lock/main.cc @@ -0,0 +1,174 @@ +/* This file is part of the KDE project + Copyright (C) 1999 David Faure + Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "lockprocess.h" +#include "main.h" +#include "kdesktopsettings.h" + +#include <kcmdlineargs.h> +#include <klocale.h> +#include <kglobal.h> +#include <kdebug.h> +#include <kglobalsettings.h> +#include <dcopref.h> + +#include <stdlib.h> + +#include <X11/Xlib.h> +#include <fixx11h.h> + +bool MyApp::x11EventFilter( XEvent *ev ) +{ + if (ev->type == XKeyPress || ev->type == ButtonPress) + emit activity(); + else if (ev->type == MotionNotify) { + time_t tick = time( 0 ); + if (tick != lastTick) { + lastTick = tick; + emit activity(); + } + } + return KApplication::x11EventFilter( ev ); +} + + +static KCmdLineOptions options[] = +{ + { "forcelock", I18N_NOOP("Force session locking"), 0 }, + { "dontlock", I18N_NOOP("Only start screensaver"), 0 }, + { "blank", I18N_NOOP("Only use the blank screensaver"), 0 }, + KCmdLineLastOption +}; + +// ----------------------------------------------------------------------------- + +int main( int argc, char **argv ) +{ + KLocale::setMainCatalogue("kdesktop"); + + KCmdLineArgs::init( argc, argv, "kdesktop_lock", I18N_NOOP("KDesktop Locker"), I18N_NOOP("Session Locker for KDesktop"), "2.0" ); + KCmdLineArgs::addCmdLineOptions( options ); + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + putenv(strdup("SESSION_MANAGER=")); + + KApplication::disableAutoDcopRegistration(); // not needed + + int kdesktop_screen_number = 0; + int starting_screen = 0; + + bool child = false; + int parent_connection = 0; // socket to the parent saver + QValueList<int> child_sockets; + + if (KGlobalSettings::isMultiHead()) + { + Display *dpy = XOpenDisplay(NULL); + if (! dpy) { + fprintf(stderr, + "%s: FATAL ERROR: couldn't open display '%s'\n", + argv[0], XDisplayName(NULL)); + exit(1); + } + + int number_of_screens = ScreenCount(dpy); + starting_screen = kdesktop_screen_number = DefaultScreen(dpy); + int pos; + QCString display_name = XDisplayString(dpy); + XCloseDisplay(dpy); + kdDebug() << "screen " << number_of_screens << " " << kdesktop_screen_number << " " << display_name << " " << starting_screen << endl; + dpy = 0; + + if ((pos = display_name.findRev('.')) != -1) + display_name.remove(pos, 10); + + QCString env; + if (number_of_screens != 1) { + for (int i = 0; i < number_of_screens; i++) { + if (i != starting_screen) { + int fd[2]; + if (pipe(fd)) { + perror("pipe"); + break; + } + if (fork() == 0) { + child = true; + kdesktop_screen_number = i; + parent_connection = fd[0]; + // break here because we are the child process, we don't + // want to fork() anymore + break; + } else { + child_sockets.append(fd[1]); + } + } + } + + env.sprintf("DISPLAY=%s.%d", display_name.data(), + kdesktop_screen_number); + kdDebug() << "env " << env << endl; + + if (putenv(strdup(env.data()))) { + fprintf(stderr, + "%s: WARNING: unable to set DISPLAY environment variable\n", + argv[0]); + perror("putenv()"); + } + } + } + + MyApp app; + kdDebug() << "app " << kdesktop_screen_number << " " << starting_screen << " " << child << " " << child_sockets.count() << " " << parent_connection << endl; + app.disableSessionManagement(); + KGlobal::locale()->insertCatalogue("libdmctl"); + + // we need to read from the right rc file - possibly taking screen number in account + KDesktopSettings::instance("kdesktoprc"); + + LockProcess process(child, args->isSet( "blank" )); + if (!child) + process.setChildren(child_sockets); + else + process.setParent(parent_connection); + + bool rt; + bool sig = false; + if( !child && args->isSet( "forcelock" )) + { + rt = process.lock(); + sig = true; + } + else if( child || args->isSet( "dontlock" )) + rt = process.dontLock(); + else + rt = process.defaultSave(); + if (!rt) + return 1; + + if( sig ) + { + DCOPRef ref( "kdesktop", "KScreensaverIface"); + ref.send( "saverLockReady" ); + } + + return app.exec(); +} + +#include "main.moc" diff --git a/kdesktop/lock/main.h b/kdesktop/lock/main.h new file mode 100644 index 000000000..c8e0e05a4 --- /dev/null +++ b/kdesktop/lock/main.h @@ -0,0 +1,39 @@ +/* This file is part of the KDE project + Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef _MAIN_H +#define _MAIN_H + +#include <kapplication.h> + +#include <time.h> + +class MyApp : public KApplication { + Q_OBJECT +public: + MyApp() : KApplication(), lastTick( 0 ) {} +protected: + bool x11EventFilter( XEvent * ); +signals: + void activity(); +private: + time_t lastTick; +}; + +#endif |