You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeartwork/kscreensaver/kdesavers/blob.cpp

528 lines
12 KiB

//-----------------------------------------------------------------------------
//
// kblob - Basic screen saver for KDE
//
// Copyright (c) Tiaan Wessels, 1997
//
// To add new alg :
// - add blob_alg enum in blob.h before ALG_LAST
// - choose 2 letter prefix for alg and add vars needed to private vars
// in KBlobSaver in blob.h
// - add xxSetup and xxNextFrame method definitions in blob.h
// - implement methods in this file. xxSetup to init vars mentioned
// in step 2. xxNextFrame to advance blob painter ( calc tx,ty and
// use box() method to position painter
// - add descriptive string in alg_str array in this file before "Random"
// - add to Algs array in KBlobSaver constructor in this file
// - test by setup saver and choosing alg from list
#include <config.h>
#include <stdlib.h>
#include <time.h>
#include <limits.h>
#include <math.h>
#include <tqcolor.h>
#include <tqlabel.h>
#include <tqlistbox.h>
#include <tqlayout.h>
#include <tqpainter.h>
#include <tqpixmap.h>
#include <tqimage.h>
#include <kapplication.h>
#include <kconfig.h>
#include <kmessagebox.h>
#include <knuminput.h>
#include <klocale.h>
#include <kglobal.h>
#include <krandomsequence.h>
#include "blob.moc"
#include "blob.h"
#define SMALLRAND(a) (int)(rnd->getLong(a)+1)
// libkscreensaver interface
extern "C"
{
KDE_EXPORT const char *kss_applicationName = "kblob.kss";
KDE_EXPORT const char *kss_description = I18N_NOOP( "KBlob" );
KDE_EXPORT const char *kss_version = "2.2.0";
KDE_EXPORT KScreenSaver *kss_create( WId id )
{
return new KBlobSaver( id );
}
KDE_EXPORT TQDialog *kss_setup()
{
return new KBlobSetup();
}
}
static KRandomSequence *rnd = 0;
TQString alg_str[5];
void initAlg()
{
alg_str[0] = i18n("Random Linear");
alg_str[1] = i18n("Horizontal Sine");
alg_str[2] = i18n("Circular Bounce");
alg_str[3] = i18n("Polar Coordinates");
alg_str[4] = i18n("Random");
}
//-----------------------------------------------------------------------------
// the blob screensaver's code
KBlobSaver::KBlobSaver ( WId id)
: KScreenSaver( id )
{
rnd = new KRandomSequence();
initAlg();
TQColor color;
float ramp = (256.0-64.0)/(float)RAMP;
TQString msg =
i18n("This screen saver requires a color display.");
blank();
// needs colors to work this one
if (TQPixmap::defaultDepth() < 8)
{
TQPainter p(this);
p.setPen( white );
p.drawText( width()/2, height()/2, msg );
return;
}
colorContext = TQColor::enterAllocContext();
// if 8-bit, create lookup table for color ramping further down
if (TQPixmap::defaultDepth() == 8)
{
memset(lookup, 0, 256*sizeof(uint));
int i;
for (i = 0; i < RAMP; i++)
{
color.setRgb(64+(int)(ramp*(float)i), 0, 0);
colors[i] = color.alloc();
}
memset(lookup, black.pixel(), sizeof(uint)*256);
for (i = 0; i < RAMP-1; i++)
lookup[colors[i]] = colors[i+1];
lookup[black.pixel()] = lookup[colors[RAMP-1]] = colors[0];
}
else
{
// make special provision for preview mode
if (height() < 400)
{
if (TQPixmap::defaultDepth() > 8 )
setColorInc(7);
else
setColorInc(4);
}
else
{
if (TQPixmap::defaultDepth() > 8 )
setColorInc(3);
else
setColorInc(2);
}
}
// the dimensions of the blob painter
dim = height()/70+1;
// record starting time to know when to change frames
start = time(NULL);
// init some parameters used by all algorithms
xhalf = width()/2;
yhalf = height()/2;
// means a new algorithm should be set at entrance of timer
newalg = newalgp = 1;
// init algorithm space
Algs[0].Name = alg_str[0];
Algs[0].Init = &KBlobSaver::lnSetup;
Algs[0].NextFrame = &KBlobSaver::lnNextFrame;
Algs[1].Name = alg_str[1];
Algs[1].Init = &KBlobSaver::hsSetup;
Algs[1].NextFrame = &KBlobSaver::hsNextFrame;
Algs[2].Name = alg_str[2];
Algs[2].Init = &KBlobSaver::cbSetup;
Algs[2].NextFrame = &KBlobSaver::cbNextFrame;
Algs[3].Name = alg_str[3];
Algs[3].Init = &KBlobSaver::pcSetup;
Algs[3].NextFrame = &KBlobSaver::pcNextFrame;
// get setup from kde registry
readSettings();
// start timer which will update blob painter
timer.start(SPEED);
connect(&timer, TQT_SIGNAL(timeout()), TQT_SLOT(slotTimeout()));
}
KBlobSaver::~KBlobSaver()
{
timer.stop();
TQColor::leaveAllocContext();
TQColor::destroyAllocContext(colorContext);
delete rnd; rnd = 0;
}
void KBlobSaver::setAlgorithm(int a)
{
newalg = newalgp = ((a == ALG_RANDOM) ? 1 : 2);
alg = a;
}
void KBlobSaver::lnSetup()
{
// initialize the blob movement dictators with random vals
// incrementals on axis
ln_xinc = SMALLRAND(3);
ln_yinc = SMALLRAND(2);
// start position
tx = SMALLRAND(width()-dim-ln_xinc*2);
ty = SMALLRAND(height()-dim-ln_yinc*2);
}
void KBlobSaver::hsSetup()
{
hs_per = SMALLRAND(7);
hs_radians = 0.0;
hs_rinc = (hs_per*M_PI)/(hs_per*90*4);
hs_flip = 1.0;
}
void KBlobSaver::cbSetup()
{
cb_radians = 0.0;
cb_rinc = (2.0*M_PI)/360.0;
cb_sradians = 0.0;
cb_deviate = SMALLRAND(height()/20)+(height()/15);
cb_radius = height()/2-cb_deviate*2-2*dim;
cb_devradinc = (rnd->getDouble()*10.0*2.0*M_PI)/360.0;
}
void KBlobSaver::pcSetup()
{
pc_angle = 0.0;
pc_radius = 0.0;
pc_inc = (2.0*M_PI)/720.0;
pc_crot = 0.0;
pc_div = SMALLRAND(4)-1;
}
// render next frame ( or change algorithms )
void KBlobSaver::slotTimeout()
{
time_t now = time(NULL);
// should algorithm be changed
if (now-start > showlen)
newalg = newalgp;
// set new algorithm
if (newalg)
{
blank();
if (newalg == 1)
alg = SMALLRAND(ALG_LAST)-1;
(this->*Algs[alg].Init)();
newalg = 0;
start = time(NULL);
}
// gen next fram for current algorithm
(this->*Algs[alg].NextFrame)();
}
void KBlobSaver::lnNextFrame()
{
int dir;
// depending on the algorithm to use, move the blob painter to
// a new location
// check for wall hit to change direction
if (tx+dim+ln_xinc > (int)width()-1 || tx+ln_xinc < 0)
{
if (ln_xinc > 0)
dir = -1;
else
dir = 1;
ln_xinc = SMALLRAND(3)*dir;
}
if (ty+dim+ln_yinc > (int)height()-1 || ty+ln_yinc < 0)
{
if (ln_yinc > 0)
dir = -1;
else
dir = 1;
ln_yinc = SMALLRAND(2)*dir;
}
// move box to new position
tx += ln_xinc;
ty += ln_yinc;
// draw new box
box(tx, ty);
}
void KBlobSaver::hsNextFrame()
{
static int xlen = width()-(4*dim);
static int ylen = height()-(4*dim);
// calc x as offset on angle line and y as vertical offset
// on interval -1..1 sine of angle
tx = (int)((hs_radians/(hs_per*M_PI))*(float)xlen);
ty = (int)((float)(ylen/4)*(hs_flip*sin(hs_radians)))+yhalf;
// draw new box
box(tx, ty);
// set new radians
hs_radians += hs_rinc;
if (hs_radians > hs_per*M_PI)
{
hs_rinc *= -1.0;
hs_radians += hs_rinc;
hs_flip *= -1.0;
}
else if (hs_radians < 0.0)
hsSetup();
}
void KBlobSaver::cbNextFrame()
{
int deviate;
// calculate deviation of circle main radius
deviate = (int)(sin(cb_sradians)*cb_deviate);
// calculate topleft of box as a circle with a sine perturbed radius
tx = (int)(cos(cb_radians)*(cb_radius+deviate))+xhalf;
ty = (int)(sin(cb_radians)*(cb_radius+deviate))+yhalf;
// draw the box
box(tx, ty);
// increase greater circle render angle
cb_radians += cb_rinc;
if (cb_radians > 2.0*M_PI)
cb_radians -= 2.0*M_PI;
// increase radius deviation offset on sine wave
cb_sradians += cb_devradinc;
}
void KBlobSaver::pcNextFrame()
{
static float scale = (float)height()/3.0 - 4.0*dim;
// simple polar coordinate equation
if (pc_div < 1.0)
pc_radius = cos(2.0*pc_angle);
else
pc_radius = 1.0/pc_div + cos(2.0*pc_angle);
tx = (int)(scale*pc_radius*cos(pc_angle+pc_crot))+xhalf;
ty = (int)(scale*pc_radius*sin(pc_angle+pc_crot))+yhalf;
// advance blob painter
box(tx, ty);
// new movement parameters
pc_angle += pc_inc;
if (pc_angle > 2.0*M_PI)
{
pc_angle -= 2.0*M_PI;
pc_crot += M_PI/45.0;
}
}
void KBlobSaver::box ( int x, int y )
{
// for bad behaving algorithms that wants to cause an X trap
// confine to the valid region before using potentially fatal XGetImage
if ((x+dim) >= width())
x = width()-dim-1;
else if (x < 0)
x = 0;
if ((y+dim) > height())
y = height()-dim-1;
else if (y < 0)
y = 0;
// get the box region from the display to upgrade
TQImage img = TQPixmap(TQPixmap::grabWindow(winId(), x, y, dim, dim)).convertToImage();
// depending on the depth of the display, use either lookup table for
// next rgb val ( 8-bit ) or ramp the color directly for other displays
if ( img.depth() == 8)
{
// manipulate image by upgrading each pixel with 1 using a lookup
// table as the color allocation could have resulted in a spread out
// configuration of the color ramp
for (int j = 0; j < img.height(); j++)
{
for (int i = 0; i < img.width(); i++)
{
img.scanLine(j)[i] = lookup[img.scanLine(j)[i]];
}
}
}
else
{
for (int j = 0; j < img.height(); j++)
{
for (int i = 0; i < img.width(); i++)
{
TQRgb p = img.pixel( i, j );
p += (colorInc<<18);
img.setPixel( i, j, p );
}
}
}
// put the image back onto the screen
TQPainter p(this);
p.drawImage( x, y, img );
}
void KBlobSaver::blank()
{
setBackgroundColor( black );
erase();
}
void KBlobSaver::readSettings()
{
KConfig *config = KGlobal::config();
config->setGroup("Settings");
// number of seconds to spend on a frame
showlen = config->readNumEntry("Showtime", 3*60);
// algorithm to use. if not set then use random
alg = config->readNumEntry("Algorithm", ALG_RANDOM);
if (alg == ALG_RANDOM)
newalg = 1;
else
newalg = 2;
newalgp = newalg;
}
//-----------------------------------------------------------------------------
// dialog to setup screen saver parameters
//
KBlobSetup::KBlobSetup
(
TQWidget *parent,
const char *name
)
: KDialogBase( parent, name, true, i18n( "Setup Blob Screen Saver" ),
Ok|Cancel|Help, Ok, true )
{
initAlg();
// get saver configuration from kde registry
readSettings();
setButtonText( Help, i18n( "A&bout" ) );
TQWidget *main = makeMainWidget();
TQHBoxLayout *tl = new TQHBoxLayout( main, 0, spacingHint() );
TQVBoxLayout *vbox = new TQVBoxLayout;
tl->addLayout(vbox);
// seconds to generate on a frame
TQLabel *label = new TQLabel(i18n("Frame duration:"), main);
stime = new KIntNumInput( showtime, main );
stime->setSuffix( i18n( " sec" ) );
vbox->addWidget(label);
vbox->addWidget(stime);
// available algorithms
label = new TQLabel(i18n("Algorithm:"), main);
algs = new TQListBox(main);
algs->setMinimumSize(150, 105);
for (int i = 0; i <= ALG_RANDOM; i++)
algs->insertItem(alg_str[i]);
algs->setCurrentItem(alg);
vbox->addWidget(label);
vbox->addWidget(algs);
// preview window
TQWidget *preview = new TQWidget( main );
preview->setFixedSize(220, 170);
preview->setBackgroundColor(black);
preview->show();
tl->addWidget(preview);
saver = new KBlobSaver(preview->winId());
saver->setDimension(3);
if (TQPixmap::defaultDepth() > 8)
saver->setColorInc(7);
else
saver->setColorInc(4);
tl->addStretch();
// so selecting an algorithm will start previewing that alg
connect(algs, TQT_SIGNAL(highlighted(int)), saver,
TQT_SLOT(setAlgorithm(int)));
}
void KBlobSetup::readSettings()
{
KConfig *config = KGlobal::config();
config->setGroup("Settings");
// number of seconds to spend on a frame
showtime = config->readNumEntry("Showtime", 3*60);
// algorithm to use. if not set then use random
alg = config->readNumEntry("Algorithm", ALG_LAST);
}
// Ok pressed - save settings and exit
void KBlobSetup::slotOk()
{
KConfig *config = KGlobal::config();
config->setGroup("Settings");
config->writeEntry("Showtime", stime->value());
config->writeEntry("Algorithm", algs->currentItem());
config->sync();
accept();
}
void KBlobSetup::slotHelp()
{
KMessageBox::about(this,
i18n("Blobsaver Version 0.1\n\nWritten by Tiaan Wessels 1997\ntiaan@netsys.co.za"));
if (saver)
saver->setAlgorithm(algs->currentItem());
}