/* * scrnsave.cpp * Copyright 1997 Matthias Hoelzer * Copyright 1996,1999,2002 Martin R. Jones * Copyright 2004 Chris Howells * Copyright 2007-2008 Benjamin Meyer * Copyright 2007-2008 Hamish Rodda * Copyright 2009 Dario Andres Rodriguez * Copyright 2009 Davide Bettio * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include #include #include #include "scrnsave.h" #include template class QList; const uint widgetEventMask = // X event mask (uint)( ExposureMask | PropertyChangeMask | StructureNotifyMask ); //=========================================================================== // DLL Interface for kcontrol K_PLUGIN_FACTORY(KSSFactory, registerPlugin(); ) K_EXPORT_PLUGIN(KSSFactory("kcmscreensaver")) static QString findExe(const QString &exe) { QString result = KStandardDirs::locate("exe", exe); if (result.isEmpty()) result = KStandardDirs::findExe(exe); return result; } KScreenSaver::KScreenSaver(QWidget *parent, const QVariantList&) : KCModule(KSSFactory::componentData(), parent) { mSetupProc = 0; mPreviewProc = 0; mTestWin = 0; mTestProc = 0; mPrevSelected = -2; mMonitor = 0; mTesting = false; setQuickHelp( i18n("

Screen Saver

This module allows you to enable and" " configure a screen saver. Note that you can enable a screen saver" " even if you have power saving features enabled for your display.

" "

Besides providing an endless variety of entertainment and" " preventing monitor burn-in, a screen saver also gives you a simple" " way to lock your display if you are going to leave it unattended" " for a while. If you want the screen saver to lock the session, make sure you enable" " the \"Require password\" feature of the screen saver; if you do not, you can still" " explicitly lock the session using the desktop's \"Lock Session\" action.

")); setButtons( KCModule::Help | KCModule::Apply ); setupUi(this); readSettings(); mSetupProc = new KProcess; connect(mSetupProc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotSetupDone())); mPreviewProc = new KProcess; connect(mPreviewProc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotPreviewExited())); mSaverListView->setColumnCount(1); mSaverListView->header()->hide(); mSelected = -1; connect( mSaverListView, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(slotSetup())); connect( mSetupBt, SIGNAL(clicked()), SLOT(slotSetup()) ); connect( mTestBt, SIGNAL(clicked()), SLOT(slotTest()) ); mEnabledCheckBox->setChecked(mEnabled); connect(mEnabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotEnable(bool))); mWaitEdit->setRange(1, INT_MAX); mWaitEdit->setSuffix(ki18ncp("unit of time. minutes until the screensaver is triggered", " minute", " minutes")); mWaitEdit->setValue(mTimeout/60); mWaitEdit->setEnabled(mEnabled); connect(mWaitEdit, SIGNAL(valueChanged(int)), this, SLOT(slotTimeoutChanged(int))); mLockCheckBox->setEnabled( mEnabled ); mLockCheckBox->setChecked( mLock ); connect( mLockCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotLock(bool)) ); mWaitLockEdit->setRange(1, 300); mWaitLockEdit->setSuffix(ki18np(" second", " seconds")); mWaitLockEdit->setValue(mLockTimeout/1000); mWaitLockEdit->setEnabled(mEnabled && mLock); connect(mWaitLockEdit, SIGNAL(valueChanged(int)), this, SLOT(slotLockTimeoutChanged(int))); connect(mPlasmaWidgetsRadio, SIGNAL(toggled(bool)), this, SLOT(slotEnablePlasma(bool))); connect(mScreenSaverRadio, SIGNAL(toggled(bool)), this, SLOT(slotEnableLegacyScreenSaver(bool))); mPlasmaSetup->setEnabled(mPlasmaEnabled); connect(mPlasmaSetup, SIGNAL(clicked()), this, SLOT(slotPlasmaSetup())); mMonitorPreview = new ScreenPreviewWidget(this); mMonitorPreview->setFixedSize(200,220); QDesktopWidget *desktop = QApplication::desktop(); QRect avail = desktop->availableGeometry(desktop->screenNumber(this)); mMonitorPreview->setRatio((qreal)avail.width()/(qreal)avail.height()); mMonitorPreview->setWhatsThis( i18n("A preview of the selected screen saver.") ); mPreviewAreaWidget->layout()->addWidget(mMonitorPreview); if (mImmutable) { setButtons(buttons() & ~Default); mSettingsGroup->setEnabled(false); } // finding the savers can take some time, so defer loading until // we've started up. mNumLoaded = 0; mLoadTimer = new QTimer( this ); connect( mLoadTimer, SIGNAL(timeout()), SLOT(findSavers()) ); mLoadTimer->start( 100 ); mChanged = false; emit changed(false); KAboutData *about = new KAboutData(I18N_NOOP("kcmscreensaver"), 0, ki18n("KDE Screen Saver Control Module"), 0, KLocalizedString(), KAboutData::License_GPL, ki18n("(c) 1997-2002 Martin R. Jones\n" "(c) 2003-2004 Chris Howells")); about->addAuthor(ki18n("Chris Howells"), KLocalizedString(), "howells@kde.org"); about->addAuthor(ki18n("Martin R. Jones"), KLocalizedString(), "jones@kde.org"); setAboutData( about ); } //--------------------------------------------------------------------------- // bool KScreenSaver::event(QEvent *e) { if (e->type() == QEvent::Resize) { if (mMonitor) mMonitor->setGeometry(mMonitorPreview->previewRect()); } else if (e->type() == QEvent::KeyPress || e->type() == QEvent::MouseButtonPress) { if (mTesting) { slotStopTest(); return true; } } return KCModule::event(e); } //--------------------------------------------------------------------------- // KScreenSaver::~KScreenSaver() { if (mPreviewProc) { if (mPreviewProc->state() == QProcess::Running) { //Avoid triggering slotPreviewExited on close mPreviewProc->disconnect(this); mPreviewProc->kill( ); mPreviewProc->waitForFinished( ); } delete mPreviewProc; } if (mSetupProc) { if (mSetupProc->state() == QProcess::Running) { //Avoid triggering slotSetupDone on close mSetupProc->disconnect(this); mSetupProc->kill( ); mSetupProc->waitForFinished( ); } delete mSetupProc; } delete mTestProc; delete mTestWin; qDeleteAll(mSaverList); } //--------------------------------------------------------------------------- // void KScreenSaver::load() { readSettings(); //with the following line, the Test and Setup buttons are not enabled correctly //if no saver was selected, the "Reset" and the "Enable screensaver", it is only called when starting and when pressing reset, aleXXX // mSelected = -1; QTreeWidgetItem * selectedItem = treeItemForSaverFile(mSaver); if (selectedItem) { mSelected = indexForSaverFile(mSaver); mSaverListView->setCurrentItem(selectedItem, QItemSelectionModel::ClearAndSelect); slotScreenSaver(selectedItem); } updateValues(); mChanged = false; emit changed(false); } //------------------------------------------------------------After--------------- // void KScreenSaver::readSettings() { KConfigGroup config( KSharedConfig::openConfig( "kscreensaverrc"), "ScreenSaver" ); mImmutable = config.isImmutable(); mEnabled = config.readEntry("Enabled", false); mTimeout = config.readEntry("Timeout", 300); mLockTimeout = config.readEntry("LockGrace", 60000); mLock = config.readEntry("Lock", false); mSaver = config.readEntry("Saver"); bool legacyScreenSaver = config.readEntry("LegacySaverEnabled", false); mScreenSaverRadio->setChecked(legacyScreenSaver); if (!legacyScreenSaver) { mPlasmaEnabled = config.readEntry("PlasmaEnabled", false); mPlasmaWidgetsRadio->setChecked(mPlasmaEnabled); } else { mPlasmaEnabled = false; } if (!legacyScreenSaver && !mPlasmaEnabled) { mSimpleLockerRadio->setChecked(true); } if (mTimeout < 60) mTimeout = 60; if (mLockTimeout < 0) mLockTimeout = 0; if (mLockTimeout > 300000) mLockTimeout = 300000; mChanged = false; } //--------------------------------------------------------------------------- // void KScreenSaver::updateValues() { if (mEnabled) { mWaitEdit->setValue(mTimeout/60); } else { mWaitEdit->setValue(0); } mWaitLockEdit->setValue(mLockTimeout/1000); mLockCheckBox->setChecked(mLock); } //--------------------------------------------------------------------------- // void KScreenSaver::defaults() { if (mImmutable) return; slotScreenSaver( 0 ); QTreeWidgetItem *item = mSaverListView->topLevelItem(0); if (item) { mSaverListView->setCurrentItem(item, QItemSelectionModel::ClearAndSelect); mSaverListView->scrollToItem(item); } slotTimeoutChanged( 5 ); slotLockTimeoutChanged( 60 ); slotLock( false ); mEnabledCheckBox->setChecked(false); mSimpleLockerRadio->setChecked(true); mPlasmaSetup->setEnabled(false); updateValues(); emit changed(true); } //--------------------------------------------------------------------------- // void KScreenSaver::save() { if ( !mChanged ) return; KConfigGroup config(KSharedConfig::openConfig( "kscreensaverrc"), "ScreenSaver" ); config.writeEntry("Enabled", mEnabled); config.writeEntry("Timeout", mTimeout); config.writeEntry("LockGrace", mLockTimeout); config.writeEntry("Lock", mLock); config.writeEntry("PlasmaEnabled", mPlasmaEnabled); config.writeEntry("LegacySaverEnabled", mScreenSaverRadio->isChecked()); if ( !mSaver.isEmpty() ) config.writeEntry("Saver", mSaver); config.sync(); org::kde::screensaver kscreensaver("org.kde.screensaver", "/ScreenSaver", QDBusConnection::sessionBus()); kscreensaver.configure(); mChanged = false; emit changed(false); } //--------------------------------------------------------------------------- // void KScreenSaver::findSavers() { if ( !mNumLoaded ) { mSaverServices = KServiceTypeTrader::self()->query( "ScreenSaver"); new QTreeWidgetItem ( mSaverListView, QStringList() << i18n("Loading...") ); if ( mSaverServices.isEmpty() ) mLoadTimer->stop(); else mLoadTimer->start( 50 ); } for( KService::List::const_iterator it = mSaverServices.constBegin(); it != mSaverServices.constEnd(); it++,mNumLoaded++) { SaverConfig *saver = new SaverConfig; QString file = KStandardDirs::locate("services", (*it)->entryPath()); if (saver->read(file)) { mSaverList.append(saver); } else delete saver; } if ( mNumLoaded != mSaverServices.count() ) { return; } int categoryCount = 0; mLoadTimer->stop(); delete mLoadTimer; qSort(mSaverList.begin(), mSaverList.end()); mSaverListView->clear(); //Create the treewidget items Q_FOREACH( SaverConfig *s, mSaverList ) { QTreeWidgetItem *item = 0; if (s->category().isEmpty()) { //Create top level item without category item = new QTreeWidgetItem ( mSaverListView, QStringList() << s->name() << '2' + s->name() ); item->setData(0, Qt::UserRole, s->file()); } else { //Create item within a category QList categoryItemList = mSaverListView->findItems( s->category(), Qt::MatchExactly ); QTreeWidgetItem * categoryItem = 0; if ( categoryItemList.isEmpty() ) { //Create the category toplevel item categoryItem = new QTreeWidgetItem ( mSaverListView, QStringList() << s->category() << '1' + s->category() ); categoryItem->setIcon ( 0, KIcon ( "preferences-desktop-screensaver" ) ); } else { categoryItem = categoryItemList.first(); } //Add the child item to the category item = new QTreeWidgetItem ( categoryItem, QStringList() << s->name() << '2' + s->name() ); item->setData(0, Qt::UserRole, s->file()); categoryCount++; } } //Get the current Item and index mSelected = indexForSaverFile(mSaver); QTreeWidgetItem *selectedItem = treeItemForSaverFile(mSaver); // Delete categories with only one item QList itemsToBeDeleted; QList itemsToBeAddedAsTopLevel; QTreeWidgetItemIterator it(mSaverListView, QTreeWidgetItemIterator::HasChildren); while ((*it)) { if ((*it)->childCount() == 1) { QTreeWidgetItem * item = (*it)->child(0); (*it)->removeChild(item); itemsToBeAddedAsTopLevel.append(item); itemsToBeDeleted.append((*it)); } ++it; } mSaverListView->addTopLevelItems(itemsToBeAddedAsTopLevel); qDeleteAll(itemsToBeDeleted); mSaverListView->setRootIsDecorated ( categoryCount > 0 ); mSaverListView->sortByColumn ( 0, Qt::AscendingOrder ); //Set the current screensaver if ( mSelected > -1 ) { mSaverListView->setCurrentItem(selectedItem, QItemSelectionModel::ClearAndSelect); mSaverListView->scrollToItem(selectedItem); mSetupBt->setEnabled(!mSaverList.at(mSelected)->setup().isEmpty()); mTestBt->setEnabled(true); } connect( mSaverListView, SIGNAL(itemSelectionChanged()), this, SLOT(slotSelectionChanged()) ); setMonitor(); } void KScreenSaver::slotSelectionChanged() { QList selection = mSaverListView->selectedItems(); if (selection.isEmpty()) { slotScreenSaver(0); } else { slotScreenSaver(selection.at(0)); } } //--------------------------------------------------------------------------- // void KScreenSaver::setMonitor() { if (mPreviewProc->state() == QProcess::Running) // CC: this will automatically cause a "slotPreviewExited" // when the viewer exits mPreviewProc->kill(); else slotPreviewExited(); } //--------------------------------------------------------------------------- // void KScreenSaver::slotPreviewExited() { // Ugly hack to prevent continual respawning of savers that crash if (mSelected == mPrevSelected) return; if ( mSaverList.isEmpty() ) // safety check return; // Some xscreensaver hacks do something nasty to the window that // requires a new one to be created (or proper investigation of the // problem). delete mMonitor; mMonitor = new KSSMonitor(mMonitorPreview); QPalette palette; palette.setColor(mMonitor->backgroundRole(), Qt::black); mMonitor->setPalette(palette); mMonitor->setGeometry(mMonitorPreview->previewRect()); mMonitor->setVisible(mScreenSaverRadio->isChecked()); // So that hacks can XSelectInput ButtonPressMask XSelectInput(QX11Info::display(), mMonitor->winId(), widgetEventMask ); if (mSelected >= 0) { mPreviewProc->clearProgram(); QString saver = mSaverList.at(mSelected)->saver(); QHash keyMap; keyMap.insert('w', QString::number(mMonitor->winId())); *mPreviewProc << KShell::splitArgs(KMacroExpander::expandMacrosShellQuote(saver, keyMap)); mPreviewProc->start(); } mPrevSelected = mSelected; } //--------------------------------------------------------------------------- // void KScreenSaver::slotEnable(bool e) { mEnabled = e; mWaitEdit->setEnabled( e ); mLockCheckBox->setEnabled( e ); mWaitLockEdit->setEnabled( e && mLock ); mChanged = true; emit changed(true); } void KScreenSaver::slotEnablePlasma(bool enable) { mPlasmaEnabled = enable; //FIXME even though the button's enabled, plasma isn't until the user hits apply //so the button will just show the screensaver, no plasma. //what should I do about this? mPlasmaSetup->setEnabled(mPlasmaEnabled); mChanged = true; emit changed(true); } void KScreenSaver::slotEnableLegacyScreenSaver(bool enable) { if (mMonitor) { mMonitor->setVisible(enable); } mChanged = true; emit changed(true); } void KScreenSaver::slotPlasmaSetup() { org::kde::screensaver kscreensaver("org.kde.screensaver", "/ScreenSaver", QDBusConnection::sessionBus()); kscreensaver.setupPlasma(); } //--------------------------------------------------------------------------- // void KScreenSaver::slotScreenSaver(QTreeWidgetItem *item) { if (!item) { mSetupBt->setEnabled(false); mTestBt->setEnabled(false); return; } //Get the index of the saver file of the current selected treewidget item int indx = indexForSaverFile(item->data(0, Qt::UserRole).toString()); mSetupBt->setEnabled(item->childCount()==0); mTestBt->setEnabled(item->childCount()==0); if (indx == -1) { mSelected = -1; return; } bool bChanged = (indx != mSelected); if (mSetupProc->state() != QProcess::Running) mSetupBt->setEnabled(!mSaverList.at(indx)->setup().isEmpty()); mTestBt->setEnabled(true); mSaver = mSaverList.at(indx)->file(); mSelected = indx; setMonitor(); if (bChanged) { mChanged = true; emit changed(true); } } //--------------------------------------------------------------------------- // void KScreenSaver::slotSetup() { if ( mSelected < 0 ) return; if (mSetupProc->state() == QProcess::Running) return; mSetupProc->clearProgram(); QString saver = mSaverList.at(mSelected)->setup(); if( saver.isEmpty()) return; QTextStream ts(&saver, QIODevice::ReadOnly); QString word; ts >> word; bool kxsconfig = word == "kxsconfig"; QString path = findExe(word); if (!path.isEmpty()) { (*mSetupProc) << path; // Add caption and icon to about dialog if (!kxsconfig) { word = "-caption"; (*mSetupProc) << word; word = mSaverList.at(mSelected)->name(); (*mSetupProc) << word; word = "-icon"; (*mSetupProc) << word; word = "kscreensaver"; (*mSetupProc) << word; } while (!ts.atEnd()) { ts >> word; (*mSetupProc) << word; } // Pass translated name to kxsconfig if (kxsconfig) { word = mSaverList.at(mSelected)->name(); (*mSetupProc) << word; } mSetupBt->setEnabled( false ); kapp->flush(); mSetupProc->start(); } } //--------------------------------------------------------------------------- // void KScreenSaver::slotTest() { if ( mSelected == -1 ) return; if (!mTestProc) { mTestProc = new KProcess; } else { mPreviewProc->kill(); mPreviewProc->waitForFinished(); mTestProc->clearProgram(); } if (!mTestWin) { mTestWin = new TestWin(); mTestWin->setAttribute(Qt::WA_NoSystemBackground, true); mTestWin->setAttribute(Qt::WA_PaintOnScreen, true); mTestWin->setGeometry(qApp->desktop()->geometry()); } mTestWin->show(); mTestWin->raise(); mTestWin->setFocus(); // So that hacks can XSelectInput ButtonPressMask XSelectInput(QX11Info::display(), mTestWin->winId(), widgetEventMask ); grabMouse(); grabKeyboard(); mTestBt->setEnabled( false ); QString saver = mSaverList.at(mSelected)->saver(); QHash keyMap; keyMap.insert('w', QString::number(mTestWin->winId())); *mTestProc << KShell::splitArgs(KMacroExpander::expandMacrosShellQuote(saver, keyMap)); mTestProc->start(); mTesting = true; } //--------------------------------------------------------------------------- // void KScreenSaver::slotStopTest() { if (mTestProc->state() == QProcess::Running) { mTestProc->kill(); mTestProc->waitForFinished(500); } releaseMouse(); releaseKeyboard(); mTestWin->hide(); mTestBt->setEnabled(true); mPrevSelected = -1; setMonitor(); mTesting = false; } //--------------------------------------------------------------------------- // void KScreenSaver::slotTimeoutChanged(int to ) { mTimeout = to * 60; mChanged = true; emit changed(true); } //----------------------------------------------------------------------- // void KScreenSaver::slotLockTimeoutChanged(int to ) { mLockTimeout = to * 1000; mChanged = true; emit changed(true); } //--------------------------------------------------------------------------- // void KScreenSaver::slotLock( bool l ) { mLock = l; mWaitLockEdit->setEnabled( l ); mChanged = true; emit changed(true); } //--------------------------------------------------------------------------- // void KScreenSaver::slotSetupDone() { mPrevSelected = -1; // see ugly hack in slotPreviewExited() setMonitor(); mSetupBt->setEnabled( true ); emit changed(true); } QTreeWidgetItem * KScreenSaver::treeItemForSaverFile(const QString & saver) { QTreeWidgetItem * item = 0; QTreeWidgetItemIterator it(mSaverListView); while ((*it) && item == 0) { if ((*it)->data(0, Qt::UserRole) == saver) { item = (*it); } ++it; } return item; } int KScreenSaver::indexForSaverFile(const QString & saver) { int index = -1; int i = 0; Q_FOREACH( SaverConfig* saverConfig, mSaverList ) { if (saverConfig->file() == saver) { index = i; break; } i++; } return index; } #include "scrnsave.moc"