summaryrefslogtreecommitdiffstats
path: root/src/komposetaskvisualizer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/komposetaskvisualizer.cpp')
-rw-r--r--src/komposetaskvisualizer.cpp458
1 files changed, 458 insertions, 0 deletions
diff --git a/src/komposetaskvisualizer.cpp b/src/komposetaskvisualizer.cpp
new file mode 100644
index 0000000..a3930aa
--- /dev/null
+++ b/src/komposetaskvisualizer.cpp
@@ -0,0 +1,458 @@
+//
+// C++ Implementation: %{MODULE}
+//
+// Description:
+//
+//
+// Author: %{AUTHOR} <%{EMAIL}>, (C) %{YEAR}
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "komposeglobal.h"
+#include "komposesettings.h"
+#include "komposeviewmanager.h"
+#include "komposetaskmanager.h"
+
+#include <qpixmap.h>
+#include <qtimer.h>
+#include <kwin.h>
+#include <netwm.h>
+#include <qapplication.h>
+#include <kdebug.h>
+
+#include <time.h>
+
+#include "komposetaskvisualizer.h"
+
+
+KomposeTaskVisualizer::KomposeTaskVisualizer(KomposeTask *parent, const char *name)
+ : QObject(parent, name),
+ task(parent),
+ scaledScreenshotDirty(false),
+ screenshotSuspended(false),
+ screenshotBlocked(false),
+ lasteffect( IEFFECT_NONE )
+{
+#ifdef COMPOSITE
+ validBackingPix = false;
+ compositeInit = false;
+#endif
+
+ screenshot.setOptimization( QPixmap::BestOptim );
+ scaledScreenshot.setOptimization( QPixmap::BestOptim );
+
+ // Create highlight color modifier
+ cmHighlight = imlib_create_color_modifier();
+ imlib_context_set_color_modifier(cmHighlight);
+ imlib_modify_color_modifier_brightness(0.13);
+
+ cmMinimized = imlib_create_color_modifier();
+ imlib_context_set_color_modifier(cmMinimized);
+ imlib_modify_color_modifier_brightness(-0.13);
+ imlib_context_set_color_modifier(0);
+
+ if ( !KomposeSettings::instance()->getCacheScaledPixmaps() )
+ {
+ // clear cached pixmaps on viewclose
+ connect( KomposeViewManager::instance(), SIGNAL(viewClosed()), this, SLOT(clearCached()) );
+ }
+
+ initXComposite();
+ connect( KomposeSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(initXComposite()) );
+}
+
+KomposeTaskVisualizer::~KomposeTaskVisualizer()
+{
+#ifdef COMPOSITE
+ if ( compositeInit )
+ XDamageDestroy( dpy, damage);
+#endif
+ scaledScreenshot.resize(0,0);
+ screenshot.resize(0,0);
+}
+
+
+/**
+ * Called from outside to retrieve a screenshot
+ * @param pix The pixmap the screenshot will be rendered onto
+ */
+void KomposeTaskVisualizer::renderOnPixmap(QPixmap* pix, int effect)
+{
+ if ( scaledScreenshotDirty || scaledScreenshot.isNull() || scaledScreenshot.size() != pix->size() ||
+ KomposeSettings::instance()->getImageEffects() && (lasteffect != effect ) )
+ {
+ lasteffect = effect;
+ renderScaledScreenshot( pix->size() );
+ }
+
+ copyBlt ( pix, 0, 0, &scaledScreenshot, 0, 0, pix->width(), pix->height() );
+
+ // QPainter p( pix );
+ // p.drawPixmap(0 ,0 , *scaledScreenshot, 0, 0, pix->width(), pix->height() );
+ // p.end();
+}
+
+
+/**
+ * Renders a scaled version of screenshot and stores it as scaledScreenshot
+ * @param newSize
+ */
+void KomposeTaskVisualizer::renderScaledScreenshot( QSize newSize )
+{
+ kdDebug() << "KomposeTaskVisualizer::renderScaledScreenshot() (" << task->window() << ") " << newSize.width() << "x" << newSize.height() << endl;
+
+ scaledScreenshot.resize( newSize );
+
+ if ( KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite() )
+ {
+#ifdef COMPOSITE
+ if ( !validBackingPix )
+ {
+ // When we get here we have never referenced a backingpix...
+ // FIXME: Currently it seems that there is no way to retrieve unmapped backing pixmaps,
+ // even switching desktops won't work due to the latency of XComposite :(
+ // Return a empty pixmap
+ scaledScreenshot.fill(white);
+ return;
+ }
+
+ // Create a Screenshot qpixmap
+ screenshot.resize( task->getGeometry().size() );
+
+ Picture picture = XRenderCreatePicture( dpy, windowBackingPix, format, CPSubwindowMode, &pa );
+ XRenderComposite( dpy,
+ hasAlpha ? PictOpOver : PictOpSrc,
+ picture,
+ None,
+ screenshot.x11RenderHandle(),
+ task->getGeometry().x() - task->getFrameGeometry().x(),
+ task->getGeometry().y() - task->getFrameGeometry().y(),
+ 0, 0, 0, 0,
+ screenshot.width(), screenshot.height() );
+ XRenderFreePicture (dpy, picture);
+#endif
+
+ }
+ /* if ( KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite() )
+ {
+ // The XComposite way
+ #ifdef COMPOSITE
+ Picture picture = XRenderCreatePicture( dpy, windowBackingPix, format, CPSubwindowMode, &pa );
+ QRect geom = task->getGeometry();
+
+ double scale = (double)pix->width() / (double)geom.width();
+ XRenderSetPictureFilter( dpy, picture, FilterBilinear, 0, 0 );
+ // Scaling matrix
+ XTransform xform = {{
+ { XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
+ { XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 ) },
+ { XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( scale ) }
+ }};
+
+ XRenderSetPictureTransform( dpy, picture, &xform );
+
+ XRenderComposite( QPaintDevice::x11AppDisplay(),
+ hasAlpha ? PictOpOver : PictOpSrc,
+ picture,
+ None,
+ pix->x11RenderHandle(),
+ 0, 0, 0, 0,
+ 0, 0, pix->width(), pix->height() );
+ #endif
+
+ }
+ else
+ {*/
+ // Scale and render screenshot on scaledScreenshot
+ imlib_context_set_anti_alias(1);
+ imlib_context_set_drawable( screenshot.handle() );
+ Imlib_Image imgOrig = imlib_create_image_from_drawable((Pixmap)0, 0, 0, screenshot.width(), screenshot.height(), 1);
+ imlib_context_set_image( imgOrig );
+ Imlib_Image img = imlib_create_cropped_scaled_image(0, 0, screenshot.width(), screenshot.height(), newSize.width(), newSize.height());
+ imlib_free_image();
+ imlib_context_set_image( img );
+ applyEffect();
+ imlib_context_set_drawable( scaledScreenshot.handle() );
+ imlib_render_image_on_drawable_at_size(0, 0, newSize.width(), newSize.height());
+ imlib_free_image();
+ // }
+ scaledScreenshotDirty = false;
+}
+
+
+
+/**
+ * Called whenever the Window has been activated
+ */
+void KomposeTaskVisualizer::slotTaskActivated()
+{
+ if ( KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite() )
+ {
+ return;
+ }
+
+ if ( KomposeViewManager::instance()->getBlockScreenshots() && !screenshotSuspended )
+ {
+ // Retry 1 sec later
+ screenshotSuspended = true;
+ QTimer::singleShot( 500, this, SLOT( slotTaskActivated() ) );
+ }
+ screenshotSuspended = false;
+
+ // Grab a Passive Screenshot
+ if ( KomposeSettings::instance()->getPassiveScreenshots() &&
+ !KomposeViewManager::instance()->hasActiveView() &&
+ !KomposeViewManager::instance()->getBlockScreenshots() )
+ {
+ kdDebug() << "KomposeTaskVisualizer::slotTaskActivated() (WId " << task->window() << ") - Screenshot already exists, but passive mode on - Grabbing new one." << endl;
+ // Use a timer to make task switching feel more responsive
+ QTimer::singleShot( 300, this, SLOT( captureScreenshot_GrabWindow() ) );
+ //captureScreenshot_GrabWindow();
+ }
+}
+
+
+/**
+ * Called whenever Kompose needs a screenshot to display (normally before a view is shown)
+ */
+void KomposeTaskVisualizer::slotUpdateScreenshot()
+{
+#ifdef COMPOSITE
+ if ( KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite() )
+ {
+ if ( !validBackingPix )
+ {
+ kdDebug() << "KomposeTaskVisualizer::slotUpdateScreenshot() (WId " << task->window() << ") - No backing pixmap referenced. Bad :(" << endl;
+ // When we get here we have never referenced a backingpix...
+ // FIXME: Currently it seems that there is no way to retrieve unmapped backing pixmaps,
+ // even switching desktops won't work due to the latency of XComposite :(
+ }
+ return;
+ }
+#endif
+
+ // If no screenshot exists grab one via activate/raise & capture
+ if ( screenshot.isNull() )
+ {
+ bool iconifyLater = false;
+
+ if ( task->isIconified() == true )
+ {
+ kdDebug() << "KomposeTaskVisualizer::slotUpdateScreenshot() (WId " << task->window() << ") - Window iconified... we have to raise it and iconify it again later." << endl;
+ iconifyLater = true;
+ }
+
+ kdDebug() << "KomposeTaskVisualizer::slotUpdateScreenshot() (WId " << task->window() << ") - Forcing activation (no screenshot exists)" << endl;
+
+ task->activate();
+ QApplication::flushX();
+ QApplication::syncX();
+
+ // Wait until window is fully redrawn
+ struct timespec req, rem;
+ req.tv_sec = 0;
+ req.tv_nsec = KomposeSettings::instance()->getScreenshotGrabDelay();
+ while(nanosleep(&req, &rem))
+ req = rem;
+
+ QApplication::flushX();
+ //task->refresh();
+
+ // Finally capture!
+ screenshot = QPixmap::grabWindow( task->window() );
+ //captureScreenshot_GrabWindow();
+
+ // Restore if formerly iconified
+ if ( iconifyLater )
+ QTimer::singleShot( 1000, task, SLOT( iconify() ) );
+
+ scaledScreenshotDirty = true;
+ }
+}
+
+
+/**
+ * This should be called whenever the window is unmapped as XComposite will reallocate
+ * or the backing pixmap (on resize, minimize, virt desk change, etc)
+ */
+void KomposeTaskVisualizer::updateXCompositeNamedPixmap()
+{
+#ifdef COMPOSITE
+ if ( compositeInit &&
+ KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite())
+ {
+ if( !task->isOnCurrentDesktop() )
+ {
+ kdDebug() << "KomposeTaskVisualizer::updateXCompositeNamedPixmap() (WId " << task->window() << ") - Not reallocationg (unmapped)" << endl;
+ return;
+ }
+
+ kdDebug() << "KomposeTaskVisualizer::updateXCompositeNamedPixmap() (WId " << task->window() << ") - Reallocating backing pixmap" << endl;
+ if ( validBackingPix )
+ XFreePixmap(dpy, windowBackingPix);
+
+ windowBackingPix = XCompositeNameWindowPixmap(dpy, task->wmFrame() );
+
+ validBackingPix = true;
+ scaledScreenshotDirty = true;
+ }
+#endif
+}
+
+
+/**
+ * Initialise Composite backing store for this window
+ */
+void KomposeTaskVisualizer::initXComposite()
+{
+#ifdef COMPOSITE
+ if ( !compositeInit && KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite())
+ {
+ dpy = QPaintDevice::x11AppDisplay();
+
+ connect( task, SIGNAL(x11ConfigureNotify()), this, SLOT(updateXCompositeNamedPixmap()));
+ XSelectInput( dpy, task->wmFrame(), StructureNotifyMask );
+ connect( task, SIGNAL( x11DamageNotify() ), SLOT( setScaledScreenshotDirty() ) );
+
+ XWindowAttributes attr;
+ XGetWindowAttributes( dpy, task->wmFrame(), &attr );
+ format = XRenderFindVisualFormat( dpy, attr.visual );
+ hasAlpha = ( format->type == PictTypeDirect && format->direct.alphaMask ); //FIXME: move this to komposetask
+ // int x = attr.x;
+ // int y = attr.y;
+ // int width = attr.width;
+ // int height = attr.height;
+ pa.subwindow_mode = IncludeInferiors; // Don't clip child widgets
+ compositeInit = true;
+ updateXCompositeNamedPixmap();
+
+ kdDebug() << "KomposeTaskVisualizer::initXComposite() (WId " << task->window() << ") - Setting up Damage extension" << endl;
+ // Create a damage handle for the window, and specify that we want an event whenever the
+ // damage state changes from not damaged to damaged.
+ damage = XDamageCreate( dpy, task->window(), XDamageReportNonEmpty );
+ }
+ else
+ {
+ disconnect( task, SIGNAL(x11ConfigureNotify()), this, SLOT(updateXCompositeNamedPixmap()));
+ disconnect( task, SIGNAL( x11DamageNotify() ), this, SLOT( setScaledScreenshotDirty() ) );
+ if ( compositeInit )
+ {
+ XDamageDestroy( dpy, damage);
+ compositeInit = false;
+ }
+ }
+#endif
+}
+
+
+/**
+ * Grabs a screenshot the old fashioned way
+ */
+void KomposeTaskVisualizer::captureScreenshot_GrabWindow()
+{
+ if ( screenshotBlocked || ( !(task->isActive() && task->isOnTop()) ) )
+ {
+ kdDebug() << "KomposeTaskVisualizer::captureScreenshot_GrabWindow() (WId " << task->window() << ") - Could not grab screenshot." << endl;
+ return;
+ }
+ //task->activate();
+
+
+ // QWidget *rootWin = qApp->desktop();
+ // screenshot = QPixmap::grabWindow( rootWin->winId(), geom.x(), geom.y(), geom.width(), geom.height() );
+
+ screenshot = QPixmap::grabWindow( task->window() );
+ scaledScreenshotDirty = true;
+
+ // We've just grabbed a screenshot and don't want this to happen again in the next 3?! seconds
+ screenshotBlocked = true;
+ QTimer::singleShot( 3000, this, SLOT( enablePasvScreenshots() ) );
+
+ kdDebug() << "KomposeTaskVisualizer::captureScreenshot_GrabWindow() (WId " << task->window() << ") - Grabbed screenshot." << endl;
+
+ // Code to create a screenshot directly as an Imlib image
+
+ // QRect geom = windowInfo.geometry();
+ // Display *disp;
+ // Visual *vis;
+ // Colormap cm;
+ // int screen;
+ //
+ // //get display information
+ // disp = XOpenDisplay(0);
+ // screen = DefaultScreen(disp);
+ // vis = DefaultVisual(disp, screen);
+ // cm = DefaultColormap(disp, screen);
+ //
+ // //set imlib properties
+ // imlib_context_set_display(disp);
+ // imlib_context_set_visual(vis);
+ // imlib_context_set_colormap(cm);
+ // imlib_context_set_drawable(RootWindow(disp, screen));
+ // imlib_context_set_anti_alias(1);
+ // imlib_context_set_blend(0);
+ //
+ // Imlib_Image img = imlib_create_image_from_drawable((Pixmap)0,geom.x(), geom.y(), geom.width(), geom.height(),1);
+ //
+ //
+ // screenshot->setImage( img );
+ //
+ // XCloseDisplay(disp);
+
+ //kdDebug() << "KomposeTaskVisualizer::captureScreenshot() - Created Screenshot: x:%d y:%d size:%dx%d", geom.x(), geom.y(), screenshot->originalWidth(), screenshot->originalHeight() );
+}
+
+void KomposeTaskVisualizer::enablePasvScreenshots()
+{
+ screenshotBlocked = false;
+}
+
+void KomposeTaskVisualizer::clearCached()
+{
+ scaledScreenshot.resize(0,0);
+}
+
+
+void KomposeTaskVisualizer::applyEffect()
+{
+ imlib_context_set_color_modifier(0);
+
+ if ( lasteffect == IEFFECT_MINIMIZED || lasteffect == IEFFECT_MINIMIZED_AND_TITLE )
+ {
+ //FIXME: maybe there is a faster tint filter?!
+ imlib_context_set_color_modifier(cmMinimized);
+ }
+
+ if ( lasteffect == IEFFECT_HIGHLIGHT )
+ {
+ //FIXME: maybe there is a faster tint filter?!
+ imlib_context_set_color_modifier(cmHighlight);
+ }
+
+ if ( lasteffect == IEFFECT_TITLE || lasteffect == IEFFECT_MINIMIZED_AND_TITLE )
+ {
+ /* we can blend stuff now */
+ imlib_context_set_blend(1);
+ /* our color range */
+ Imlib_Color_Range range;
+
+ /* draw a gradient on top of things at the top left of the window */
+ /* create a range */
+ range = imlib_create_color_range();
+ imlib_context_set_color_range(range);
+ imlib_context_set_color(255, 255, 255, 0);
+ imlib_add_color_to_color_range(0);
+ imlib_context_set_color(255, 255, 255, 255);
+ imlib_add_color_to_color_range(1000);
+ /* draw the range */
+ //imlib_context_set_image(myIm);
+ imlib_image_fill_color_range_rectangle(0, 0, scaledScreenshot.width(), KomposeSettings::instance()->getWindowTitleFontAscent() * 3, -180.0);
+ /* free it */
+ imlib_free_color_range();
+ }
+
+}
+
+#include "komposetaskvisualizer.moc"