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.
13 #include <config-workspace.h>
24 #include <QDesktopWidget>
25 #include <QPaintEngine>
30 #include <kapplication.h>
32 #include <kstandarddirs.h>
33 #include <qimageblitz.h>
34 #include <k3process.h>
35 #include <ktemporaryfile.h>
37 #include <kfilemetainfo.h>
39 #include <kconfiggroup.h>
40 #include <ksvgrenderer.h>
42 #include "bgdefaults.h"
48 /**** KBackgroundRenderer ****/
51 KBackgroundRenderer::KBackgroundRenderer(int desk
, int screen
, bool drawBackgroundPerScreen
, const KSharedConfigPtr
&config
, bool kdmMode
)
52 : KBackgroundSettings(desk
, screen
, drawBackgroundPerScreen
, config
, kdmMode
)
55 m_isBusyCursor
= false;
56 m_enableBusyCursor
= false;
57 m_pDirs
= KGlobal::dirs();
58 m_rSize
= m_Size
= drawBackgroundPerScreen
?
59 QApplication::desktop()->screenGeometry(screen
).size() : QApplication::desktop()->size();
64 m_TilingEnabled
= false;
66 m_pTimer
= new QTimer(this);
67 m_pTimer
->setSingleShot(true);
68 connect(m_pTimer
, SIGNAL(timeout()), SLOT(render()));
72 KBackgroundRenderer::~KBackgroundRenderer()
80 void KBackgroundRenderer::setSize(const QSize
&size
)
82 m_rSize
= m_Size
= size
;
86 * Re-configure because the desktop has been resized.
88 void KBackgroundRenderer::desktopResized()
91 m_rSize
= drawBackgroundPerScreen() ?
92 QApplication::desktop()->screenGeometry(screen()).size() : QApplication::desktop()->size();
98 void KBackgroundRenderer::tile(QImage
& dest
, const QRect
&_rect
, const QImage
& src
)
104 int h
= rect
.height(), w
= rect
.width();
105 int offx
= rect
.x(), offy
= rect
.y();
106 int sw
= src
.width(), sh
= src
.height();
108 for (y
=offy
; y
<offy
+h
; y
++)
109 for (x
=offx
; x
<offx
+w
; x
++)
110 dest
.setPixel(x
, y
, src
.pixel(x
%sw
, y
%sh
));
115 * Build a command line to run the program.
118 QString
KBackgroundRenderer::buildCommand()
125 cmd
= previewCommand();
132 while ((pos
= cmd
.indexOf('%', pos
)) != -1) {
134 if (pos
== (int) (cmd
.length() - 1))
137 switch (cmd
.at(pos
+1).toLatin1()) {
140 cmd
.replace(pos
, 2, K3ShellProcess::quote(m_Tempfile
->fileName()));
141 pos
+= m_Tempfile
->fileName().length() - 2;
145 num
.setNum(m_Size
.width());
146 cmd
.replace(pos
, 2, num
);
147 pos
+= num
.length() - 2;
151 num
.setNum(m_Size
.height());
152 cmd
.replace(pos
, 2, num
);
153 pos
+= num
.length() - 2;
157 cmd
.replace(pos
, 2, "%");
161 ++pos
; // avoid infinite loop
171 * Create a background tile. If the background mode is `Program',
172 * this is asynchronous.
174 int KBackgroundRenderer::doBackground(bool quit
)
176 if (m_State
& BackgroundDone
)
178 int bgmode
= backgroundMode();
184 if (bgmode
== Program
&& m_pProc
)
192 static unsigned int tileWidth
= 0;
193 static unsigned int tileHeight
= 0;
196 int tile_val
= QPixmap::defaultDepth() >= 24 ? 1 : 2;
197 // some dithering may be needed even with bpb==15/16, so don't use tileWidth==1
199 // with tileWidth>2, repainting the desktop causes nasty effect (XFree86 4.1.0 )
200 if( XQueryBestTile( QX11Info::display(), QX11Info::appRootWindow(), tile_val
, tile_val
,
201 &tileWidth
, &tileHeight
) != Success
)
202 tileWidth
= tileHeight
= tile_val
; // some defaults
207 // this can be tiled correctly without problems
208 m_Background
= QImage( tileWidth
, tileHeight
, QImage::Format_RGB32
);
209 m_Background
.fill(colorA().rgb());
214 if (pattern().isEmpty())
216 file
= m_pDirs
->findResource("dtop_pattern", pattern());
220 m_Background
.load(file
);
221 if (m_Background
.isNull())
223 int w
= m_Background
.width();
224 int h
= m_Background
.height();
225 if ((w
> m_Size
.width()) || (h
> m_Size
.height())) {
226 w
= qMin(w
, m_Size
.width());
227 h
= qMin(h
, m_Size
.height());
228 m_Background
= m_Background
.copy(0, 0, w
, h
);
230 Blitz::flatten(m_Background
, colorA(), colorB());
234 if (m_State
& BackgroundStarted
)
236 m_State
|= BackgroundStarted
;
239 file
= buildCommand();
244 m_pProc
= new K3ShellProcess
;
246 connect(m_pProc
, SIGNAL(processExited(K3Process
*)),
247 SLOT(slotBackgroundDone(K3Process
*)));
248 m_pProc
->start(K3ShellProcess::NotifyOnExit
);
252 case HorizontalGradient
:
255 // on <16bpp displays the gradient sucks when tiled because of dithering
257 size
.setHeight( tileHeight
);
258 m_Background
= Blitz::gradient(size
, colorA(), colorB(),
259 Blitz::HorizontalGradient
);
262 case VerticalGradient
:
265 // on <16bpp displays the gradient sucks when tiled because of dithering
267 size
.setWidth( tileWidth
);
268 m_Background
= Blitz::gradient(size
, colorA(), colorB(),
269 Blitz::VerticalGradient
);
272 case PyramidGradient
:
273 m_Background
= Blitz::gradient(m_Size
, colorA(), colorB(),
274 Blitz::PyramidGradient
);
277 case PipeCrossGradient
:
278 m_Background
= Blitz::gradient(m_Size
, colorA(), colorB(),
279 Blitz::PipeCrossGradient
);
282 case EllipticGradient
:
283 m_Background
= Blitz::gradient(m_Size
, colorA(), colorB(),
284 Blitz::EllipticGradient
);
289 m_State
|= BackgroundDone
;
295 int KBackgroundRenderer::doWallpaper(bool quit
)
297 if (m_State
& WallpaperDone
)
301 // currently no asynch. wallpapers
304 int wpmode
= enabled()?wallpaperMode():NoWallpaper
;
306 m_Wallpaper
= QImage();
307 if (wpmode
!= NoWallpaper
) {
309 if (currentWallpaper().isEmpty()) {
310 wpmode
= NoWallpaper
;
313 QString file
= m_pDirs
->findResource("wallpaper", currentWallpaper());
314 if (file
.isEmpty()) {
315 wpmode
= NoWallpaper
;
319 // _Don't_ use KMimeType, as it relies on ksycoca which we really
320 // don't want in krootimage (kdm context).
321 //if ( KMimeType::findByPath( file )->is( "image/svg+xml" ) ) {
322 if (file
.endsWith(".svg") || file
.endsWith(".svgz")) {
324 // Special stuff for SVG icons
327 //ksvgiconloader doesn't seem to let us find out the
328 //ratio of width to height so for the most part we just
329 //assume it's a square
336 svgHeight
= (int)(m_Size
.height() * 0.8);
337 svgWidth
= svgHeight
;
341 svgHeight
= (int)(m_Size
.height() * 0.5);
342 svgWidth
= svgHeight
;
345 svgHeight
= m_Size
.height();
346 svgWidth
= m_Size
.width();
351 svgHeight
= m_Size
.height();
352 svgWidth
= svgHeight
;
356 kWarning() << "unknown diagram type" ;
357 svgHeight
= m_Size
.height();
358 svgWidth
= svgHeight
;
361 //FIXME hack due to strangeness with
362 //background control modules
363 if ( svgHeight
< 200 ) {
368 KSvgRenderer
renderer(file
);
369 if (renderer
.isValid()) {
370 m_Wallpaper
= QImage(svgWidth
, svgHeight
, QImage::Format_ARGB32_Premultiplied
);
372 QPainter
p(&m_Wallpaper
);
376 m_Wallpaper
.load(file
);
378 if (m_Wallpaper
.isNull()) {
379 kWarning() << "failed to load wallpaper " << file
;
380 if (discardCurrentWallpaper())
382 wpmode
= NoWallpaper
;
385 m_Wallpaper
= m_Wallpaper
.convertToFormat(QImage::Format_ARGB32_Premultiplied
, Qt::DiffuseAlphaDither
);
387 // If we're previewing, scale the wallpaper down to make the preview
388 // look more like the real desktop.
390 int xs
= m_Wallpaper
.width() * m_Size
.width() / m_rSize
.width();
391 int ys
= m_Wallpaper
.height() * m_Size
.height() / m_rSize
.height();
392 if ((xs
< 1) || (ys
< 1))
396 if( m_WallpaperRect
.size() != QSize( xs
, ys
))
397 m_Wallpaper
= m_Wallpaper
.scaled(xs
, ys
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
400 // HACK: Use KFileMetaInfo only when we have a KApplication
401 // KFileMetaInfo needs ksycoca and so on, but this code is
402 // used also in krootimage (which in turn is used by kdm).
404 KFileMetaInfo
metaInfo(file
);
405 if (metaInfo
.isValid() && metaInfo
.item("Orientation").isValid()) {
406 switch (metaInfo
.item("Orientation").value().toInt()) {
408 // Flipped horizontally
409 m_Wallpaper
.mirrored(true, false);
412 // Rotated 180 degrees
413 m_Wallpaper
= m_Wallpaper
.transformed(QMatrix().rotate(180));
416 // Flipped vertically
417 m_Wallpaper
= m_Wallpaper
.mirrored(false, true);
420 // Rotated 90 degrees & flipped horizontally
421 m_Wallpaper
= m_Wallpaper
.transformed(QMatrix().rotate(90)).mirrored(true, false);
424 // Rotated 90 degrees
425 m_Wallpaper
= m_Wallpaper
.transformed(QMatrix().rotate(90));
428 // Rotated 90 degrees & flipped vertically
429 m_Wallpaper
= m_Wallpaper
.transformed(QMatrix().rotate(90)).mirrored(false, true);
432 // Rotated 270 degrees
433 m_Wallpaper
= m_Wallpaper
.transformed(QMatrix().rotate(270));
437 // Normal or invalid orientation
445 if (m_Background
.isNull()) {
446 m_Background
= QImage(8, 8, QImage::Format_RGB32
);
447 m_Background
.fill(colorA().rgb());
452 // desktop width/height
453 int w
= m_Size
.width();
454 int h
= m_Size
.height();
456 // wallpaper width/height
457 int ww
= m_Wallpaper
.width();
458 int wh
= m_Wallpaper
.height();
460 // to be filled destination rectangle; may exceed desktop!
461 m_WallpaperRect
= QRect();
468 m_WallpaperRect
.setRect((w
- ww
) / 2, (h
- wh
) / 2, ww
, wh
);
471 m_WallpaperRect
.setRect(0, 0, w
, h
);
474 m_WallpaperRect
.setCoords(-ww
+ ((w
- ww
) / 2) % ww
, -wh
+ ((h
- wh
) / 2) % wh
, w
-1, h
-1);
479 if( m_WallpaperRect
.size() != QSize( w
, h
))
480 m_Wallpaper
= m_Wallpaper
.scaled( w
, h
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
481 m_WallpaperRect
.setRect(0, 0, w
, h
);
484 if( ww
<= w
&& wh
<= h
) {
485 m_WallpaperRect
.setRect((w
- ww
) / 2, (h
- wh
) / 2, ww
, wh
); // like Centred
491 double sx
= (double) w
/ ww
;
492 double sy
= (double) h
/ wh
;
500 if( m_WallpaperRect
.size() != QSize( ww
, wh
))
501 m_Wallpaper
= m_Wallpaper
.scaled(ww
, wh
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
502 m_WallpaperRect
.setRect((w
- ww
) / 2, (h
- wh
) / 2, ww
, wh
);
507 double sx
= (double) w
/ ww
;
508 double sy
= (double) h
/ wh
;
516 if( m_WallpaperRect
.size() != QSize( ww
, wh
))
517 m_Wallpaper
= m_Wallpaper
.scaled(ww
, wh
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
518 m_WallpaperRect
.setRect(0, 0, w
, h
);
523 double sx
= (double) w
/ ww
;
524 double sy
= (double) h
/ wh
;
526 //Case 1: x needs bigger scaling. Lets increase x and leave part of y offscreen
530 //Case 2: y needs bigger scaling. Lets increase y and leave part of x offscreen
534 if( m_WallpaperRect
.size() != QSize( ww
, wh
))
535 m_Wallpaper
= m_Wallpaper
.scaled(ww
, wh
, Qt::IgnoreAspectRatio
, Qt::SmoothTransformation
);
536 m_WallpaperRect
.setRect((w
- ww
) / 2, (h
- wh
) / 2,w
, h
);
544 m_State
|= WallpaperDone
;
549 bool KBackgroundRenderer::canTile() const
551 return m_TilingEnabled
&& optimize();
554 void KBackgroundRenderer::wallpaperBlend()
556 if( !enabled() || wallpaperMode() == NoWallpaper
557 || (blendMode() == NoBlending
&&
558 ( QApplication::desktop()->paintEngine()->hasFeature(QPaintEngine::Antialiasing
)
559 || !m_Wallpaper
.hasAlphaChannel()))) {
560 fastWallpaperBlend();
563 fullWallpaperBlend();
567 // works only for NoBlending and no alpha in wallpaper
568 // but is much faster than QImage fidling
569 void KBackgroundRenderer::fastWallpaperBlend()
572 // copy background to m_pPixmap
573 if( !enabled() || (wallpaperMode() == NoWallpaper
&& canTile())) {
574 // if there's no wallpaper, no need to tile the pixmap to the size of desktop, as X does
575 // that automatically and using a smaller pixmap should save some memory
576 m_Pixmap
= QPixmap::fromImage( m_Background
);
579 else if( wallpaperMode() == Tiled
&& !m_Wallpaper
.hasAlphaChannel() && canTile() && !m_bPreview
) {
580 // tiles will be tiled by X automatically
581 m_Pixmap
= QPixmap::fromImage( m_Wallpaper
);
584 else if( m_WallpaperRect
.contains( QRect( QPoint( 0, 0 ), m_Size
))
585 && !m_Wallpaper
.hasAlphaChannel()) // wallpaper covers all and no blending
586 m_Pixmap
= QPixmap( m_Size
);
587 else if (m_Background
.size() == m_Size
)
588 m_Pixmap
= QPixmap::fromImage( m_Background
);
590 m_Pixmap
= QPixmap( m_Size
);
591 QPainter
p( &m_Pixmap
);
592 QPixmap pm
= QPixmap::fromImage( m_Background
);
593 p
.drawTiledPixmap( 0, 0, m_Size
.width(), m_Size
.height(), pm
);
596 // paint/alpha-blend wallpaper to destination rectangle of m_pPixmap
597 if (m_WallpaperRect
.isValid()) {
598 QPixmap wp_pixmap
= QPixmap::fromImage( m_Wallpaper
);
599 QPainter
pa( &m_Pixmap
);
600 int ww
= m_Wallpaper
.width();
601 int wh
= m_Wallpaper
.height();
602 for (int y
= m_WallpaperRect
.top(); y
< m_WallpaperRect
.bottom(); y
+= wh
) {
603 for (int x
= m_WallpaperRect
.left(); x
< m_WallpaperRect
.right(); x
+= ww
) {
604 pa
.drawPixmap( x
, y
, wp_pixmap
);
611 void KBackgroundRenderer::fullWallpaperBlend()
613 m_Pixmap
= QPixmap();
615 // desktop width/height
616 int w
= m_Size
.width();
617 int h
= m_Size
.height();
619 // copy background to m_pImage
620 if (m_Background
.size() == m_Size
) {
621 m_Image
= m_Background
.copy();
623 if (m_Image
.depth() < 32) {
624 m_Image
= m_Image
.convertToFormat(QImage::Format_ARGB32_Premultiplied
, Qt::DiffuseAlphaDither
);
627 m_Image
= QImage(w
, h
, QImage::Format_RGB32
);
628 tile(m_Image
, QRect(0, 0, w
, h
), m_Background
);
631 // blend wallpaper to destination rectangle of m_pImage
632 if (m_WallpaperRect
.isValid())
634 int blendFactor
= 100;
635 if (blendMode() == FlatBlending
)
636 blendFactor
= (blendBalance()+200)/4;
637 int ww
= m_Wallpaper
.width();
638 int wh
= m_Wallpaper
.height();
639 for (int y
= m_WallpaperRect
.top(); y
< m_WallpaperRect
.bottom(); y
+= wh
) {
640 for (int x
= m_WallpaperRect
.left(); x
< m_WallpaperRect
.right(); x
+= ww
) {
641 blend(m_Image
, QRect(x
, y
, ww
, wh
), m_Wallpaper
,
642 QPoint(-qMin(x
, 0), -qMin(y
, 0)), blendFactor
);
648 // blend whole desktop
649 if ( wallpaperMode() != NoWallpaper
) {
650 int bal
= blendBalance();
652 switch( blendMode() ) {
653 /* TODO disabled for now
654 case HorizontalBlending:
655 Blitz::blend( m_Image, m_Background,
656 Blitz::HorizontalGradient,
660 case VerticalBlending:
661 Blitz::blend( m_Image, m_Background,
662 Blitz::VerticalGradient,
666 case PyramidBlending:
667 Blitz::blend( m_Image, m_Background,
668 Blitz::PyramidGradient,
672 case PipeCrossBlending:
673 Blitz::blend( m_Image, m_Background,
674 Blitz::PipeCrossGradient,
678 case EllipticBlending:
679 Blitz::blend( m_Image, m_Background,
680 Blitz::EllipticGradient,
685 case IntensityBlending
:
686 Blitz::modulate( m_Image
, m_Background
, reverseBlending(),
687 Blitz::Intensity
, bal
, Blitz::All
);
690 case SaturateBlending
:
691 Blitz::modulate( m_Image
, m_Background
, reverseBlending(),
692 Blitz::Saturation
, bal
, Blitz::Brightness
);
695 case ContrastBlending
:
696 Blitz::modulate( m_Image
, m_Background
, reverseBlending(),
697 Blitz::Contrast
, bal
, Blitz::All
);
700 case HueShiftBlending
:
701 Blitz::modulate( m_Image
, m_Background
, reverseBlending(),
702 Blitz::HueShift
, bal
, Blitz::Brightness
);
712 /* Alpha blend an area from <src> with offset <soffs> to rectangle <dr> of <dst>
713 * Default offset is QPoint(0, 0).
714 * blendfactor = [0, 100%]
716 void KBackgroundRenderer::blend(QImage
& dst
, const QRect
&_dr
, const QImage
& src
, const QPoint
&soffs
, int blendFactor
)
722 for (y
= 0; y
< dr
.height(); y
++) {
723 if (dst
.scanLine(dr
.y() + y
) && src
.scanLine(soffs
.y() + y
)) {
726 for (x
= 0; x
< dr
.width(); x
++) {
727 b
= reinterpret_cast<QRgb
*>(dst
.scanLine(dr
.y() + y
)
728 + (dr
.x() + x
) * sizeof(QRgb
));
729 d
= reinterpret_cast<const QRgb
*>(src
.scanLine(soffs
.y() + y
)
730 + (soffs
.x() + x
) * sizeof(QRgb
));
731 a
= (qAlpha(*d
) * blendFactor
) / 100;
732 *b
= qRgb(qRed(*b
) - (((qRed(*b
) - qRed(*d
)) * a
) >> 8),
733 qGreen(*b
) - (((qGreen(*b
) - qGreen(*d
)) * a
) >> 8),
734 qBlue(*b
) - (((qBlue(*b
) - qBlue(*d
)) * a
) >> 8));
742 void KBackgroundRenderer::slotBackgroundDone(K3Process
*process
)
744 Q_ASSERT(process
== m_pProc
);
745 m_State
|= BackgroundDone
;
747 if (m_pProc
->normalExit() && !m_pProc
->exitStatus()) {
748 m_Background
.load(m_Tempfile
->fileName());
749 m_State
|= BackgroundDone
;
752 delete m_Tempfile
; m_Tempfile
= 0;
754 setBusyCursor(false);
760 * Starts the rendering process.
762 void KBackgroundRenderer::start(bool enableBusyCursor
)
764 m_enableBusyCursor
= enableBusyCursor
;
775 * This slot is connected to a timer event. It is called repeatedly until
776 * the rendering is done.
778 void KBackgroundRenderer::render()
781 if (!(m_State
& Rendering
))
784 if( !(m_State
& InitCheck
)) {
785 QString f
= cacheFileName();
786 if( useCacheFile()) {
787 QString w
= m_pDirs
->findResource("wallpaper", currentWallpaper());
790 if( wi
.lastModified().isValid() && fi
.lastModified().isValid()
791 && wi
.lastModified() < fi
.lastModified()) {
793 if( im
.load( f
, "PNG" )) {
795 m_Pixmap
= QPixmap::fromImage( m_Image
);
797 m_State
|= InitCheck
| BackgroundDone
| WallpaperDone
;
802 m_State
|= InitCheck
;
808 if (!(m_State
& BackgroundDone
)) {
809 ret
= doBackground();
815 // No async wallpaper
819 setBusyCursor(false);
824 * Rendering is finished.
826 void KBackgroundRenderer::done()
828 setBusyCursor(false);
830 emit
imageDone(desk(), screen());
831 if(backgroundMode() == Program
&& m_pProc
&&
832 m_pProc
->normalExit() && m_pProc
->exitStatus()) {
833 emit
programFailure(desk(), m_pProc
->exitStatus());
834 } else if(backgroundMode() == Program
&& m_pProc
&&
835 !m_pProc
->normalExit()) {
836 emit
programFailure(desk(), -1);
837 } else if(backgroundMode() == Program
) {
838 emit
programSuccess(desk());
843 * This function toggles a busy cursor on and off, for use in rendering.
844 * It is useful because of the ASYNC nature of the rendering - it is hard
845 * to make sure we don't set the busy cursor twice, but only restore
848 void KBackgroundRenderer::setBusyCursor(bool isBusy
) {
849 if(m_isBusyCursor
== isBusy
)
851 if (isBusy
&& !m_enableBusyCursor
)
853 m_isBusyCursor
= isBusy
;
855 QApplication::setOverrideCursor( QCursor(Qt::BusyCursor
) );
857 QApplication::restoreOverrideCursor();
861 * Stop the rendering.
863 void KBackgroundRenderer::stop()
865 if (!(m_State
& Rendering
))
875 * Cleanup after rendering.
877 void KBackgroundRenderer::cleanup()
879 setBusyCursor(false);
880 m_Background
= QImage();
882 m_Pixmap
= QPixmap();
883 m_Wallpaper
= QImage();
884 delete m_pProc
; m_pProc
= 0L;
886 m_WallpaperRect
= QRect();
891 void KBackgroundRenderer::setPreview(const QSize
&size
)
902 QPixmap
KBackgroundRenderer::pixmap()
904 if (m_State
& AllDone
) {
905 if( m_Pixmap
.isNull())
906 m_Pixmap
= QPixmap::fromImage( m_Image
);
912 QImage
KBackgroundRenderer::image()
914 if (m_State
& AllDone
) {
915 if( m_Image
.isNull())
916 fullWallpaperBlend(); // create from m_Pixmap
923 void KBackgroundRenderer::load(int desk
, int screen
, bool drawBackgroundPerScreen
, bool reparseConfig
)
925 if (m_State
& Rendering
)
932 KBackgroundSettings::load(desk
, screen
, drawBackgroundPerScreen
, reparseConfig
);
935 void KBackgroundRenderer::createTempFile()
938 m_Tempfile
= new KTemporaryFile();
943 QString
KBackgroundRenderer::cacheFileName()
945 QString f
= fingerprint();
946 f
.replace ( ':', '_' ); // avoid characters that shouldn't be in filenames
947 f
.replace ( '/', '#' );
948 f
= KStandardDirs::locateLocal( "cache", QString( "background/%1x%2_%3.png" )
949 .arg( m_Size
.width()).arg( m_Size
.height()).arg( f
));
953 bool KBackgroundRenderer::useCacheFile() const
957 if( backgroundMode() == Program
)
958 return false; // don't cache these at all
959 if( wallpaperMode() == NoWallpaper
)
960 return false; // generating only background patterns should be always faster
961 QString file
= currentWallpaper();
962 if( file
.endsWith(".svg") || file
.endsWith(".svgz"))
963 return true; // cache these, they can be bloody slow
964 switch( backgroundMode())
970 return false; // these don't need scaling
981 void KBackgroundRenderer::saveCacheFile()
983 if( !( m_State
& AllDone
))
987 if( m_Image
.isNull())
988 fullWallpaperBlend(); // generate from m_Pixmap
989 QString f
= cacheFileName();
990 if( KStandardDirs::exists( f
) || m_Cached
)
991 utime( QFile::encodeName( f
), NULL
);
993 m_Image
.save( f
, "PNG" );
994 // remove old entries from the cache
995 QDir
dir( KStandardDirs::locateLocal( "cache", "background/" ));
996 const QFileInfoList list
= dir
.entryInfoList( QStringList() << "*.png", QDir::Files
, QDir::Time
| QDir::Reversed
);
997 if( !list
.isEmpty()) {
999 Q_FOREACH( QFileInfo info
, list
)
1000 size
+= info
.size();
1001 Q_FOREACH( QFileInfo info
, list
) {
1002 if( size
< 8 * 1024 * 1024 )
1004 // keep everything newer than 10 minutes if the total size is less than 50M (just in case)
1005 if( size
< 50 * 1024 * 1024
1006 && ( time_t ) info
.lastModified().toTime_t() >= time( NULL
) - 10 * 60 )
1008 size
-= info
.size();
1009 QFile::remove( info
.absoluteFilePath());
1015 //BEGIN class KVirtualBGRenderer
1016 KVirtualBGRenderer::KVirtualBGRenderer(int desk
, const KSharedConfigPtr
&config
, bool kdmMode
)
1023 m_kdmMode
= kdmMode
;
1025 // The following code is borrowed from KBackgroundSettings::KBackgroundSettings
1027 int screen_number
= 0;
1028 if (QX11Info::display())
1029 screen_number
= DefaultScreen(QX11Info::display());
1031 if (screen_number
== 0)
1032 configname
= "kdesktoprc";
1034 configname
.sprintf("kdesktop-screen-%drc", screen_number
);
1036 m_pConfig
= KSharedConfig::openConfig(configname
, KConfig::NoGlobals
);
1042 m_size
= QApplication::desktop()->size();
1045 KVirtualBGRenderer::~KVirtualBGRenderer()
1047 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1048 delete m_renderer
[i
];
1054 KBackgroundRenderer
* KVirtualBGRenderer::renderer(unsigned screen
)
1056 return m_renderer
[screen
];
1060 QPixmap
KVirtualBGRenderer::pixmap()
1062 if (m_numRenderers
== 1)
1063 return m_renderer
[0]->pixmap();
1069 bool KVirtualBGRenderer::needProgramUpdate()
1071 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1073 if ( m_renderer
[i
]->backgroundMode() == KBackgroundSettings::Program
&&
1074 m_renderer
[i
]->KBackgroundProgram::needUpdate() )
1081 void KVirtualBGRenderer::programUpdate()
1083 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1085 if ( m_renderer
[i
]->backgroundMode() == KBackgroundSettings::Program
&&
1086 m_renderer
[i
]->KBackgroundProgram::needUpdate() )
1088 m_renderer
[i
]->KBackgroundProgram::update();
1094 bool KVirtualBGRenderer::needWallpaperChange()
1096 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1098 if ( m_renderer
[i
]->needWallpaperChange() )
1105 void KVirtualBGRenderer::changeWallpaper()
1107 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1109 m_renderer
[i
]->changeWallpaper();
1114 int KVirtualBGRenderer::hash()
1117 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1119 fp
+= m_renderer
[i
]->fingerprint();
1122 kDebug() << " fp=\""<<fp
<<"\" h="<<h
;
1127 bool KVirtualBGRenderer::isActive()
1129 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1131 if ( m_renderer
[i
]->isActive() )
1138 void KVirtualBGRenderer::setEnabled(bool enable
)
1140 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1141 m_renderer
[i
]->setEnabled(enable
);
1145 void KVirtualBGRenderer::desktopResized()
1147 m_size
= QApplication::desktop()->size();
1152 m_pPixmap
= new QPixmap(m_size
);
1153 m_pPixmap
->fill(Qt::black
);
1156 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1157 m_renderer
[i
]->desktopResized();
1161 void KVirtualBGRenderer::setPreview(const QSize
& size
)
1169 *m_pPixmap
= QPixmap(m_size
);
1172 m_scaleX
= float(m_size
.width()) / float(QApplication::desktop()->size().width());
1173 m_scaleY
= float(m_size
.height()) / float(QApplication::desktop()->size().height());
1175 // Scale renderers appropriately
1176 for (unsigned i
=0; i
<m_renderer
.size(); ++i
)
1178 QSize unscaledRendererSize
= renderSize(i
);
1180 m_renderer
[i
]->setPreview( QSize(
1181 int(unscaledRendererSize
.width() * m_scaleX
),
1182 int(unscaledRendererSize
.height() * m_scaleY
) ) );
1187 QSize
KVirtualBGRenderer::renderSize(int screen
)
1189 return m_bDrawBackgroundPerScreen
?
1190 QApplication::desktop()->screenGeometry(screen
).size() : QApplication::desktop()->size();
1194 void KVirtualBGRenderer::initRenderers()
1196 KConfigGroup
cg(m_pConfig
, "Background Common");
1197 m_bDrawBackgroundPerScreen
= cg
.readEntry( QString("DrawBackgroundPerScreen_%1").arg(m_desk
), _defDrawBackgroundPerScreen
);
1199 m_bCommonScreen
= cg
.readEntry("CommonScreen", _defCommonScreen
);
1201 m_numRenderers
= m_bDrawBackgroundPerScreen
? QApplication::desktop()->numScreens() : 1;
1203 m_bFinished
.resize(m_numRenderers
);
1204 m_bFinished
.fill(false);
1206 if (m_numRenderers
== m_renderer
.size())
1209 for (unsigned i
=0; i
<m_renderer
.size(); ++i
)
1210 delete m_renderer
[i
];
1212 m_renderer
.resize(m_numRenderers
);
1213 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1215 int eScreen
= m_bCommonScreen
? 0 : i
;
1216 KBackgroundRenderer
* r
= new KBackgroundRenderer( m_desk
, eScreen
, m_bDrawBackgroundPerScreen
, m_pConfig
, m_kdmMode
);
1217 m_renderer
.insert( i
, r
);
1218 r
->setSize(renderSize(i
));
1219 connect( r
, SIGNAL(imageDone(int,int)), this, SLOT(screenDone(int,int)) );
1224 void KVirtualBGRenderer::load(int desk
, bool reparseConfig
)
1228 m_bCommonScreen
= m_pConfig
->group("Background Common").readEntry("CommonScreen", _defCommonScreen
);
1232 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1234 unsigned eScreen
= m_bCommonScreen
? 0 : i
;
1235 m_renderer
[i
]->load(desk
, eScreen
, m_bDrawBackgroundPerScreen
, reparseConfig
);
1240 void KVirtualBGRenderer::screenDone(int _desk
, int _screen
)
1245 const KBackgroundRenderer
* sender
= dynamic_cast<const KBackgroundRenderer
*>(this->sender());
1246 int screen
= m_renderer
.find(sender
);
1251 m_bFinished
[screen
] = true;
1256 // There's more than one renderer, so we are drawing each output to our own pixmap
1258 QRect overallGeometry
;
1259 for (int i
=0; i
< QApplication::desktop()->numScreens(); ++i
)
1260 overallGeometry
|= QApplication::desktop()->screenGeometry(i
);
1262 QPoint drawPos
= QApplication::desktop()->screenGeometry(screen
).topLeft() - overallGeometry
.topLeft();
1263 drawPos
.setX( int(drawPos
.x() * m_scaleX
) );
1264 drawPos
.setY( int(drawPos
.y() * m_scaleY
) );
1266 QPixmap source
= m_renderer
[screen
]->pixmap();
1267 QSize renderSize
= this->renderSize(screen
);
1268 renderSize
.setWidth( int(renderSize
.width() * m_scaleX
) );
1269 renderSize
.setHeight( int(renderSize
.height() * m_scaleY
) );
1271 QPainter
p(m_pPixmap
);
1273 if (renderSize
== source
.size())
1274 p
.drawPixmap( drawPos
, source
);
1277 p
.drawTiledPixmap( drawPos
.x(), drawPos
.y(), renderSize
.width(), renderSize
.height(), source
);
1282 for (int i
=0; i
<m_bFinished
.size(); ++i
)
1284 if (!m_bFinished
[i
])
1288 emit
imageDone(m_desk
);
1292 void KVirtualBGRenderer::start()
1300 if (m_numRenderers
> 1)
1302 m_pPixmap
= new QPixmap(m_size
);
1303 // If are screen sizes do not properly tile the overall virtual screen
1304 // size, then we want the untiled parts to be black for use in desktop
1306 m_pPixmap
->fill(Qt::black
);
1309 m_bFinished
.fill(false);
1310 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1311 m_renderer
[i
]->start();
1315 void KVirtualBGRenderer::stop()
1317 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1318 m_renderer
[i
]->stop();
1322 void KVirtualBGRenderer::cleanup()
1324 m_bFinished
.fill(false);
1326 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1327 m_renderer
[i
]->cleanup();
1333 void KVirtualBGRenderer::saveCacheFile()
1335 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1336 m_renderer
[i
]->saveCacheFile();
1339 void KVirtualBGRenderer::enableTiling( bool enable
)
1341 for (unsigned i
=0; i
<m_numRenderers
; ++i
)
1342 m_renderer
[i
]->enableTiling( enable
);
1345 //END class KVirtualBGRenderer
1348 #include "bgrender.moc"