not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kcontrol / kdm / background / bgrender.cpp
blob33329a1f11d4361677a85a338cbf3eb8991bf92c
1 /* vi: ts=8 sts=4 sw=4
2 * kate: space-indent on; tab-width 8; indent-width 4; indent-mode cstyle;
4 * This file is part of the KDE project, module kdesktop.
5 * Copyright (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl>
7 * You can Freely distribute this program under the GNU Library General
8 * Public License. See the file "COPYING.LIB" for the exact licensing terms.
9 */
10 #include "bgrender.h"
12 #include <fixx11h.h>
13 #include <config-workspace.h>
15 #include <time.h>
16 #include <stdlib.h>
17 #include <utime.h>
19 #include <QTimer>
20 #include <QPainter>
21 #include <QImage>
22 #include <QFileInfo>
23 #include <QDir>
24 #include <QDesktopWidget>
25 #include <QPaintEngine>
26 #include <QHash>
27 #include <QDateTime>
28 #include <QPixmap>
30 #include <kapplication.h>
31 #include <kconfig.h>
32 #include <kdebug.h>
33 #include <kstandarddirs.h>
34 #include <qimageblitz.h>
35 #include <ktemporaryfile.h>
36 #include <kcursor.h>
37 #include <kfilemetainfo.h>
38 #include <kconfig.h>
39 #include <kconfiggroup.h>
40 #include <ksvgrenderer.h>
41 #include <kmacroexpander.h>
43 #include "bgdefaults.h"
45 #include <X11/Xlib.h>
47 #include <QX11Info>
49 /**** KBackgroundRenderer ****/
52 KBackgroundRenderer::KBackgroundRenderer(int screen, bool drawBackgroundPerScreen, const KSharedConfigPtr &config)
53 : KBackgroundSettings(screen, drawBackgroundPerScreen, config)
55 m_State = 0;
56 m_isBusyCursor = false;
57 m_enableBusyCursor = false;
58 m_pDirs = KGlobal::dirs();
59 m_rSize = m_Size = drawBackgroundPerScreen ?
60 QApplication::desktop()->screenGeometry(screen).size() : QApplication::desktop()->size();
61 m_pProc = 0L;
62 m_Tempfile = 0L;
63 m_bPreview = false;
64 m_Cached = false;
65 m_TilingEnabled = false;
67 m_pTimer = new QTimer(this);
68 m_pTimer->setSingleShot(true);
69 connect(m_pTimer, SIGNAL(timeout()), SLOT(render()));
73 KBackgroundRenderer::~KBackgroundRenderer()
75 cleanup();
76 delete m_Tempfile;
77 m_Tempfile = 0;
81 void KBackgroundRenderer::setSize(const QSize &size)
83 m_rSize = m_Size = size;
87 * Re-configure because the desktop has been resized.
89 void KBackgroundRenderer::desktopResized()
91 m_State = 0;
92 m_rSize = drawBackgroundPerScreen() ?
93 QApplication::desktop()->screenGeometry(screen()).size() : QApplication::desktop()->size();
94 if( !m_bPreview )
95 m_Size = m_rSize;
99 void KBackgroundRenderer::tile(QImage& dest, const QRect &_rect, const QImage& src)
101 QRect rect = _rect;
102 rect &= dest.rect();
104 int x, y;
105 int h = rect.height(), w = rect.width();
106 int offx = rect.x(), offy = rect.y();
107 int sw = src.width(), sh = src.height();
109 for (y=offy; y<offy+h; y++)
110 for (x=offx; x<offx+w; x++)
111 dest.setPixel(x, y, src.pixel(x%sw, y%sh));
116 * Build a command line to run the program.
119 QString KBackgroundRenderer::buildCommand()
121 QString cmd;
122 if (m_bPreview)
123 cmd = previewCommand();
124 else
125 cmd = command();
127 if (cmd.isEmpty())
128 return QString();
130 QHash<QChar, QString> map;
131 map.insert('f', m_Tempfile->fileName());
132 map.insert('x', QString::number(m_Size.width()));
133 map.insert('y', QString::number(m_Size.height()));
134 return KMacroExpander::expandMacrosShellQuote(cmd, map);
139 * Create a background tile. If the background mode is `Program',
140 * this is asynchronous.
142 int KBackgroundRenderer::doBackground(bool quit)
144 if (m_State & BackgroundDone)
145 return Done;
146 int bgmode = backgroundMode();
148 if (!enabled())
149 bgmode= Flat;
151 if (quit) {
152 if (bgmode == Program && m_pProc)
153 m_pProc->terminate();
154 return Done;
157 int retval = Done;
158 QString file;
160 static unsigned int tileWidth = 0;
161 static unsigned int tileHeight = 0;
162 if( tileWidth == 0 )
164 int tile_val = QPixmap::defaultDepth() >= 24 ? 1 : 2;
165 // some dithering may be needed even with bpb==15/16, so don't use tileWidth==1
166 // for them
167 // with tileWidth>2, repainting the desktop causes nasty effect (XFree86 4.1.0 )
168 if( XQueryBestTile( QX11Info::display(), QX11Info::appRootWindow(), tile_val, tile_val,
169 &tileWidth, &tileHeight ) != Success )
170 tileWidth = tileHeight = tile_val; // some defaults
172 switch (bgmode) {
174 case Flat:
175 // this can be tiled correctly without problems
176 m_Background = QImage( tileWidth, tileHeight, QImage::Format_RGB32 );
177 m_Background.fill(colorA().rgb());
178 break;
180 case Pattern:
182 if (pattern().isEmpty())
183 break;
184 file = m_pDirs->findResource("dtop_pattern", pattern());
185 if (file.isEmpty())
186 break;
188 m_Background.load(file);
189 if (m_Background.isNull())
190 break;
191 int w = m_Background.width();
192 int h = m_Background.height();
193 if ((w > m_Size.width()) || (h > m_Size.height())) {
194 w = qMin(w, m_Size.width());
195 h = qMin(h, m_Size.height());
196 m_Background = m_Background.copy(0, 0, w, h);
198 Blitz::flatten(m_Background, colorA(), colorB());
199 break;
201 case Program:
202 if (m_State & BackgroundStarted)
203 break;
204 m_State |= BackgroundStarted;
205 createTempFile();
207 file = buildCommand();
208 if (file.isEmpty())
209 break;
211 delete m_pProc;
212 m_pProc = new KProcess;
213 m_pProc->setShellCommand(file);
214 connect(m_pProc,
215 SIGNAL(finished(int, QProcess::ExitStatus)),
216 SLOT(slotBackgroundDone(int, QProcess::ExitStatus)));
217 m_pProc->start();
218 retval = Wait;
219 break;
221 case HorizontalGradient:
223 QSize size = m_Size;
224 // on <16bpp displays the gradient sucks when tiled because of dithering
225 if( canTile())
226 size.setHeight( tileHeight );
227 m_Background = Blitz::gradient(size, colorA(), colorB(),
228 Blitz::HorizontalGradient);
229 break;
231 case VerticalGradient:
233 QSize size = m_Size;
234 // on <16bpp displays the gradient sucks when tiled because of dithering
235 if( canTile())
236 size.setWidth( tileWidth );
237 m_Background = Blitz::gradient(size, colorA(), colorB(),
238 Blitz::VerticalGradient);
239 break;
241 case PyramidGradient:
242 m_Background = Blitz::gradient(m_Size, colorA(), colorB(),
243 Blitz::PyramidGradient);
244 break;
246 case PipeCrossGradient:
247 m_Background = Blitz::gradient(m_Size, colorA(), colorB(),
248 Blitz::PipeCrossGradient);
249 break;
251 case EllipticGradient:
252 m_Background = Blitz::gradient(m_Size, colorA(), colorB(),
253 Blitz::EllipticGradient);
254 break;
257 if (retval == Done)
258 m_State |= BackgroundDone;
260 return retval;
264 int KBackgroundRenderer::doWallpaper(bool quit)
266 if (m_State & WallpaperDone)
267 return Done;
269 if (quit)
270 // currently no asynch. wallpapers
271 return Done;
273 int wpmode= enabled()?wallpaperMode():NoWallpaper;
275 m_Wallpaper = QImage();
276 if (wpmode != NoWallpaper) {
277 wp_load:
278 if (currentWallpaper().isEmpty()) {
279 wpmode = NoWallpaper;
280 goto wp_out;
282 QString file = m_pDirs->findResource("wallpaper", currentWallpaper());
283 if (file.isEmpty()) {
284 wpmode = NoWallpaper;
285 goto wp_out;
288 // _Don't_ use KMimeType, as it relies on ksycoca which we really
289 // don't want in krootimage (kdm context).
290 //if ( KMimeType::findByPath( file )->is( "image/svg+xml" ) ) {
291 if (file.endsWith(".svg") || file.endsWith(".svgz")) {
293 // Special stuff for SVG icons
295 //FIXME
296 //ksvgiconloader doesn't seem to let us find out the
297 //ratio of width to height so for the most part we just
298 //assume it's a square
299 int svgWidth;
300 int svgHeight;
301 switch (wpmode)
303 case Centred:
304 case CentredAutoFit:
305 svgHeight = (int)(m_Size.height() * 0.8);
306 svgWidth = svgHeight;
307 break;
308 case Tiled:
309 case CenterTiled:
310 svgHeight = (int)(m_Size.height() * 0.5);
311 svgWidth = svgHeight;
312 break;
313 case Scaled:
314 svgHeight = m_Size.height();
315 svgWidth = m_Size.width();
316 break;
317 case CentredMaxpect:
318 case ScaleAndCrop:
319 case TiledMaxpect:
320 svgHeight = m_Size.height();
321 svgWidth = svgHeight;
322 break;
323 case NoWallpaper:
324 default:
325 kWarning() << "unknown diagram type" ;
326 svgHeight = m_Size.height();
327 svgWidth = svgHeight;
328 break;
330 //FIXME hack due to strangeness with
331 //background control modules
332 if ( svgHeight < 200 ) {
333 svgHeight *= 6;
334 svgWidth *= 6;
337 KSvgRenderer renderer(file);
338 if (renderer.isValid()) {
339 m_Wallpaper = QImage(svgWidth, svgHeight, QImage::Format_ARGB32_Premultiplied);
340 m_Wallpaper.fill(0);
341 QPainter p(&m_Wallpaper);
342 renderer.render(&p);
344 } else {
345 m_Wallpaper.load(file);
347 if (m_Wallpaper.isNull()) {
348 kWarning() << "failed to load wallpaper " << file ;
349 if (discardCurrentWallpaper())
350 goto wp_load;
351 wpmode = NoWallpaper;
352 goto wp_out;
354 m_Wallpaper = m_Wallpaper.convertToFormat(QImage::Format_ARGB32_Premultiplied, Qt::DiffuseAlphaDither);
356 // If we're previewing, scale the wallpaper down to make the preview
357 // look more like the real desktop.
358 if (m_bPreview) {
359 int xs = m_Wallpaper.width() * m_Size.width() / m_rSize.width();
360 int ys = m_Wallpaper.height() * m_Size.height() / m_rSize.height();
361 if ((xs < 1) || (ys < 1))
363 xs = ys = 1;
365 if( m_WallpaperRect.size() != QSize( xs, ys ))
366 m_Wallpaper = m_Wallpaper.scaled(xs, ys, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
369 #if 0
370 // HACK: Use KFileMetaInfo only when we have a KApplication
371 // KFileMetaInfo needs ksycoca and so on, but this code is
372 // used also in krootimage (which in turn is used by kdm).
373 if (kapp) {
374 KFileMetaInfo metaInfo(file);
375 if (metaInfo.isValid() && metaInfo.item("Orientation").isValid()) {
376 switch (metaInfo.item("Orientation").value().toInt()) {
377 case 2:
378 // Flipped horizontally
379 m_Wallpaper.mirrored(true, false);
380 break;
381 case 3:
382 // Rotated 180 degrees
383 m_Wallpaper = m_Wallpaper.transformed(QMatrix().rotate(180));
384 break;
385 case 4:
386 // Flipped vertically
387 m_Wallpaper = m_Wallpaper.mirrored(false, true);
388 break;
389 case 5:
390 // Rotated 90 degrees & flipped horizontally
391 m_Wallpaper = m_Wallpaper.transformed(QMatrix().rotate(90)).mirrored(true, false);
392 break;
393 case 6:
394 // Rotated 90 degrees
395 m_Wallpaper = m_Wallpaper.transformed(QMatrix().rotate(90));
396 break;
397 case 7:
398 // Rotated 90 degrees & flipped vertically
399 m_Wallpaper = m_Wallpaper.transformed(QMatrix().rotate(90)).mirrored(false, true);
400 break;
401 case 8:
402 // Rotated 270 degrees
403 m_Wallpaper = m_Wallpaper.transformed(QMatrix().rotate(270));
404 break;
405 case 1:
406 default:
407 // Normal or invalid orientation
408 break;
412 #endif
414 wp_out:
416 if (m_Background.isNull()) {
417 m_Background = QImage(8, 8, QImage::Format_RGB32);
418 m_Background.fill(colorA().rgb());
421 int retval = Done;
423 // desktop width/height
424 int w = m_Size.width();
425 int h = m_Size.height();
427 // wallpaper width/height
428 int ww = m_Wallpaper.width();
429 int wh = m_Wallpaper.height();
431 // to be filled destination rectangle; may exceed desktop!
432 m_WallpaperRect = QRect();
434 switch (wpmode)
436 case NoWallpaper:
437 break;
438 case Centred:
439 m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
440 break;
441 case Tiled:
442 m_WallpaperRect.setRect(0, 0, w, h);
443 break;
444 case CenterTiled:
445 m_WallpaperRect.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh, w-1, h-1);
446 break;
447 case Scaled:
448 ww = w;
449 wh = h;
450 if( m_WallpaperRect.size() != QSize( w, h ))
451 m_Wallpaper = m_Wallpaper.scaled( w, h, Qt::IgnoreAspectRatio, Qt::SmoothTransformation );
452 m_WallpaperRect.setRect(0, 0, w, h);
453 break;
454 case CentredAutoFit:
455 if( ww <= w && wh <= h ) {
456 m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centred
457 break;
459 // fall through
460 case CentredMaxpect:
462 double sx = (double) w / ww;
463 double sy = (double) h / wh;
464 if (sx > sy) {
465 ww = (int)(sy * ww);
466 wh = h;
467 } else {
468 wh = (int)(sx * wh);
469 ww = w;
471 if( m_WallpaperRect.size() != QSize( ww, wh ))
472 m_Wallpaper = m_Wallpaper.scaled(ww, wh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
473 m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
474 break;
476 case TiledMaxpect:
478 double sx = (double) w / ww;
479 double sy = (double) h / wh;
480 if (sx > sy) {
481 ww = (int)(sy * ww);
482 wh = h;
483 } else {
484 wh = (int)(sx * wh);
485 ww = w;
487 if( m_WallpaperRect.size() != QSize( ww, wh ))
488 m_Wallpaper = m_Wallpaper.scaled(ww, wh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
489 m_WallpaperRect.setRect(0, 0, w, h);
490 break;
492 case ScaleAndCrop:
494 double sx = (double) w / ww;
495 double sy = (double) h / wh;
496 if (sx > sy) {
497 //Case 1: x needs bigger scaling. Lets increase x and leave part of y offscreen
498 ww = w;
499 wh=(int)(sx * wh);
500 } else {
501 //Case 2: y needs bigger scaling. Lets increase y and leave part of x offscreen
502 wh = h;
503 ww = (int)(sy*ww);
505 if( m_WallpaperRect.size() != QSize( ww, wh ))
506 m_Wallpaper = m_Wallpaper.scaled(ww, wh, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
507 m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2,w, h);
508 break;
512 wallpaperBlend();
514 if (retval == Done)
515 m_State |= WallpaperDone;
517 return retval;
520 bool KBackgroundRenderer::canTile() const
522 return m_TilingEnabled && optimize();
525 void KBackgroundRenderer::wallpaperBlend()
527 if( !enabled() || wallpaperMode() == NoWallpaper
528 || (blendMode() == NoBlending &&
529 ( QApplication::desktop()->paintEngine()->hasFeature(QPaintEngine::Antialiasing)
530 || !m_Wallpaper.hasAlphaChannel()))) {
531 fastWallpaperBlend();
533 else {
534 fullWallpaperBlend();
538 // works only for NoBlending and no alpha in wallpaper
539 // but is much faster than QImage fidling
540 void KBackgroundRenderer::fastWallpaperBlend()
542 m_Image = QImage();
543 // copy background to m_pPixmap
544 if( !enabled() || (wallpaperMode() == NoWallpaper && canTile())) {
545 // if there's no wallpaper, no need to tile the pixmap to the size of desktop, as X does
546 // that automatically and using a smaller pixmap should save some memory
547 m_Pixmap = QPixmap::fromImage( m_Background );
548 return;
550 else if( wallpaperMode() == Tiled && !m_Wallpaper.hasAlphaChannel() && canTile() && !m_bPreview ) {
551 // tiles will be tiled by X automatically
552 m_Pixmap = QPixmap::fromImage( m_Wallpaper );
553 return;
555 else if( m_WallpaperRect.contains( QRect( QPoint( 0, 0 ), m_Size ))
556 && !m_Wallpaper.hasAlphaChannel()) // wallpaper covers all and no blending
557 m_Pixmap = QPixmap( m_Size );
558 else if (m_Background.size() == m_Size)
559 m_Pixmap = QPixmap::fromImage( m_Background );
560 else {
561 m_Pixmap = QPixmap( m_Size );
562 QPainter p( &m_Pixmap );
563 QPixmap pm = QPixmap::fromImage( m_Background );
564 p.drawTiledPixmap( 0, 0, m_Size.width(), m_Size.height(), pm );
567 // paint/alpha-blend wallpaper to destination rectangle of m_pPixmap
568 if (m_WallpaperRect.isValid()) {
569 QPixmap wp_pixmap = QPixmap::fromImage( m_Wallpaper );
570 QPainter pa( &m_Pixmap );
571 int ww = m_Wallpaper.width();
572 int wh = m_Wallpaper.height();
573 for (int y = m_WallpaperRect.top(); y < m_WallpaperRect.bottom(); y += wh) {
574 for (int x = m_WallpaperRect.left(); x < m_WallpaperRect.right(); x += ww) {
575 pa.drawPixmap( x, y, wp_pixmap );
582 void KBackgroundRenderer::fullWallpaperBlend()
584 m_Pixmap = QPixmap();
586 // desktop width/height
587 int w = m_Size.width();
588 int h = m_Size.height();
590 // copy background to m_pImage
591 if (m_Background.size() == m_Size) {
592 m_Image = m_Background.copy();
594 if (m_Image.depth() < 32) {
595 m_Image = m_Image.convertToFormat(QImage::Format_ARGB32_Premultiplied, Qt::DiffuseAlphaDither);
597 } else {
598 m_Image = QImage(w, h, QImage::Format_RGB32);
599 tile(m_Image, QRect(0, 0, w, h), m_Background);
602 // blend wallpaper to destination rectangle of m_pImage
603 if (m_WallpaperRect.isValid())
605 int blendFactor = 100;
606 if (blendMode() == FlatBlending)
607 blendFactor = (blendBalance()+200)/4;
608 int ww = m_Wallpaper.width();
609 int wh = m_Wallpaper.height();
610 for (int y = m_WallpaperRect.top(); y < m_WallpaperRect.bottom(); y += wh) {
611 for (int x = m_WallpaperRect.left(); x < m_WallpaperRect.right(); x += ww) {
612 blend(m_Image, QRect(x, y, ww, wh), m_Wallpaper,
613 QPoint(-qMin(x, 0), -qMin(y, 0)), blendFactor);
619 // blend whole desktop
620 if ( wallpaperMode() != NoWallpaper) {
621 int bal = blendBalance();
623 switch( blendMode() ) {
624 /* TODO disabled for now
625 case HorizontalBlending:
626 Blitz::blend( m_Image, m_Background,
627 Blitz::HorizontalGradient,
628 bal, 100 );
629 break;
631 case VerticalBlending:
632 Blitz::blend( m_Image, m_Background,
633 Blitz::VerticalGradient,
634 100, bal );
635 break;
637 case PyramidBlending:
638 Blitz::blend( m_Image, m_Background,
639 Blitz::PyramidGradient,
640 bal, bal );
641 break;
643 case PipeCrossBlending:
644 Blitz::blend( m_Image, m_Background,
645 Blitz::PipeCrossGradient,
646 bal, bal );
647 break;
649 case EllipticBlending:
650 Blitz::blend( m_Image, m_Background,
651 Blitz::EllipticGradient,
652 bal, bal );
653 break;
656 case IntensityBlending:
657 Blitz::modulate( m_Image, m_Background, reverseBlending(),
658 Blitz::Intensity, bal, Blitz::All );
659 break;
661 case SaturateBlending:
662 Blitz::modulate( m_Image, m_Background, reverseBlending(),
663 Blitz::Saturation, bal, Blitz::Brightness );
664 break;
666 case ContrastBlending:
667 Blitz::modulate( m_Image, m_Background, reverseBlending(),
668 Blitz::Contrast, bal, Blitz::All );
669 break;
671 case HueShiftBlending:
672 Blitz::modulate( m_Image, m_Background, reverseBlending(),
673 Blitz::HueShift, bal, Blitz::Brightness );
674 break;
676 case FlatBlending:
677 // Already handled
678 break;
683 /* Alpha blend an area from <src> with offset <soffs> to rectangle <dr> of <dst>
684 * Default offset is QPoint(0, 0).
685 * blendfactor = [0, 100%]
687 void KBackgroundRenderer::blend(QImage& dst, const QRect &_dr, const QImage& src, const QPoint &soffs, int blendFactor)
689 QRect dr = _dr;
690 int x, y, a;
691 dr &= dst.rect();
693 for (y = 0; y < dr.height(); y++) {
694 if (dst.scanLine(dr.y() + y) && src.scanLine(soffs.y() + y)) {
695 QRgb *b;
696 const QRgb *d;
697 for (x = 0; x < dr.width(); x++) {
698 b = reinterpret_cast<QRgb*>(dst.scanLine(dr.y() + y)
699 + (dr.x() + x) * sizeof(QRgb));
700 d = reinterpret_cast<const QRgb*>(src.scanLine(soffs.y() + y)
701 + (soffs.x() + x) * sizeof(QRgb));
702 a = (qAlpha(*d) * blendFactor) / 100;
703 *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8),
704 qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8),
705 qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8));
713 void KBackgroundRenderer::slotBackgroundDone(int exitCode, QProcess::ExitStatus exitStatus)
715 m_State |= BackgroundDone;
717 if (exitStatus == QProcess::NormalExit && !exitCode) {
718 m_Background.load(m_Tempfile->fileName());
719 m_State |= BackgroundDone;
722 delete m_Tempfile; m_Tempfile = 0;
723 m_pTimer->start(0);
724 setBusyCursor(false);
730 * Starts the rendering process.
732 void KBackgroundRenderer::start(bool enableBusyCursor)
734 m_enableBusyCursor = enableBusyCursor;
735 setBusyCursor(true);
737 m_Cached = false;
739 m_State = Rendering;
740 m_pTimer->start(0);
745 * This slot is connected to a timer event. It is called repeatedly until
746 * the rendering is done.
748 void KBackgroundRenderer::render()
750 setBusyCursor(true);
751 if (!(m_State & Rendering))
752 return;
754 if( !(m_State & InitCheck)) {
755 QString f = cacheFileName();
756 if( useCacheFile()) {
757 QString w = m_pDirs->findResource("wallpaper", currentWallpaper());
758 QFileInfo wi( w );
759 QFileInfo fi( f );
760 if( wi.lastModified().isValid() && fi.lastModified().isValid()
761 && wi.lastModified() < fi.lastModified()) {
762 QImage im;
763 if( im.load( f, "PNG" )) {
764 m_Image = im;
765 m_Pixmap = QPixmap::fromImage( m_Image );
766 m_Cached = true;
767 m_State |= InitCheck | BackgroundDone | WallpaperDone;
771 m_pTimer->start(0);
772 m_State |= InitCheck;
773 return;
776 int ret;
778 if (!(m_State & BackgroundDone)) {
779 ret = doBackground();
780 if (ret != Wait)
781 m_pTimer->start(0);
782 return;
785 // No async wallpaper
786 doWallpaper();
788 done();
789 setBusyCursor(false);
794 * Rendering is finished.
796 void KBackgroundRenderer::done()
798 setBusyCursor(false);
799 m_State |= AllDone;
800 emit imageDone(screen());
801 if(backgroundMode() == Program && m_pProc &&
802 m_pProc->exitStatus() != QProcess::NormalExit) {
803 emit programFailure(-1);
804 } else if(backgroundMode() == Program && m_pProc &&
805 m_pProc->exitCode()) {
806 emit programFailure(m_pProc->exitStatus());
807 } else if(backgroundMode() == Program) {
808 emit programSuccess();
813 * This function toggles a busy cursor on and off, for use in rendering.
814 * It is useful because of the ASYNC nature of the rendering - it is hard
815 * to make sure we don't set the busy cursor twice, but only restore
816 * once.
818 void KBackgroundRenderer::setBusyCursor(bool isBusy) {
819 if(m_isBusyCursor == isBusy)
820 return;
821 if (isBusy && !m_enableBusyCursor)
822 return;
823 m_isBusyCursor = isBusy;
824 if(isBusy)
825 QApplication::setOverrideCursor( QCursor(Qt::BusyCursor) );
826 else
827 QApplication::restoreOverrideCursor();
831 * Stop the rendering.
833 void KBackgroundRenderer::stop()
835 if (!(m_State & Rendering))
836 return;
838 doBackground(true);
839 doWallpaper(true);
840 m_State = 0;
845 * Cleanup after rendering.
847 void KBackgroundRenderer::cleanup()
849 setBusyCursor(false);
850 m_Background = QImage();
851 m_Image = QImage();
852 m_Pixmap = QPixmap();
853 m_Wallpaper = QImage();
854 delete m_pProc; m_pProc = 0L;
855 m_State = 0;
856 m_WallpaperRect = QRect();
857 m_Cached = false;
861 void KBackgroundRenderer::setPreview(const QSize &size)
863 if (size.isNull())
864 m_bPreview = false;
865 else {
866 m_bPreview = true;
867 m_Size = size;
872 QPixmap KBackgroundRenderer::pixmap()
874 if (m_State & AllDone) {
875 if( m_Pixmap.isNull())
876 m_Pixmap = QPixmap::fromImage( m_Image );
877 return m_Pixmap;
879 return QPixmap();
882 QImage KBackgroundRenderer::image()
884 if (m_State & AllDone) {
885 if( m_Image.isNull())
886 fullWallpaperBlend(); // create from m_Pixmap
887 return m_Image;
889 return QImage();
893 void KBackgroundRenderer::load(int screen, bool drawBackgroundPerScreen, bool reparseConfig)
895 if (m_State & Rendering)
896 stop();
898 cleanup();
899 m_bPreview = false;
900 m_Size = m_rSize;
902 KBackgroundSettings::load(screen, drawBackgroundPerScreen, reparseConfig);
905 void KBackgroundRenderer::createTempFile()
907 if( !m_Tempfile ){
908 m_Tempfile = new KTemporaryFile();
909 m_Tempfile->open();
913 QString KBackgroundRenderer::cacheFileName()
915 QString f = fingerprint();
916 f.replace ( ':', '_' ); // avoid characters that shouldn't be in filenames
917 f.replace ( '/', '#' );
918 f = KStandardDirs::locateLocal( "cache", QString( "background/%1x%2_%3.png" )
919 .arg( m_Size.width()).arg( m_Size.height()).arg( f ));
920 return f;
923 bool KBackgroundRenderer::useCacheFile() const
925 if( !enabled())
926 return false;
927 if( backgroundMode() == Program )
928 return false; // don't cache these at all
929 if( wallpaperMode() == NoWallpaper )
930 return false; // generating only background patterns should be always faster
931 QString file = currentWallpaper();
932 if( file.endsWith(".svg") || file.endsWith(".svgz"))
933 return true; // cache these, they can be bloody slow
934 switch( backgroundMode())
936 case NoWallpaper:
937 case Centred:
938 case Tiled:
939 case CenterTiled:
940 return false; // these don't need scaling
941 case CentredMaxpect:
942 case TiledMaxpect:
943 case Scaled:
944 case CentredAutoFit:
945 case ScaleAndCrop:
946 default:
947 return true;
951 void KBackgroundRenderer::saveCacheFile()
953 if( !( m_State & AllDone ))
954 return;
955 if( !useCacheFile())
956 return;
957 if( m_Image.isNull())
958 fullWallpaperBlend(); // generate from m_Pixmap
959 QString f = cacheFileName();
960 if( KStandardDirs::exists( f ) || m_Cached )
961 utime( QFile::encodeName( f ), NULL );
962 else {
963 m_Image.save( f, "PNG" );
964 // remove old entries from the cache
965 QDir dir( KStandardDirs::locateLocal( "cache", "background/" ));
966 const QFileInfoList list = dir.entryInfoList( QStringList() << "*.png", QDir::Files, QDir::Time | QDir::Reversed );
967 if( !list.isEmpty()) {
968 int size = 0;
969 Q_FOREACH( const QFileInfo& info, list )
970 size += info.size();
971 Q_FOREACH( const QFileInfo& info, list ) {
972 if( size < 8 * 1024 * 1024 )
973 break;
974 // keep everything newer than 10 minutes if the total size is less than 50M (just in case)
975 if( size < 50 * 1024 * 1024
976 && ( time_t ) info.lastModified().toTime_t() >= time( NULL ) - 10 * 60 )
977 break;
978 size -= info.size();
979 QFile::remove( info.absoluteFilePath());
985 #include "bgrender.moc"