// // 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 #include #include #include #include #include #include #include "komposetaskvisualizer.h" KomposeTaskVisualizer::KomposeTaskVisualizer(KomposeTask *parent, const char *name) : TQObject(parent, name), task(parent), scaledScreenshotDirty(false), screenshotSuspended(false), screenshotBlocked(false), lasteffect( IEFFECT_NONE ) { #ifdef COMPOSITE validBackingPix = false; compositeInit = false; #endif screenshot.setOptimization( TQPixmap::BestOptim ); scaledScreenshot.setOptimization( TQPixmap::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(TQPixmap* 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() ); // TQPainter 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( TQSize 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 ); TQRect 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( TQPaintDevice::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; TQTimer::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 TQTimer::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(); TQApplication::flushX(); TQApplication::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; TQApplication::flushX(); //task->refresh(); // Finally capture! screenshot = TQPixmap::grabWindow( task->window() ); //captureScreenshot_GrabWindow(); // Restore if formerly iconified if ( iconifyLater ) TQTimer::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 = TQPaintDevice::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(); // TQWidget *rootWin = tqApp->desktop(); // screenshot = TQPixmap::grabWindow( rootWin->winId(), geom.x(), geom.y(), geom.width(), geom.height() ); screenshot = TQPixmap::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; TQTimer::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 // TQRect 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"