Back to index

kdeartwork  4.3.2
blob.cpp
Go to the documentation of this file.
00001 //-----------------------------------------------------------------------------
00002 //
00003 // kblob - Basic screen saver for KDE
00004 //
00005 // Copyright (c)  Tiaan Wessels, 1997
00006 //
00007 // To add new alg :
00008 //            - add blob_alg enum in blob.h before ALG_LAST
00009 //            - choose 2 letter prefix for alg and add vars needed to private vars
00010 //              in KBlobSaver in blob.h
00011 //            - add xxSetup and xxNextFrame method definitions in blob.h
00012 //            - implement methods in this file. xxSetup to init vars mentioned
00013 //              in step 2. xxNextFrame to advance blob painter ( calc tx,ty and
00014 //              use box() method to position painter
00015 //            - add descriptive string in alg_str array in this file before "Random"
00016 //            - add to Algs array in KBlobSaver constructor in this file
00017 //            - test by setup saver and choosing alg from list
00018 
00019 #include <stdlib.h>
00020 #include <time.h>
00021 #include <limits.h>
00022 #include <math.h>
00023 
00024 #include <qcolor.h>
00025 #include <qcolormap.h>
00026 #include <qlabel.h>
00027 #include <QListWidget>
00028 #include <qlayout.h>
00029 #include <qpainter.h>
00030 #include <qpixmap.h>
00031 #include <qimage.h>
00032 #include <QVBoxLayout>
00033 #include <QHBoxLayout>
00034 
00035 #include <kapplication.h>
00036 #include <kconfig.h>
00037 #include <kmessagebox.h>
00038 #include <knuminput.h>
00039 #include <klocale.h>
00040 #include <kglobal.h>
00041 #include <krandomsequence.h>
00042 
00043 #include "blob.moc"
00044 #include "blob.h"
00045 
00046 #define SMALLRAND(a) (int)(rnd->getLong(a)+1)
00047 
00048 
00049 // libkscreensaver interface
00050 class KBlobSaverInterface : public KScreenSaverInterface
00051 {
00052 
00053 public:
00054     virtual KAboutData* aboutData() {
00055         return new KAboutData( "kblob.kss", "klock", ki18n( "KBlob" ), "2.2.0", ki18n( "KBlob" ) );
00056     }
00057 
00058 
00059     virtual KScreenSaver* create( WId id )
00060     {
00061         return new KBlobSaver( id );
00062     }
00063 
00064     virtual QDialog* setup()
00065     {
00066         return new KBlobSetup();
00067     }
00068 };
00069 
00070 int main( int argc, char *argv[] )
00071 {
00072     KBlobSaverInterface kss;
00073     return kScreenSaverMain( argc, argv, kss );
00074 }
00075 
00076 static KRandomSequence *rnd = 0;
00077 
00078 QString alg_str[5];
00079 void initAlg()
00080 {
00081     alg_str[0] = i18n("Random Linear");
00082     alg_str[1] = i18n("Horizontal Sine");
00083     alg_str[2] = i18n("Circular Bounce");
00084     alg_str[3] = i18n("Polar Coordinates");
00085     alg_str[4] = i18n("Random");
00086 }
00087 
00088 //-----------------------------------------------------------------------------
00089 // the blob screensaver's code
00090 
00091 KBlobSaver::KBlobSaver ( WId id)
00092     : KScreenSaver( id )
00093 {
00094     setAttribute( Qt::WA_OpaquePaintEvent, true );
00095 
00096     {
00097         QPalette palette;
00098         palette.setColor( backgroundRole(), Qt::black );
00099         setPalette( palette );
00100        setAutoFillBackground(true);
00101     }
00102 
00103     rnd = new KRandomSequence();
00104     initAlg();
00105     QColor color;
00106     float ramp = (256.0-64.0)/(float)RAMP;
00107 
00108     // if 8-bit, create lookup table for color ramping further down
00109     if (QPixmap::defaultDepth() == 8)
00110     {
00111         QColormap cmap = QColormap::instance();
00112         memset(lookup, 0, 256*sizeof(uint));
00113         int i;
00114         for (i = 0; i < RAMP; i++)
00115         {
00116             color.setRgb(64+(int)(ramp*(float)i), 0, 0);
00117             colors[i] = cmap.pixel(color);
00118         }
00119         memset(lookup, cmap.pixel(Qt::black), sizeof(uint)*256);
00120         for (i = 0; i < RAMP-1; i++)
00121             lookup[colors[i]] = colors[i+1];
00122         lookup[cmap.pixel(Qt::black)] = lookup[colors[RAMP-1]] = colors[0];
00123     }
00124     else
00125     {
00126         // make special provision for preview mode
00127         if (height() < 400)
00128         {
00129             if (QPixmap::defaultDepth() > 8 )
00130                 setColorInc(7);
00131             else
00132                 setColorInc(4);
00133         }
00134         else
00135         {
00136             if (QPixmap::defaultDepth() > 8 )
00137                 setColorInc(3);
00138             else
00139                 setColorInc(2);
00140         }
00141     }
00142 
00143     // the dimensions of the blob painter
00144     dim = height()/70+1;
00145 
00146     // record starting time to know when to change frames
00147     start = time(NULL);
00148 
00149     // init some parameters used by all algorithms
00150     xhalf = width()/2;
00151     yhalf = height()/2;
00152 
00153     // means a new algorithm should be set at entrance of timer
00154     newalg = newalgp = 1;
00155 
00156     // init algorithm space
00157     Algs[0].Name = alg_str[0];
00158     Algs[0].Init = &KBlobSaver::lnSetup;
00159     Algs[0].NextFrame = &KBlobSaver::lnNextFrame;
00160 
00161     Algs[1].Name = alg_str[1];
00162     Algs[1].Init = &KBlobSaver::hsSetup;
00163     Algs[1].NextFrame = &KBlobSaver::hsNextFrame;
00164 
00165     Algs[2].Name = alg_str[2];
00166     Algs[2].Init = &KBlobSaver::cbSetup;
00167     Algs[2].NextFrame = &KBlobSaver::cbNextFrame;
00168 
00169     Algs[3].Name = alg_str[3];
00170     Algs[3].Init = &KBlobSaver::pcSetup;
00171     Algs[3].NextFrame = &KBlobSaver::pcNextFrame;
00172 
00173     // get setup from kde registry
00174     readSettings();
00175 
00176     // start timer which will update blob painter
00177     timer.start(SPEED);
00178     connect(&timer, SIGNAL(timeout()), SLOT(update()));
00179     show();
00180 }
00181 
00182 KBlobSaver::~KBlobSaver()
00183 {
00184     timer.stop();
00185 
00186     delete rnd; rnd = 0;
00187 }
00188 
00189 void KBlobSaver::setAlgorithm(int a )
00190 {
00191     newalg = newalgp = ((a == ALG_RANDOM) ? 1 : 2);
00192     alg = a;
00193 }
00194 
00195 void KBlobSaver::lnSetup()
00196 {
00197     // initialize the blob movement dictators with random vals
00198     // incrementals on axis
00199     ln_xinc = SMALLRAND(3);
00200     ln_yinc = SMALLRAND(2);
00201 
00202     // start position
00203     tx = SMALLRAND(width()-dim-ln_xinc*2);
00204     ty = SMALLRAND(height()-dim-ln_yinc*2);
00205 }
00206 
00207 void KBlobSaver::hsSetup()
00208 {
00209     hs_per = SMALLRAND(7);
00210     hs_radians = 0.0;
00211     hs_rinc = (hs_per*M_PI)/(hs_per*90*4);
00212     hs_flip = 1.0;
00213 }
00214 
00215 void KBlobSaver::cbSetup()
00216 {
00217     cb_radians = 0.0;
00218     cb_rinc = (2.0*M_PI)/360.0;
00219     cb_sradians = 0.0;
00220     cb_deviate = SMALLRAND(height()/20)+(height()/15);
00221     cb_radius = height()/2-cb_deviate*2-2*dim;
00222     cb_devradinc = (rnd->getDouble()*10.0*2.0*M_PI)/360.0;
00223 }
00224 
00225 void KBlobSaver::pcSetup()
00226 {
00227     pc_angle = 0.0;
00228     pc_radius = 0.0;
00229     pc_inc = (2.0*M_PI)/720.0;
00230     pc_crot = 0.0;
00231     pc_div = SMALLRAND(4)-1;
00232 }
00233 
00234 // render next frame ( or change algorithms )
00235 void KBlobSaver::paintEvent(QPaintEvent *)
00236 {
00237     time_t now = time(NULL);
00238 
00239     // should algorithm be changed
00240     if (now-start > showlen)
00241         newalg = newalgp;
00242 
00243     // set new algorithm
00244     if (newalg)
00245     {
00246         QPainter p(this);
00247         p.fillRect(rect(), p.background());
00248         if (newalg == 1)
00249             alg = SMALLRAND(ALG_LAST)-1;
00250         (this->*Algs[alg].Init)();
00251         newalg = 0;
00252         start = time(NULL);
00253     }
00254 
00255     // gen next fram for current algorithm
00256     (this->*Algs[alg].NextFrame)();
00257 }
00258 
00259 void KBlobSaver::lnNextFrame()
00260 {
00261     int dir;
00262 
00263     // depending on the algorithm to use, move the blob painter to
00264     // a new location
00265     // check for wall hit to change direction
00266     if (tx+dim+ln_xinc > (int)width()-1 || tx+ln_xinc < 0)
00267     {
00268         if (ln_xinc > 0)
00269             dir = -1;
00270         else
00271             dir = 1;
00272         ln_xinc = SMALLRAND(3)*dir;
00273     }
00274     if (ty+dim+ln_yinc > (int)height()-1 || ty+ln_yinc < 0)
00275     {
00276         if (ln_yinc > 0)
00277             dir = -1;
00278         else
00279             dir = 1;
00280         ln_yinc = SMALLRAND(2)*dir;
00281     }
00282 
00283     // move box to new position
00284     tx += ln_xinc;
00285     ty += ln_yinc;
00286 
00287     // draw new box
00288     box(tx, ty);
00289 }
00290 
00291 void KBlobSaver::hsNextFrame()
00292 {
00293     static int xlen = width()-(4*dim);
00294     static int ylen = height()-(4*dim);
00295 
00296     // calc x as offset on angle line and y as vertical offset
00297     // on interval -1..1 sine of angle
00298     tx = (int)((hs_radians/(hs_per*M_PI))*(float)xlen);
00299     ty = (int)((float)(ylen/4)*(hs_flip*sin(hs_radians)))+yhalf;
00300 
00301     // draw new box
00302     box(tx, ty);
00303 
00304     // set new radians
00305     hs_radians += hs_rinc;
00306     if (hs_radians > hs_per*M_PI)
00307     {
00308         hs_rinc *= -1.0;
00309         hs_radians += hs_rinc;
00310         hs_flip *= -1.0;
00311     }
00312     else if (hs_radians < 0.0)
00313         hsSetup();
00314 }
00315 
00316 void KBlobSaver::cbNextFrame()
00317 {
00318     int deviate;
00319 
00320     // calculate deviation of circle main radius
00321     deviate = (int)(sin(cb_sradians)*cb_deviate);
00322 
00323     // calculate topleft of box as a circle with a sine perturbed radius
00324     tx = (int)(cos(cb_radians)*(cb_radius+deviate))+xhalf;
00325     ty = (int)(sin(cb_radians)*(cb_radius+deviate))+yhalf;
00326 
00327     // draw the box
00328     box(tx, ty);
00329 
00330     // increase greater circle render angle
00331     cb_radians += cb_rinc;
00332     if (cb_radians > 2.0*M_PI)
00333         cb_radians -= 2.0*M_PI;
00334 
00335     // increase radius deviation offset on sine wave
00336     cb_sradians += cb_devradinc;
00337 }
00338 
00339 void KBlobSaver::pcNextFrame()
00340 {
00341     static float scale = (float)height()/3.0 - 4.0*dim;
00342 
00343     // simple polar coordinate equation
00344     if (pc_div < 1.0)
00345         pc_radius = cos(2.0*pc_angle);
00346     else
00347         pc_radius = 1.0/pc_div + cos(2.0*pc_angle);
00348 
00349     tx = (int)(scale*pc_radius*cos(pc_angle+pc_crot))+xhalf;
00350     ty = (int)(scale*pc_radius*sin(pc_angle+pc_crot))+yhalf;
00351 
00352     // advance blob painter
00353     box(tx, ty);
00354 
00355     // new movement parameters
00356     pc_angle += pc_inc;
00357     if (pc_angle > 2.0*M_PI)
00358     {
00359         pc_angle -= 2.0*M_PI;
00360         pc_crot += M_PI/45.0;
00361     }
00362 }
00363 
00364 void KBlobSaver::box ( int x, int y )
00365 {
00366     // for bad behaving algorithms that wants to cause an X trap
00367     // confine to the valid region before using potentially fatal XGetImage
00368     if ((x+dim) >= width())
00369         x = width()-dim-1;
00370     else if (x < 0)
00371         x = 0;
00372     if ((y+dim) > height())
00373         y = height()-dim-1;
00374     else if (y < 0)
00375         y = 0;
00376 
00377     // get the box region from the display to upgrade
00378     QImage img = QPixmap::grabWindow(winId(), x, y, dim, dim).toImage();
00379 
00380     // depending on the depth of the display, use either lookup table for
00381     // next rgb val ( 8-bit ) or ramp the color directly for other displays
00382     if ( img.depth() == 8)
00383     {
00384         // manipulate image by upgrading each pixel with 1 using a lookup
00385         // table as the color allocation could have resulted in a spread out
00386         // configuration of the color ramp
00387         for (int j = 0; j < img.height(); j++)
00388         {
00389             for (int i = 0; i < img.width(); i++)
00390             {
00391                 img.scanLine(j)[i] = lookup[img.scanLine(j)[i]];
00392             }
00393         }
00394     }
00395     else
00396     {
00397         for (int j = 0; j < img.height(); j++)
00398         {
00399             for (int i = 0; i < img.width(); i++)
00400             {
00401                 QRgb p = img.pixel( i, j );
00402                 p += (colorInc<<18);
00403                 img.setPixel( i, j, p );
00404             }
00405         }
00406     }
00407 
00408     // put the image back onto the screen
00409     QPainter p(this);
00410     p.drawImage( x, y, img );
00411 }
00412 
00413 void KBlobSaver::readSettings()
00414 {
00415     KConfigGroup config(KGlobal::config(), "Settings");
00416 
00417     // number of seconds to spend on a frame
00418     showlen = config.readEntry("Showtime", 3*60);
00419 
00420     // algorithm to use. if not set then use random
00421     alg = config.readEntry("Algorithm", int(ALG_RANDOM));
00422     if (alg == ALG_RANDOM)
00423        newalg = 1;
00424     else
00425        newalg = 2;
00426     newalgp = newalg;
00427 }
00428 
00429 //-----------------------------------------------------------------------------
00430 // dialog to setup screen saver parameters
00431 //
00432 KBlobSetup::KBlobSetup(QWidget *parent)
00433     : KDialog( parent)
00434 {
00435     setCaption(i18n( "Setup Blob Screen Saver" ));
00436     setButtons(Ok|Cancel|Help);
00437     setDefaultButton(Ok);
00438     setModal(true);
00439     initAlg();
00440 
00441     // get saver configuration from kde registry
00442     readSettings();
00443 
00444     setButtonText( Help, i18n( "A&bout" ) );
00445     QWidget *main = new QWidget(this);
00446     setMainWidget(main);
00447 
00448     QHBoxLayout *tl = new QHBoxLayout( main );
00449     //tl->setContentsMargins(0, 0, 0, 0);
00450     tl->setSpacing(spacingHint());
00451 
00452     QVBoxLayout *vbox = new QVBoxLayout;
00453     tl->addLayout(vbox);
00454 
00455     // seconds to generate on a frame
00456     QLabel *label = new QLabel(i18n("Frame duration:"), main);
00457     stime = new KIntNumInput( showtime, main );
00458     stime->setSuffix( i18n( " sec" ) );
00459     vbox->addWidget(label);
00460     vbox->addWidget(stime);
00461 
00462     // available algorithms
00463     label = new QLabel(i18n("Algorithm:"), main);
00464     algs = new QListWidget(main);
00465     algs->setMinimumSize(150, 105);
00466     for (int i = 0; i <= ALG_RANDOM; i++)
00467         algs->addItem(alg_str[i]);
00468     algs->setCurrentRow(alg);
00469     vbox->addWidget(label);
00470     vbox->addWidget(algs);
00471 
00472     // preview window
00473     QWidget *preview = new QWidget( main );
00474     preview->setFixedSize(220, 170);
00475     {
00476         QPalette palette;
00477         palette.setColor( preview->backgroundRole(), Qt::black );
00478         preview->setPalette( palette );
00479     }
00480     preview->show();
00481     tl->addWidget(preview);
00482     saver = new KBlobSaver(preview->winId());
00483     saver->setDimension(3);
00484     if (QPixmap::defaultDepth() > 8)
00485         saver->setColorInc(7);
00486     else
00487         saver->setColorInc(4);
00488 
00489     tl->addStretch();
00490 
00491     // so selecting an algorithm will start previewing that alg
00492     connect(algs, SIGNAL(itemChanged(QListWidgetItem*)), this,
00493             SLOT(setAlgorithm(QListWidgetItem*)));
00494     connect(this,SIGNAL(okClicked()),this,SLOT(slotOk()));
00495     connect(this,SIGNAL(helpClicked()),this, SLOT(slotHelp()));
00496 }
00497 
00498 void KBlobSetup::setAlgorithm(QListWidgetItem* item)
00499 {
00500    if(!item)
00501      return;
00502    int pos = algs->currentRow();
00503    saver->setAlgorithm(pos);
00504 }
00505 
00506 void KBlobSetup::readSettings()
00507 {
00508     KConfigGroup config(KGlobal::config(), "Settings");
00509 
00510     // number of seconds to spend on a frame
00511     showtime = config.readEntry("Showtime", 3*60);
00512 
00513     // algorithm to use. if not set then use random
00514     alg = config.readEntry("Algorithm", (int)ALG_LAST);
00515 }
00516 
00517 // Ok pressed - save settings and exit
00518 void KBlobSetup::slotOk()
00519 {
00520     KConfigGroup config( KGlobal::config(), "Settings");
00521     config.writeEntry("Showtime", stime->value());
00522     config.writeEntry("Algorithm", algs->currentRow());
00523 
00524     config.sync();
00525 
00526     accept();
00527 }
00528 
00529 void KBlobSetup::slotHelp()
00530 {
00531     KMessageBox::about(this,
00532                        i18n("Blobsaver Version 0.1\n\nWritten by Tiaan Wessels 1997\ntiaan@netsys.co.za"));
00533     if (saver)
00534         setAlgorithm(algs->currentItem());
00535 }
00536 
00537