TDE base libraries and programs
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.

1364 lines
36 KiB

  1. /* vi: ts=8 sts=4 sw=4
  2. * kate: space-indent on; tab-width 8; indent-width 4; indent-mode cstyle;
  3. *
  4. * This file is part of the KDE project, module kdesktop.
  5. * Copyright (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl>
  6. *
  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 <config.h>
  11. #include "KCrossBGRender.h"
  12. #include <time.h>
  13. #include <stdlib.h>
  14. #include <utime.h>
  15. #include <tqtimer.h>
  16. #include <tqpainter.h>
  17. #include <tqimage.h>
  18. #include <tqfileinfo.h>
  19. #include <tqdir.h>
  20. #include <dcopclient.h>
  21. #include <tdeapplication.h>
  22. #include <kdebug.h>
  23. #include <kstandarddirs.h>
  24. #include <kimageeffect.h>
  25. #include <kprocess.h>
  26. #include <kpixmapio.h>
  27. #include <tdetempfile.h>
  28. #include <kcursor.h>
  29. #include <kmimetype.h>
  30. #include <tdefilemetainfo.h>
  31. #ifdef HAVE_LIBART
  32. #include <ksvgiconengine.h>
  33. #endif
  34. #include "bgdefaults.h"
  35. #include "bghash.h"
  36. #include "bgrender.h"
  37. #include <X11/Xlib.h>
  38. #include <config.h>
  39. /**** KBackgroundRenderer ****/
  40. KBackgroundRenderer::KBackgroundRenderer(int desk, int screen, bool drawBackgroundPerScreen, TDEConfig *config)
  41. : KBackgroundSettings(desk, screen, drawBackgroundPerScreen, config)
  42. {
  43. m_State = 0;
  44. m_isBusyCursor = false;
  45. m_enableBusyCursor = false;
  46. m_pDirs = TDEGlobal::dirs();
  47. m_rSize = m_Size = drawBackgroundPerScreen ? TDEApplication::desktop()->screenGeometry(screen).size() : TDEApplication::desktop()->geometry().size();
  48. m_pProc = 0L;
  49. m_Tempfile = 0L;
  50. m_bPreview = false;
  51. m_Cached = false;
  52. m_TilingEnabled = false;
  53. m_pTimer = new TQTimer(this);
  54. connect(m_pTimer, TQT_SIGNAL(timeout()), TQT_SLOT(render()));
  55. }
  56. KBackgroundRenderer::~KBackgroundRenderer()
  57. {
  58. cleanup();
  59. delete m_Tempfile;
  60. m_Tempfile = 0;
  61. }
  62. void KBackgroundRenderer::setSize(const TQSize &size)
  63. {
  64. m_rSize = m_Size = size;
  65. }
  66. /*
  67. * Re-configure because the desktop has been resized.
  68. */
  69. void KBackgroundRenderer::desktopResized()
  70. {
  71. m_State = 0;
  72. m_rSize = drawBackgroundPerScreen() ? TDEApplication::desktop()->screenGeometry(screen()).size() : TDEApplication::desktop()->geometry().size();
  73. if( !m_bPreview )
  74. m_Size = m_rSize;
  75. }
  76. void KBackgroundRenderer::tile(TQImage& dest, TQRect rect, const TQImage& src)
  77. {
  78. rect &= dest.rect();
  79. int x, y;
  80. int h = rect.height(), w = rect.width();
  81. int offx = rect.x(), offy = rect.y();
  82. int sw = src.width(), sh = src.height();
  83. for (y=offy; y<offy+h; y++)
  84. for (x=offx; x<offx+w; x++)
  85. dest.setPixel(x, y, src.pixel(x%sw, y%sh));
  86. }
  87. /*
  88. * Build a command line to run the program.
  89. */
  90. TQString KBackgroundRenderer::buildCommand()
  91. {
  92. TQString num;
  93. int pos = 0;
  94. TQString cmd;
  95. if (m_bPreview)
  96. cmd = previewCommand();
  97. else
  98. cmd = command();
  99. if (cmd.isEmpty())
  100. return TQString();
  101. while ((pos = cmd.find('%', pos)) != -1) {
  102. if (pos == (int) (cmd.length() - 1))
  103. break;
  104. switch (cmd.at(pos+1).latin1()) {
  105. case 'f':
  106. createTempFile();
  107. cmd.replace(pos, 2, KShellProcess::quote(m_Tempfile->name()));
  108. pos += m_Tempfile->name().length() - 2;
  109. break;
  110. case 'x':
  111. num.setNum(m_Size.width());
  112. cmd.replace(pos, 2, num);
  113. pos += num.length() - 2;
  114. break;
  115. case 'y':
  116. num.setNum(m_Size.height());
  117. cmd.replace(pos, 2, num);
  118. pos += num.length() - 2;
  119. break;
  120. case '%':
  121. cmd.replace(pos, 2, "%");
  122. pos--;
  123. break;
  124. default:
  125. ++pos; // avoid infinite loop
  126. break;
  127. }
  128. }
  129. return cmd;
  130. }
  131. /*
  132. * Create a background tile. If the background mode is `Program',
  133. * this is asynchronous.
  134. */
  135. int KBackgroundRenderer::doBackground(bool quit)
  136. {
  137. if (m_State & BackgroundDone)
  138. return Done;
  139. int bgmode = backgroundMode();
  140. if (!enabled())
  141. bgmode= Flat;
  142. if (quit) {
  143. if (bgmode == Program && m_pProc)
  144. m_pProc->kill();
  145. return Done;
  146. }
  147. int retval = Done;
  148. TQString file;
  149. static unsigned int tileWidth = 0;
  150. static unsigned int tileHeight = 0;
  151. if( tileWidth == 0 )
  152. {
  153. int tile_val = TQPixmap::defaultDepth() >= 24 ? 1 : 2;
  154. // some dithering may be needed even with bpb==15/16, so don't use tileWidth==1
  155. // for them
  156. // with tileWidth>2, repainting the desktop causes nasty effect (XFree86 4.1.0 )
  157. if( XQueryBestTile( tqt_xdisplay(), tqt_xrootwin(), tile_val, tile_val,
  158. &tileWidth, &tileHeight ) != Success )
  159. tileWidth = tileHeight = tile_val; // some defaults
  160. }
  161. switch (bgmode) {
  162. case Flat:
  163. // this can be tiled correctly without problems
  164. m_Background.create( tileWidth, tileHeight, 32);
  165. m_Background.fill(colorA().rgb());
  166. break;
  167. case Pattern:
  168. {
  169. if (pattern().isEmpty())
  170. break;
  171. file = m_pDirs->findResource("dtop_pattern", pattern());
  172. if (file.isEmpty())
  173. break;
  174. m_Background.load(file);
  175. if (m_Background.isNull())
  176. break;
  177. int w = m_Background.width();
  178. int h = m_Background.height();
  179. if ((w > m_Size.width()) || (h > m_Size.height())) {
  180. w = TQMIN(w, m_Size.width());
  181. h = TQMIN(h, m_Size.height());
  182. m_Background = m_Background.copy(0, 0, w, h);
  183. }
  184. KImageEffect::flatten(m_Background, colorA(), colorB(), 0);
  185. break;
  186. }
  187. case Program:
  188. if (m_State & BackgroundStarted)
  189. break;
  190. m_State |= BackgroundStarted;
  191. createTempFile();
  192. file = buildCommand();
  193. if (file.isEmpty())
  194. break;
  195. delete m_pProc;
  196. m_pProc = new KShellProcess;
  197. *m_pProc << file;
  198. connect(m_pProc, TQT_SIGNAL(processExited(TDEProcess *)),
  199. TQT_SLOT(slotBackgroundDone(TDEProcess *)));
  200. m_pProc->start(KShellProcess::NotifyOnExit);
  201. retval = Wait;
  202. break;
  203. case HorizontalGradient:
  204. {
  205. TQSize size = m_Size;
  206. // on <16bpp displays the gradient sucks when tiled because of dithering
  207. if( canTile())
  208. size.setHeight( tileHeight );
  209. m_Background = KImageEffect::gradient(size, colorA(), colorB(),
  210. KImageEffect::HorizontalGradient, 0);
  211. break;
  212. }
  213. case VerticalGradient:
  214. {
  215. TQSize size = m_Size;
  216. // on <16bpp displays the gradient sucks when tiled because of dithering
  217. if( canTile())
  218. size.setWidth( tileWidth );
  219. m_Background = KImageEffect::gradient(size, colorA(), colorB(),
  220. KImageEffect::VerticalGradient, 0);
  221. break;
  222. }
  223. case PyramidGradient:
  224. m_Background = KImageEffect::gradient(m_Size, colorA(), colorB(),
  225. KImageEffect::PyramidGradient, 0);
  226. break;
  227. case PipeCrossGradient:
  228. m_Background = KImageEffect::gradient(m_Size, colorA(), colorB(),
  229. KImageEffect::PipeCrossGradient, 0);
  230. break;
  231. case EllipticGradient:
  232. m_Background = KImageEffect::gradient(m_Size, colorA(), colorB(),
  233. KImageEffect::EllipticGradient, 0);
  234. break;
  235. }
  236. if (retval == Done)
  237. m_State |= BackgroundDone;
  238. return retval;
  239. }
  240. int KBackgroundRenderer::doWallpaper(bool quit)
  241. {
  242. if (m_State & WallpaperDone)
  243. return Done;
  244. if (quit)
  245. // currently no asynch. wallpapers
  246. return Done;
  247. int wpmode= enabled()?wallpaperMode():NoWallpaper;
  248. m_Wallpaper = TQImage();
  249. if (wpmode != NoWallpaper) {
  250. wp_load:
  251. if (currentWallpaper().isEmpty()) {
  252. wpmode = NoWallpaper;
  253. goto wp_out;
  254. }
  255. TQString file = m_pDirs->findResource("wallpaper", currentWallpaper());
  256. if (file.isEmpty()) {
  257. wpmode = NoWallpaper;
  258. goto wp_out;
  259. }
  260. // _Don't_ use KMimeType, as it relies on tdesycoca which we really
  261. // don't want in krootimage (tdm context).
  262. //if ( KMimeType::findByPath( file )->is( "image/svg+xml" ) ) {
  263. if (file.endsWith(".svg") || file.endsWith(".svgz")) {
  264. #ifdef HAVE_LIBART
  265. // Special stuff for SVG icons
  266. KSVGIconEngine* svgEngine = new KSVGIconEngine();
  267. //FIXME
  268. //ksvgiconloader doesn't seem to let us find out the
  269. //ratio of width to height so for the most part we just
  270. //assume it's a square
  271. int svgWidth;
  272. int svgHeight;
  273. switch (wpmode)
  274. {
  275. case Centred:
  276. case CentredAutoFit:
  277. svgHeight = (int)(m_Size.height() * 0.8);
  278. svgWidth = svgHeight;
  279. break;
  280. case Tiled:
  281. case CenterTiled:
  282. svgHeight = (int)(m_Size.height() * 0.5);
  283. svgWidth = svgHeight;
  284. break;
  285. case Scaled:
  286. svgHeight = m_Size.height();
  287. svgWidth = m_Size.width();
  288. break;
  289. case CentredMaxpect:
  290. case ScaleAndCrop:
  291. case TiledMaxpect:
  292. svgHeight = m_Size.height();
  293. svgWidth = svgHeight;
  294. break;
  295. case NoWallpaper:
  296. default:
  297. kdWarning() << k_funcinfo << "unknown diagram type" << endl;
  298. svgHeight = m_Size.height();
  299. svgWidth = svgHeight;
  300. break;
  301. }
  302. //FIXME hack due to strangeness with
  303. //background control modules
  304. if ( svgHeight < 200 ) {
  305. svgHeight *= 6;
  306. svgWidth *= 6;
  307. }
  308. if (svgEngine->load(svgWidth, svgHeight, file )) {
  309. TQImage *image = svgEngine->image();
  310. m_Wallpaper = *image;
  311. delete image;
  312. } else {
  313. kdWarning() << "failed to load SVG file " << file << endl;
  314. }
  315. delete svgEngine;
  316. #else //not libart
  317. kdWarning() << k_funcinfo
  318. << "tried to load SVG file but libart not installed" << endl;
  319. #endif
  320. } else {
  321. m_Wallpaper.load(file);
  322. }
  323. if (m_Wallpaper.isNull()) {
  324. if (discardCurrentWallpaper())
  325. goto wp_load;
  326. wpmode = NoWallpaper;
  327. goto wp_out;
  328. }
  329. m_Wallpaper = m_Wallpaper.convertDepth(32, Qt::DiffuseAlphaDither);
  330. // If we're previewing, scale the wallpaper down to make the preview
  331. // look more like the real desktop.
  332. if (m_bPreview) {
  333. int xs = m_Wallpaper.width() * m_Size.width() / m_rSize.width();
  334. int ys = m_Wallpaper.height() * m_Size.height() / m_rSize.height();
  335. if ((xs < 1) || (ys < 1))
  336. {
  337. xs = ys = 1;
  338. }
  339. if( m_Wallpaper.size() != TQSize( xs, ys ))
  340. m_Wallpaper = m_Wallpaper.smoothScale(xs, ys);
  341. }
  342. // HACK: Use KFileMetaInfo only when we're attached to DCOP.
  343. // KFileMetaInfo needs tdesycoca and so on, but this code is
  344. // used also in krootimage (which in turn is used by tdm).
  345. if( kapp->dcopClient()->isAttached()) {
  346. KFileMetaInfo metaInfo(file);
  347. if (metaInfo.isValid() && metaInfo.item("Orientation").isValid()) {
  348. switch (metaInfo.item("Orientation").string().toInt()) {
  349. case 2:
  350. // Flipped horizontally
  351. m_Wallpaper = m_Wallpaper.mirror(true, false);
  352. break;
  353. case 3:
  354. // Rotated 180 degrees
  355. m_Wallpaper = KImageEffect::rotate(m_Wallpaper, KImageEffect::Rotate180);
  356. break;
  357. case 4:
  358. // Flipped vertically
  359. m_Wallpaper = m_Wallpaper.mirror(false, true);
  360. break;
  361. case 5:
  362. // Rotated 90 degrees & flipped horizontally
  363. m_Wallpaper = KImageEffect::rotate(m_Wallpaper, KImageEffect::Rotate90).mirror(true, false);
  364. break;
  365. case 6:
  366. // Rotated 90 degrees
  367. m_Wallpaper = KImageEffect::rotate(m_Wallpaper, KImageEffect::Rotate90);
  368. break;
  369. case 7:
  370. // Rotated 90 degrees & flipped vertically
  371. m_Wallpaper = KImageEffect::rotate(m_Wallpaper, KImageEffect::Rotate90).mirror(false, true);
  372. break;
  373. case 8:
  374. // Rotated 270 degrees
  375. m_Wallpaper = KImageEffect::rotate(m_Wallpaper, KImageEffect::Rotate270);
  376. break;
  377. case 1:
  378. default:
  379. // Normal or invalid orientation
  380. break;
  381. }
  382. }
  383. }
  384. }
  385. wp_out:
  386. if (m_Background.isNull()) {
  387. m_Background.create(8, 8, 32);
  388. m_Background.fill(colorA().rgb());
  389. }
  390. int retval = Done;
  391. int w = m_Size.width(); // desktop width/height
  392. int h = m_Size.height();
  393. int ww = m_Wallpaper.width(); // wallpaper width/height
  394. int wh = m_Wallpaper.height();
  395. m_WallpaperRect = TQRect(); // to be filled destination rectangle; may exceed desktop!
  396. switch (wpmode)
  397. {
  398. case NoWallpaper:
  399. break;
  400. case Centred:
  401. m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
  402. break;
  403. case Tiled:
  404. m_WallpaperRect.setRect(0, 0, w, h);
  405. break;
  406. case CenterTiled:
  407. m_WallpaperRect.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh, w-1, h-1);
  408. break;
  409. case Scaled:
  410. ww = w;
  411. wh = h;
  412. if( m_WallpaperRect.size() != TQSize( w, h ))
  413. m_Wallpaper = m_Wallpaper.smoothScale( w, h );
  414. m_WallpaperRect.setRect(0, 0, w, h);
  415. break;
  416. case CentredAutoFit:
  417. if( ww <= w && wh <= h ) {
  418. m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centred
  419. break;
  420. }
  421. // fall through
  422. case CentredMaxpect:
  423. {
  424. double sx = (double) w / ww;
  425. double sy = (double) h / wh;
  426. if (sx > sy) {
  427. ww = (int)(sy * ww);
  428. wh = h;
  429. } else {
  430. wh = (int)(sx * wh);
  431. ww = w;
  432. }
  433. if( m_WallpaperRect.size() != TQSize( ww, wh ))
  434. m_Wallpaper = m_Wallpaper.smoothScale(ww, wh);
  435. m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
  436. break;
  437. }
  438. case TiledMaxpect:
  439. {
  440. double sx = (double) w / ww;
  441. double sy = (double) h / wh;
  442. if (sx > sy) {
  443. ww = (int)(sy * ww);
  444. wh = h;
  445. } else {
  446. wh = (int)(sx * wh);
  447. ww = w;
  448. }
  449. if( m_WallpaperRect.size() != TQSize( ww, wh ))
  450. m_Wallpaper = m_Wallpaper.smoothScale(ww, wh);
  451. m_WallpaperRect.setRect(0, 0, w, h);
  452. break;
  453. }
  454. case ScaleAndCrop:
  455. {
  456. double sx = (double) w / ww;
  457. double sy = (double) h / wh;
  458. if (sx > sy) {
  459. //Case 1: x needs bigger scaling. Lets increase x and leave part of y offscreen
  460. ww = w;
  461. wh=(int)(sx * wh);
  462. } else {
  463. //Case 2: y needs bigger scaling. Lets increase y and leave part of x offscreen
  464. wh = h;
  465. ww = (int)(sy*ww);
  466. }
  467. if( m_WallpaperRect.size() != TQSize( ww, wh ))
  468. m_Wallpaper = m_Wallpaper.smoothScale(ww, wh);
  469. m_WallpaperRect.setRect((w - ww) / 2, (h - wh) / 2,w, h);
  470. break;
  471. }
  472. }
  473. wallpaperBlend();
  474. if (retval == Done)
  475. m_State |= WallpaperDone;
  476. return retval;
  477. }
  478. bool KBackgroundRenderer::canTile() const
  479. {
  480. return m_TilingEnabled && optimize();
  481. }
  482. extern bool tqt_use_xrender; // in Qt ( qapplication_x11.cpp )
  483. void KBackgroundRenderer::wallpaperBlend()
  484. {
  485. if( !enabled() || wallpaperMode() == NoWallpaper
  486. || (blendMode() == NoBlending && ( tqt_use_xrender || !m_Wallpaper.hasAlphaBuffer()))) {
  487. fastWallpaperBlend();
  488. }
  489. else {
  490. fullWallpaperBlend();
  491. }
  492. }
  493. // works only for NoBlending and no alpha in wallpaper
  494. // but is much faster than TQImage fidling
  495. void KBackgroundRenderer::fastWallpaperBlend()
  496. {
  497. m_Image = TQImage();
  498. // copy background to m_pPixmap
  499. if( !enabled() || (wallpaperMode() == NoWallpaper && canTile())) {
  500. // if there's no wallpaper, no need to tile the pixmap to the size of desktop, as X does
  501. // that automatically and using a smaller pixmap should save some memory
  502. m_Pixmap.convertFromImage( m_Background );
  503. return;
  504. }
  505. else if( wallpaperMode() == Tiled && !m_Wallpaper.hasAlphaBuffer() && canTile() && !m_bPreview ) {
  506. // tiles will be tiled by X automatically
  507. if( useShm()) {
  508. KPixmapIO io;
  509. m_Pixmap = io.convertToPixmap( m_Wallpaper );
  510. }
  511. else
  512. m_Pixmap.convertFromImage( m_Wallpaper );
  513. return;
  514. }
  515. else if( m_WallpaperRect.contains( TQRect( TQPoint( 0, 0 ), m_Size ))
  516. && !m_Wallpaper.hasAlphaBuffer()) // wallpaper covers all and no blending
  517. m_Pixmap = TQPixmap( m_Size );
  518. else if (m_Background.size() == m_Size)
  519. m_Pixmap.convertFromImage( m_Background );
  520. else {
  521. m_Pixmap = TQPixmap( m_Size );
  522. TQPainter p( &m_Pixmap );
  523. TQPixmap pm;
  524. pm.convertFromImage( m_Background );
  525. p.drawTiledPixmap( 0, 0, m_Size.width(), m_Size.height(), pm );
  526. }
  527. // paint/alpha-blend wallpaper to destination rectangle of m_pPixmap
  528. if (m_WallpaperRect.isValid()) {
  529. TQPixmap wp_pixmap;
  530. if( useShm() && !m_Wallpaper.hasAlphaBuffer()) {
  531. KPixmapIO io;
  532. wp_pixmap = io.convertToPixmap( m_Wallpaper );
  533. }
  534. else
  535. wp_pixmap.convertFromImage( m_Wallpaper );
  536. int ww = m_Wallpaper.width();
  537. int wh = m_Wallpaper.height();
  538. for (int y = m_WallpaperRect.top(); y < m_WallpaperRect.bottom(); y += wh) {
  539. for (int x = m_WallpaperRect.left(); x < m_WallpaperRect.right(); x += ww) {
  540. bitBlt( &m_Pixmap, x, y, &wp_pixmap, 0, 0, ww, wh );
  541. }
  542. }
  543. }
  544. }
  545. void KBackgroundRenderer::fullWallpaperBlend()
  546. {
  547. m_Pixmap = TQPixmap();
  548. int w = m_Size.width(); // desktop width/height
  549. int h = m_Size.height();
  550. // copy background to m_pImage
  551. if (m_Background.size() == m_Size) {
  552. m_Image = m_Background.copy();
  553. if (m_Image.depth() < 32)
  554. m_Image = m_Image.convertDepth(32, Qt::DiffuseAlphaDither);
  555. } else {
  556. m_Image.create(w, h, 32);
  557. tile(m_Image, TQRect(0, 0, w, h), m_Background);
  558. }
  559. // blend wallpaper to destination rectangle of m_pImage
  560. if (m_WallpaperRect.isValid())
  561. {
  562. int blendFactor = 100;
  563. if (blendMode() == FlatBlending)
  564. blendFactor = (blendBalance()+200)/4;
  565. int ww = m_Wallpaper.width();
  566. int wh = m_Wallpaper.height();
  567. for (int y = m_WallpaperRect.top(); y < m_WallpaperRect.bottom(); y += wh) {
  568. for (int x = m_WallpaperRect.left(); x < m_WallpaperRect.right(); x += ww) {
  569. blend(m_Image, TQRect(x, y, ww, wh), m_Wallpaper,
  570. TQPoint(-TQMIN(x, 0), -TQMIN(y, 0)), blendFactor);
  571. }
  572. }
  573. }
  574. // blend whole desktop
  575. if ( wallpaperMode() != NoWallpaper) {
  576. int bal = blendBalance();
  577. switch( blendMode() ) {
  578. case HorizontalBlending:
  579. KImageEffect::blend( m_Image, m_Background,
  580. KImageEffect::HorizontalGradient,
  581. bal, 100 );
  582. break;
  583. case VerticalBlending:
  584. KImageEffect::blend( m_Image, m_Background,
  585. KImageEffect::VerticalGradient,
  586. 100, bal );
  587. break;
  588. case PyramidBlending:
  589. KImageEffect::blend( m_Image, m_Background,
  590. KImageEffect::PyramidGradient,
  591. bal, bal );
  592. break;
  593. case PipeCrossBlending:
  594. KImageEffect::blend( m_Image, m_Background,
  595. KImageEffect::PipeCrossGradient,
  596. bal, bal );
  597. break;
  598. case EllipticBlending:
  599. KImageEffect::blend( m_Image, m_Background,
  600. KImageEffect::EllipticGradient,
  601. bal, bal );
  602. break;
  603. case IntensityBlending:
  604. KImageEffect::modulate( m_Image, m_Background, reverseBlending(),
  605. KImageEffect::Intensity, bal, KImageEffect::All );
  606. break;
  607. case SaturateBlending:
  608. KImageEffect::modulate( m_Image, m_Background, reverseBlending(),
  609. KImageEffect::Saturation, bal, KImageEffect::Gray );
  610. break;
  611. case ContrastBlending:
  612. KImageEffect::modulate( m_Image, m_Background, reverseBlending(),
  613. KImageEffect::Contrast, bal, KImageEffect::All );
  614. break;
  615. case HueShiftBlending:
  616. KImageEffect::modulate( m_Image, m_Background, reverseBlending(),
  617. KImageEffect::HueShift, bal, KImageEffect::Gray );
  618. break;
  619. case FlatBlending:
  620. // Already handled
  621. break;
  622. }
  623. }
  624. }
  625. /* Alpha blend an area from <src> with offset <soffs> to rectangle <dr> of <dst>
  626. * Default offset is TQPoint(0, 0).
  627. * blendfactor = [0, 100%]
  628. */
  629. void KBackgroundRenderer::blend(TQImage& dst, TQRect dr, const TQImage& src, TQPoint soffs, int blendFactor)
  630. {
  631. int x, y, a;
  632. dr &= dst.rect();
  633. for (y = 0; y < dr.height(); y++) {
  634. if (dst.scanLine(dr.y() + y) && src.scanLine(soffs.y() + y)) {
  635. TQRgb *b, *d;
  636. for (x = 0; x < dr.width(); x++) {
  637. b = reinterpret_cast<TQRgb*>(dst.scanLine(dr.y() + y)
  638. + (dr.x() + x) * sizeof(TQRgb));
  639. d = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(src).scanLine(soffs.y() + y)
  640. + (soffs.x() + x) * sizeof(TQRgb));
  641. a = (tqAlpha(*d) * blendFactor) / 100;
  642. *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8),
  643. tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8),
  644. tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8));
  645. }
  646. }
  647. }
  648. }
  649. void KBackgroundRenderer::slotBackgroundDone(TDEProcess *process)
  650. {
  651. Q_ASSERT(process == m_pProc);
  652. m_State |= BackgroundDone;
  653. if (m_pProc->normalExit() && !m_pProc->exitStatus()) {
  654. m_Background.load(m_Tempfile->name());
  655. m_State |= BackgroundDone;
  656. }
  657. m_Tempfile->unlink();
  658. delete m_Tempfile; m_Tempfile = 0;
  659. m_pTimer->start(0, true);
  660. setBusyCursor(false);
  661. }
  662. /*
  663. * Starts the rendering process.
  664. */
  665. void KBackgroundRenderer::start(bool enableBusyCursor)
  666. {
  667. m_enableBusyCursor = enableBusyCursor;
  668. setBusyCursor(true);
  669. m_Cached = false;
  670. m_State = Rendering;
  671. m_pTimer->start(0, true);
  672. }
  673. /*
  674. * This slot is connected to a timer event. It is called repeatedly until
  675. * the rendering is done.
  676. */
  677. void KBackgroundRenderer::render()
  678. {
  679. setBusyCursor(true);
  680. if (!(m_State & Rendering))
  681. return;
  682. if( !(m_State & InitCheck)) {
  683. TQString f = cacheFileName();
  684. if( useCacheFile()) {
  685. TQString w = m_pDirs->findResource("wallpaper", currentWallpaper());
  686. TQFileInfo wi( w );
  687. TQFileInfo fi( f );
  688. if( wi.lastModified().isValid() && fi.lastModified().isValid()
  689. && wi.lastModified() < fi.lastModified()) {
  690. TQImage im;
  691. if( im.load( f, "PNG" )) {
  692. m_Image = im;
  693. m_Pixmap = TQPixmap( m_Size );
  694. m_Pixmap.convertFromImage( m_Image );
  695. m_Cached = true;
  696. m_State |= InitCheck | BackgroundDone | WallpaperDone;
  697. }
  698. }
  699. }
  700. m_pTimer->start(0, true);
  701. m_State |= InitCheck;
  702. return;
  703. }
  704. int ret;
  705. if (!(m_State & BackgroundDone)) {
  706. ret = doBackground();
  707. if (ret != Wait)
  708. m_pTimer->start(0, true);
  709. return;
  710. }
  711. // No async wallpaper
  712. doWallpaper();
  713. done();
  714. setBusyCursor(false);
  715. }
  716. /*
  717. * Rendering is finished.
  718. */
  719. void KBackgroundRenderer::done()
  720. {
  721. setBusyCursor(false);
  722. m_State |= AllDone;
  723. emit imageDone(desk(), screen());
  724. if(backgroundMode() == Program && m_pProc &&
  725. m_pProc->normalExit() && m_pProc->exitStatus()) {
  726. emit programFailure(desk(), m_pProc->exitStatus());
  727. } else if(backgroundMode() == Program && m_pProc &&
  728. !m_pProc->normalExit()) {
  729. emit programFailure(desk(), -1);
  730. } else if(backgroundMode() == Program) {
  731. emit programSuccess(desk());
  732. }
  733. }
  734. /*
  735. * This function toggles a busy cursor on and off, for use in rendering.
  736. * It is useful because of the ASYNC nature of the rendering - it is hard
  737. * to make sure we don't set the busy cursor twice, but only restore
  738. * once.
  739. */
  740. void KBackgroundRenderer::setBusyCursor(bool isBusy) {
  741. if(m_isBusyCursor == isBusy)
  742. return;
  743. if (isBusy && !m_enableBusyCursor)
  744. return;
  745. m_isBusyCursor = isBusy;
  746. if(isBusy)
  747. TQApplication::setOverrideCursor( KCursor::workingCursor() );
  748. else
  749. TQApplication::restoreOverrideCursor();
  750. }
  751. /*
  752. * Stop the rendering.
  753. */
  754. void KBackgroundRenderer::stop()
  755. {
  756. if (!(m_State & Rendering))
  757. return;
  758. doBackground(true);
  759. doWallpaper(true);
  760. m_State = 0;
  761. }
  762. /*
  763. * Cleanup after rendering.
  764. */
  765. void KBackgroundRenderer::cleanup()
  766. {
  767. setBusyCursor(false);
  768. m_Background = TQImage();
  769. m_Image = TQImage();
  770. m_Pixmap = TQPixmap();
  771. m_Wallpaper = TQImage();
  772. delete m_pProc; m_pProc = 0L;
  773. m_State = 0;
  774. m_WallpaperRect = TQRect();
  775. m_Cached = false;
  776. }
  777. void KBackgroundRenderer::setPreview(const TQSize &size)
  778. {
  779. if (size.isNull())
  780. m_bPreview = false;
  781. else {
  782. m_bPreview = true;
  783. m_Size = size;
  784. }
  785. }
  786. TQPixmap KBackgroundRenderer::pixmap()
  787. {
  788. if (m_State & AllDone) {
  789. if( m_Pixmap.isNull())
  790. m_Pixmap.convertFromImage( m_Image );
  791. return m_Pixmap;
  792. }
  793. return TQPixmap();
  794. }
  795. TQImage KBackgroundRenderer::image()
  796. {
  797. if (m_State & AllDone) {
  798. if( m_Image.isNull())
  799. fullWallpaperBlend(); // create from m_Pixmap
  800. return m_Image;
  801. }
  802. return TQImage();
  803. }
  804. void KBackgroundRenderer::load(int desk, int screen, bool drawBackgroundPerScreen, bool reparseConfig)
  805. {
  806. if (m_State & Rendering)
  807. stop();
  808. cleanup();
  809. m_bPreview = false;
  810. m_Size = m_rSize;
  811. KBackgroundSettings::load(desk, screen, drawBackgroundPerScreen, reparseConfig);
  812. }
  813. void KBackgroundRenderer::createTempFile()
  814. {
  815. if( !m_Tempfile )
  816. m_Tempfile = new KTempFile();
  817. }
  818. TQString KBackgroundRenderer::cacheFileName()
  819. {
  820. TQString f = fingerprint();
  821. f.replace ( ':', '_' ); // avoid characters that shouldn't be in filenames
  822. f.replace ( '/', '#' );
  823. f = locateLocal( "cache", TQString( "background/%1x%2_%3.png" )
  824. .arg( m_Size.width()).arg( m_Size.height()).arg( f ));
  825. return f;
  826. }
  827. bool KBackgroundRenderer::useCacheFile() const
  828. {
  829. if( !enabled())
  830. return false;
  831. if( backgroundMode() == Program )
  832. return false; // don't cache these at all
  833. if( wallpaperMode() == NoWallpaper )
  834. return false; // generating only background patterns should be always faster
  835. TQString file = currentWallpaper();
  836. if( file.endsWith(".svg") || file.endsWith(".svgz"))
  837. return true; // cache these, they can be bloody slow
  838. switch( backgroundMode())
  839. {
  840. case NoWallpaper:
  841. case Centred:
  842. case Tiled:
  843. case CenterTiled:
  844. return false; // these don't need scaling
  845. case CentredMaxpect:
  846. case TiledMaxpect:
  847. case Scaled:
  848. case CentredAutoFit:
  849. case ScaleAndCrop:
  850. default:
  851. return true;
  852. }
  853. }
  854. void KBackgroundRenderer::saveCacheFile()
  855. {
  856. if( !( m_State & AllDone ))
  857. return;
  858. if( !useCacheFile())
  859. return;
  860. if( m_Image.isNull())
  861. fullWallpaperBlend(); // generate from m_Pixmap
  862. TQString f = cacheFileName();
  863. if( TDEStandardDirs::exists( f ) || m_Cached )
  864. utime( TQFile::encodeName( f ), NULL );
  865. else {
  866. m_Image.save( f, "PNG" );
  867. // remove old entries from the cache
  868. TQDir dir( locateLocal( "cache", "background/" ));
  869. if( const TQFileInfoList* list = dir.entryInfoList( "*.png", TQDir::Files, TQDir::Time | TQDir::Reversed )) {
  870. int size = 0;
  871. for( TQFileInfoListIterator it( *list );
  872. TQFileInfo* info = it.current();
  873. ++it )
  874. size += info->size();
  875. for( TQFileInfoListIterator it( *list );
  876. TQFileInfo* info = it.current();
  877. ++it ) {
  878. if( size < 8 * 1024 * 1024 )
  879. break;
  880. // keep everything newer than 10 minutes if the total size is less than 50M (just in case)
  881. if( size < 50 * 1024 * 1024
  882. && ( time_t ) info->lastModified().toTime_t() >= time( NULL ) - 10 * 60 )
  883. break;
  884. size -= info->size();
  885. TQFile::remove( info->absFilePath());
  886. }
  887. }
  888. }
  889. }
  890. //BEGIN class KVirtualBGRenderer
  891. KVirtualBGRenderer::KVirtualBGRenderer( int desk, TDEConfig *config )
  892. {
  893. m_pPixmap = 0l;
  894. m_desk = desk;
  895. m_numRenderers = 0;
  896. m_scaleX = 1;
  897. m_scaleY = 1;
  898. // The following code is borrowed from KBackgroundSettings::KBackgroundSettings
  899. if (!config) {
  900. int screen_number = 0;
  901. if (tqt_xdisplay())
  902. screen_number = DefaultScreen(tqt_xdisplay());
  903. TQCString configname;
  904. if (screen_number == 0)
  905. configname = "kdesktoprc";
  906. else
  907. configname.sprintf("kdesktop-screen-%drc", screen_number);
  908. m_pConfig = new TDEConfig(configname, false, false);
  909. m_bDeleteConfig = true;
  910. } else {
  911. m_pConfig = config;
  912. m_bDeleteConfig = false;
  913. }
  914. initRenderers();
  915. m_size = TDEApplication::desktop()->geometry().size();
  916. }
  917. KVirtualBGRenderer::~KVirtualBGRenderer()
  918. {
  919. for (unsigned i=0; i<m_numRenderers; ++i)
  920. delete m_renderer[i];
  921. delete m_pPixmap;
  922. if (m_bDeleteConfig)
  923. delete m_pConfig;
  924. }
  925. KCrossBGRender * KVirtualBGRenderer::renderer(unsigned screen)
  926. {
  927. return m_renderer[screen];
  928. }
  929. TQPixmap KVirtualBGRenderer::pixmap()
  930. {
  931. if (m_numRenderers == 1)
  932. return m_renderer[0]->pixmap();
  933. return *m_pPixmap;
  934. }
  935. bool KVirtualBGRenderer::needProgramUpdate()
  936. {
  937. for (unsigned i=0; i<m_numRenderers; ++i)
  938. {
  939. if ( m_renderer[i]->backgroundMode() == KBackgroundSettings::Program &&
  940. m_renderer[i]->KBackgroundProgram::needUpdate() )
  941. return true;
  942. }
  943. return false;
  944. }
  945. void KVirtualBGRenderer::programUpdate()
  946. {
  947. for (unsigned i=0; i<m_numRenderers; ++i)
  948. {
  949. if ( m_renderer[i]->backgroundMode() == KBackgroundSettings::Program &&
  950. m_renderer[i]->KBackgroundProgram::needUpdate() )
  951. {
  952. m_renderer[i]->KBackgroundProgram::update();
  953. }
  954. }
  955. }
  956. bool KVirtualBGRenderer::needWallpaperChange()
  957. {
  958. for (unsigned i=0; i<m_numRenderers; ++i)
  959. {
  960. if ( m_renderer[i]->needWallpaperChange() )
  961. return true;
  962. }
  963. return false;
  964. }
  965. void KVirtualBGRenderer::changeWallpaper()
  966. {
  967. for (unsigned i=0; i<m_numRenderers; ++i)
  968. {
  969. m_renderer[i]->changeWallpaper();
  970. }
  971. }
  972. int KVirtualBGRenderer::hash()
  973. {
  974. TQString fp;
  975. for (unsigned i=0; i<m_numRenderers; ++i)
  976. {
  977. fp += m_renderer[i]->fingerprint();
  978. }
  979. //kdDebug() << k_funcinfo << " fp=\""<<fp<<"\" h="<<QHash(fp)<<endl;
  980. return TQHash(fp);
  981. }
  982. bool KVirtualBGRenderer::isActive()
  983. {
  984. for (unsigned i=0; i<m_numRenderers; ++i)
  985. {
  986. if ( m_renderer[i]->isActive() )
  987. return true;
  988. }
  989. return false;
  990. }
  991. void KVirtualBGRenderer::setEnabled(bool enable)
  992. {
  993. for (unsigned i=0; i<m_numRenderers; ++i)
  994. m_renderer[i]->setEnabled(enable);
  995. }
  996. void KVirtualBGRenderer::desktopResized()
  997. {
  998. m_size = TDEApplication::desktop()->geometry().size();
  999. if (m_pPixmap)
  1000. {
  1001. delete m_pPixmap;
  1002. m_pPixmap = new TQPixmap(m_size);
  1003. m_pPixmap->fill(Qt::black);
  1004. }
  1005. initRenderers();
  1006. }
  1007. void KVirtualBGRenderer::setPreview(const TQSize & size)
  1008. {
  1009. if (m_size == size)
  1010. return;
  1011. m_size = size;
  1012. if (m_pPixmap)
  1013. m_pPixmap->resize(m_size);
  1014. // Scaling factors
  1015. m_scaleX = float(m_size.width()) / float(TQApplication::desktop()->size().width());
  1016. m_scaleY = float(m_size.height()) / float(TQApplication::desktop()->size().height());
  1017. // Scale renderers appropriately
  1018. for (unsigned i=0; i<m_renderer.size(); ++i)
  1019. {
  1020. TQSize unscaledRendererSize = renderSize(i);
  1021. m_renderer[i]->setPreview( TQSize(
  1022. int(unscaledRendererSize.width() * m_scaleX),
  1023. int(unscaledRendererSize.height() * m_scaleY) ) );
  1024. }
  1025. }
  1026. TQSize KVirtualBGRenderer::renderSize(int screen)
  1027. {
  1028. return m_bDrawBackgroundPerScreen ? TDEApplication::desktop()->screenGeometry(screen).size() : TDEApplication::desktop()->geometry().size();
  1029. }
  1030. void KVirtualBGRenderer::initRenderers()
  1031. {
  1032. m_pConfig->setGroup("Background Common");
  1033. m_bDrawBackgroundPerScreen = m_pConfig->readBoolEntry( TQString("DrawBackgroundPerScreen_%1").arg(m_desk), _defDrawBackgroundPerScreen );
  1034. m_bCommonScreen = m_pConfig->readBoolEntry("CommonScreen", _defCommonScreen);
  1035. m_numRenderers = m_bDrawBackgroundPerScreen ? TDEApplication::desktop()->numScreens() : 1;
  1036. if (m_numRenderers < 2) {
  1037. // Only one screen is currently available; deactivate per-screen rendering but do not overwrite multi-screen settings
  1038. m_bDrawBackgroundPerScreen = false;
  1039. }
  1040. m_bFinished.resize(m_numRenderers);
  1041. m_bFinished.fill(false);
  1042. if (m_numRenderers == m_renderer.size())
  1043. return;
  1044. for (unsigned i=0; i<m_renderer.size(); ++i)
  1045. delete m_renderer[i];
  1046. m_renderer.resize(m_numRenderers);
  1047. for (unsigned i=0; i<m_numRenderers; ++i)
  1048. {
  1049. int eScreen = m_bCommonScreen ? 0 : i;
  1050. KCrossBGRender *r = new KCrossBGRender(m_desk, eScreen, m_bDrawBackgroundPerScreen, m_pConfig);
  1051. m_renderer.insert( i, r );
  1052. r->setSize(renderSize(i));
  1053. connect( r, TQT_SIGNAL(imageDone(int,int)), this, TQT_SLOT(screenDone(int,int)) );
  1054. }
  1055. }
  1056. void KVirtualBGRenderer::load(int desk, bool reparseConfig)
  1057. {
  1058. m_desk = desk;
  1059. m_pConfig->setGroup("Background Common");
  1060. m_bCommonScreen = m_pConfig->readBoolEntry("CommonScreen", _defCommonScreen);
  1061. initRenderers();
  1062. for (unsigned i=0; i<m_numRenderers; ++i)
  1063. {
  1064. unsigned eScreen = m_bCommonScreen ? 0 : i;
  1065. m_renderer[i]->load(desk, eScreen, m_bDrawBackgroundPerScreen, reparseConfig);
  1066. }
  1067. }
  1068. void KVirtualBGRenderer::screenDone(int _desk, int _screen)
  1069. {
  1070. Q_UNUSED(_desk);
  1071. Q_UNUSED(_screen);
  1072. const KCrossBGRender * sender = dynamic_cast<const KCrossBGRender*>(this->sender());
  1073. int screen = m_renderer.find(sender);
  1074. if (screen == -1)
  1075. //??
  1076. return;
  1077. m_bFinished[screen] = true;
  1078. if (m_pPixmap)
  1079. {
  1080. // There's more than one renderer, so we are drawing each output to our own pixmap
  1081. TQRect overallGeometry;
  1082. for (int i=0; i < TDEApplication::desktop()->numScreens(); ++i) {
  1083. overallGeometry |= TDEApplication::desktop()->screenGeometry(i);
  1084. }
  1085. TQPoint drawPos = TDEApplication::desktop()->screenGeometry(screen).topLeft() - overallGeometry.topLeft();
  1086. drawPos.setX( int(drawPos.x() * m_scaleX) );
  1087. drawPos.setY( int(drawPos.y() * m_scaleY) );
  1088. TQPixmap source = m_renderer[screen]->pixmap();
  1089. TQSize renderSize = this->renderSize(screen);
  1090. renderSize.setWidth( int(renderSize.width() * m_scaleX) );
  1091. renderSize.setHeight( int(renderSize.height() * m_scaleY) );
  1092. TQPainter p(m_pPixmap);
  1093. if (renderSize == source.size())
  1094. p.drawPixmap( drawPos, source );
  1095. else
  1096. p.drawTiledPixmap( drawPos.x(), drawPos.y(), renderSize.width(), renderSize.height(), source );
  1097. p.end();
  1098. }
  1099. for (unsigned i=0; i<m_bFinished.size(); ++i)
  1100. {
  1101. if (!m_bFinished[i])
  1102. return;
  1103. }
  1104. emit imageDone(m_desk);
  1105. }
  1106. void KVirtualBGRenderer::start()
  1107. {
  1108. if (m_pPixmap)
  1109. {
  1110. delete m_pPixmap;
  1111. m_pPixmap = 0l;
  1112. }
  1113. if (m_numRenderers > 1)
  1114. {
  1115. m_pPixmap = new TQPixmap(m_size);
  1116. // If are screen sizes do not properly tile the overall virtual screen
  1117. // size, then we want the untiled parts to be black for use in desktop
  1118. // previews, etc
  1119. m_pPixmap->fill(Qt::black);
  1120. }
  1121. m_bFinished.fill(false);
  1122. for (unsigned i=0; i<m_numRenderers; ++i)
  1123. m_renderer[i]->start();
  1124. }
  1125. void KVirtualBGRenderer::stop()
  1126. {
  1127. for (unsigned i=0; i<m_numRenderers; ++i)
  1128. m_renderer[i]->stop();
  1129. }
  1130. void KVirtualBGRenderer::cleanup()
  1131. {
  1132. m_bFinished.fill(false);
  1133. for (unsigned i=0; i<m_numRenderers; ++i)
  1134. m_renderer[i]->cleanup();
  1135. delete m_pPixmap;
  1136. m_pPixmap = 0l;
  1137. }
  1138. void KVirtualBGRenderer::saveCacheFile()
  1139. {
  1140. for (unsigned i=0; i<m_numRenderers; ++i)
  1141. m_renderer[i]->saveCacheFile();
  1142. }
  1143. void KVirtualBGRenderer::enableTiling( bool enable )
  1144. {
  1145. for (unsigned i=0; i<m_numRenderers; ++i)
  1146. m_renderer[i]->enableTiling( enable );
  1147. }
  1148. //END class KVirtualBGRenderer
  1149. #include "bgrender.moc"