Back to index

kdeartwork  4.3.2
kxsconfig.cpp
Go to the documentation of this file.
00001 //-----------------------------------------------------------------------------
00002 //
00003 // KDE xscreensaver configuration dialog
00004 //
00005 // Copyright (c)  Martin R. Jones <mjones@kde.org> 1999
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public
00009 // License as published by the Free Software Foundation;
00010 // version 2 of the License.
00011 //
00012 // This program is distributed in the hope that it will be useful,
00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015 // General Public License for more details.
00016 //
00017 // You should have received a copy of the GNU General Public License
00018 // along with this program; see the file COPYING.  If not, write to
00019 // the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00020 // Boston, MA 02110-1301, USA.
00021 
00022 // This file contains code copied from xscreensaver.  The xscreensaver
00023 // copyright notice follows.
00024 
00025 /*
00026  * xscreensaver, Copyright (c) 1993-2002 Jamie Zawinski <jwz@jwz.org>
00027  *
00028  * Permission to use, copy, modify, distribute, and sell this software and its
00029  * documentation for any purpose is hereby granted without fee, provided that
00030  * the above copyright notice appear in all copies and that both that
00031  * copyright notice and this permission notice appear in supporting
00032  * documentation.  No representations are made about the suitability of this
00033  * software for any purpose.  It is provided "as is" without express or
00034  * implied warranty.
00035  */
00036 #include <config-kxsconfig.h>
00037 
00038 #include <stdlib.h>
00039 #include <qlayout.h>
00040 #include <qtimer.h>
00041 #include <kvbox.h>
00042 #include <qlabel.h>
00043 #include <qfileinfo.h>
00044 #include <QVBoxLayout>
00045 #include <QHBoxLayout>
00046 #include <QBoxLayout>
00047 
00048 #include <kdebug.h>
00049 #include <kapplication.h>
00050 #include <kconfig.h>
00051 #include <kstandarddirs.h>
00052 #include <klocale.h>
00053 #include <kcmdlineargs.h>
00054 #include <kshell.h>
00055 #include <kmessagebox.h>
00056 
00057 #include "kxsconfig.h"
00058 #include "kxscontrol.h"
00059 #include "kxsxml.h"
00060 
00061 #include <X11/Xlib.h>
00062 #include <X11/Xatom.h>
00063 #include <X11/IntrinsicP.h>
00064 #include <X11/ShellP.h>
00065 
00066 int ignoreXError(Display *, XErrorEvent *);
00067 
00068 //===========================================================================
00069 
00070 const uint widgetEventMask =                 // X event mask
00071 (uint)(
00072        ExposureMask |
00073        PropertyChangeMask |
00074        StructureNotifyMask
00075       );
00076 
00077 KXSConfigDialog::KXSConfigDialog(const QString &filename, const QString &name)
00078   : KDialog(0),
00079     mFilename(filename), mPreviewProc(0), mKilled(false)
00080 {
00081     Q_UNUSED(name)
00082 
00083     setButtons(Ok| Cancel);
00084     setDefaultButton( Ok);
00085     setModal(false);
00086     int slash = filename.lastIndexOf('/');
00087     if (slash >= 0)
00088        mConfigFile = filename.mid(slash+1);
00089     else
00090        mConfigFile = filename;
00091 
00092     mExeName = mConfigFile;
00093     mConfigFile += "rc";
00094     connect( this,SIGNAL(okClicked()),this,SLOT(slotOk()));
00095     connect( this,SIGNAL(cancelClicked()),this,SLOT(slotCancel()));
00096 }
00097 
00098 bool KXSConfigDialog::create()
00099 {
00100     QWidget *main = new QWidget(this);
00101     setMainWidget(main);
00102     QVBoxLayout *topLayout = new QVBoxLayout(main);
00103     topLayout->setSpacing(spacingHint());
00104     QHBoxLayout *layout = new QHBoxLayout();
00105     topLayout->addLayout(layout);
00106     layout->setSpacing(spacingHint());
00107     KVBox *controlLayout = new KVBox(main);
00108     controlLayout->setSpacing(spacingHint());
00109     layout->addWidget(controlLayout);
00110     ((QBoxLayout*)controlLayout->layout())->addStrut(120);
00111 
00112     KConfig config(mConfigFile);
00113 
00114     QString xmlFile = "/doesntexist";
00115 #ifdef XSCREENSAVER_CONFIG_DIR
00116     xmlFile = XSCREENSAVER_CONFIG_DIR;
00117 #endif
00118 
00119     xmlFile += '/' + mExeName + ".xml";
00120     if ( QFile::exists( xmlFile ) ) {
00121        // We can use the xscreensaver xml config files.
00122        KXSXml xmlParser(controlLayout);
00123        xmlParser.parse( xmlFile );
00124        mConfigItemList = xmlParser.items();
00125        QWidget *spacer = new QWidget(controlLayout);
00126        controlLayout->setStretchFactor(spacer, 1 );
00127        QString descr = xmlParser.description();
00128        if ( !descr.isEmpty() ) {
00129            descr = descr.replace('\n', ' ').simplified();
00130            QLabel *l = new QLabel( i18n( descr.toUtf8() ), main );
00131            l->setWordWrap( 1 );
00132            topLayout->addWidget( l );
00133        }
00134     } else {
00135         // fall back to KDE's old config files.
00136        int idx = 0;
00137        while (true) {
00138            QString group = QString("Arg%1").arg(idx);
00139            if (config.hasGroup(group)) {
00140               KConfigGroup grp = config.group(group);
00141               QString type = grp.readEntry("Type");
00142               if (type == "Range") {
00143                   KXSRangeControl *rc = new KXSRangeControl(controlLayout, group, config);
00144                   mConfigItemList.append(rc);
00145               } else if (type == "DoubleRange") {
00146                   KXSDoubleRangeControl *rc =
00147                      new KXSDoubleRangeControl(controlLayout, group, config);
00148                   mConfigItemList.append(rc);
00149               } else if (type == "Check") {
00150                   KXSCheckBoxControl *cc = new KXSCheckBoxControl(controlLayout, group,
00151                          config);
00152                   mConfigItemList.append(cc);
00153               } else if (type == "DropList") {
00154                   KXSDropListControl *dl = new KXSDropListControl(controlLayout, group,
00155                          config);
00156                   mConfigItemList.append(dl);
00157               }
00158            } else {
00159               break;
00160            }
00161            idx++;
00162        }
00163        if ( idx == 0 )
00164            return false;
00165     }
00166 
00167     for ( int i = 0; i < mConfigItemList.size(); i++ ) {
00168         KXSConfigItem *item = mConfigItemList[i];
00169         item->read( config );
00170         QWidget *widget = dynamic_cast<QWidget*>( item );
00171         if ( widget ) {
00172             connect( widget, SIGNAL(changed()), SLOT(slotChanged()) );
00173         }
00174     }
00175 
00176     mPreviewProc = new KProcess;
00177     connect(mPreviewProc, SIGNAL(finished(int,QProcess::ExitStatus)),
00178            SLOT(slotPreviewProcFinished(int,QProcess::ExitStatus)));
00179 
00180     mPreviewTimer = new QTimer(this);
00181     mPreviewTimer->setSingleShot(true);
00182     connect(mPreviewTimer, SIGNAL(timeout()), SLOT(slotNewPreview()));
00183 
00184     mPreview = new QWidget(main);
00185     mPreview->setFixedSize(250, 200);
00186     {
00187         QPalette palette;
00188         palette.setColor(mPreview->backgroundRole(), Qt::black);
00189         mPreview->setPalette(palette);
00190        mPreview->setAutoFillBackground(true);
00191     }
00192 
00193     layout->addWidget(mPreview);
00194     show();
00195 
00196     // So that hacks can XSelectInput ButtonPressMask
00197     XSelectInput(QX11Info::display(), mPreview->winId(), widgetEventMask );
00198 
00199     startProcess();
00200     return true;
00201 }
00202 
00203 //---------------------------------------------------------------------------
00204 KXSConfigDialog::~KXSConfigDialog()
00205 {
00206   if (mPreviewProc && mPreviewProc->state() == QProcess::Running) {
00207     mPreviewProc->kill();
00208     mPreviewProc->waitForFinished(5000);
00209     delete mPreviewProc;
00210   }
00211 }
00212 
00213 //---------------------------------------------------------------------------
00214 QString KXSConfigDialog::command()
00215 {
00216   QString cmd;
00217 
00218   for (int i = 0; i < mConfigItemList.size(); i++)
00219   {
00220     if (mConfigItemList[i]) {
00221       cmd += " " + mConfigItemList[i]->command();
00222     }
00223   }
00224 
00225   return cmd;
00226 }
00227 
00228 //---------------------------------------------------------------------------
00229 void KXSConfigDialog::startProcess()
00230 {
00231     mKilled = false;
00232     mPreviewProc->clearProgram();
00233     QString saver = QString( "%1 -window-id 0x%2" )
00234         .arg( mFilename )
00235         .arg( long(mPreview->winId()), 0, 16 );
00236     saver += command();
00237     saver = saver.trimmed();
00238 
00239     kDebug() << "Command: " <<  saver;
00240 
00241     int i = 0;
00242     QString exe;
00243     while ( !saver[i].isSpace() ) {
00244         exe += saver[i++];
00245     }
00246     // work around a KStandarDirs::findExe() "feature" where it looks in
00247     // $KDEDIR/bin first no matter what and sometimes finds the wrong executable
00248     QFileInfo checkExe;
00249     QString saverdir = QString(XSCREENSAVER_HACKS_DIR "/") + exe;
00250     QString path;
00251     checkExe.setFile(saverdir);
00252     if (checkExe.exists() && checkExe.isExecutable() && checkExe.isFile())
00253     {
00254         path = saverdir;
00255     }
00256     if (!path.isEmpty()) {
00257         (*mPreviewProc) << path;
00258 
00259         (*mPreviewProc) << KShell::splitArgs(saver.mid(i));
00260 
00261         mPreviewProc->start();
00262     }
00263 }
00264 
00265 //---------------------------------------------------------------------------
00266 void KXSConfigDialog::slotPreviewProcFinished(int exitCode, QProcess::ExitStatus exitStatus)
00267 {
00268     Q_UNUSED(exitCode)
00269     Q_UNUSED(exitStatus)
00270 
00271     if ( mKilled ) {
00272         startProcess();
00273     } else {
00274        // stops us from spawning the hack really fast, but still not the best
00275        QString path = KStandardDirs::findExe(mFilename, XSCREENSAVER_HACKS_DIR);
00276        if ( QFile::exists(path) ) {
00277            mKilled = true;
00278            slotChanged();
00279        }
00280     }
00281 }
00282 
00283 //---------------------------------------------------------------------------
00284 void KXSConfigDialog::slotNewPreview()
00285 {
00286   if (mPreviewProc->state() == QProcess::Running) {
00287     mKilled = true;
00288     mPreviewProc->kill(); // restarted in slotPreviewExited()
00289   } else {
00290     startProcess();
00291   }
00292 }
00293 
00294 //---------------------------------------------------------------------------
00295 void KXSConfigDialog::slotChanged()
00296 {
00297     mPreviewTimer->start(1000);
00298 }
00299 
00300 //---------------------------------------------------------------------------
00301 void KXSConfigDialog::slotOk()
00302 {
00303   KConfig config(mConfigFile);
00304 
00305   for (int i = 0; i < mConfigItemList.size(); i++)
00306   {
00307     if (mConfigItemList[i]) {
00308       mConfigItemList[i]->save(config);
00309     }
00310   }
00311 
00312   kapp->quit();
00313 }
00314 
00315 //---------------------------------------------------------------------------
00316 void KXSConfigDialog::slotCancel()
00317 {
00318   kapp->quit();
00319 }
00320 
00321 
00322 //===========================================================================
00323 
00324 static const char appName[] = "kxsconfig";
00325 
00326 static const char description[] = I18N_NOOP("KDE X Screen Saver Configuration tool");
00327 
00328 static const char version[] = "3.0.0";
00329 
00330 static const char *defaults[] = {
00331 #include "XScreenSaver_ad.h"
00332  0
00333 };
00334 
00335 const char *progname = 0;
00336 const char *progclass = "XScreenSaver";
00337 XrmDatabase db;
00338 
00339 int main(int argc, char *argv[])
00340 {
00341   KCmdLineArgs::init(argc, argv, appName, 0, ki18n("KXSConfig"), version, ki18n(description));
00342 
00343 
00344   KCmdLineOptions options;
00345 
00346   options.add("+screensaver", ki18n("Filename of the screen saver to configure"));
00347 
00348   options.add("+[savername]", ki18n("Optional screen saver name used in messages"));
00349 
00350   KCmdLineArgs::addCmdLineOptions(options);
00351 
00352   KApplication app;
00353 
00354   KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00355   if(args->count()==0)
00356     exit(1);
00357 
00358   /* We must read exactly the same resources as xscreensaver.
00359      That means we must have both the same progclass *and* progname,
00360      at least as far as the resource database is concerned.  So,
00361      put "xscreensaver" in argv[0] while initializing Xt.
00362    */
00363   const char *dummyargs[] = { "xscreensaver" };
00364   int dummyargc = 1;
00365   progname = dummyargs[0];
00366 
00367   // Teach Xt to use the Display that Qt has already opened.
00368   XtToolkitInitialize ();
00369   XtAppContext xtApp = XtCreateApplicationContext ();
00370   Display *dpy = QX11Info::display();
00371   XtAppSetFallbackResources (xtApp, const_cast<char**>(defaults));
00372   XtDisplayInitialize (xtApp, dpy, progname, progclass, 0, 0,
00373                        &dummyargc,
00374                        const_cast<char**>(dummyargs));
00375   Widget toplevel_shell = XtAppCreateShell (progname, progclass,
00376          applicationShellWidgetClass,
00377          dpy, 0, 0);
00378   dpy = XtDisplay (toplevel_shell);
00379   db = XtDatabase (dpy);
00380   XtGetApplicationNameAndClass (dpy, const_cast<char**>(&progname),
00381                                 const_cast<char**>(&progclass));
00382 
00383   QString name = args->arg(args->count() - 1);
00384   KXSConfigDialog *dialog=new KXSConfigDialog(args->arg(0), name);
00385   if ( dialog->create() ) {
00386       dialog->show();
00387       app.exec();
00388   } else {
00389       KMessageBox::sorry(0,
00390              i18n("No configuration available for %1", name),
00391              name );
00392   }
00393 
00394   delete dialog;
00395 }
00396 
00397 #include "kxsconfig.moc"