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.
1021 lines
37 KiB
1021 lines
37 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org> |
|
Copyright (C) 2009 Fredrik Höglund <fredrik@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
|
|
/* |
|
This is the XRender-based compositing code. The primary compositing |
|
backend is the OpenGL-based one, which should be more powerful |
|
and also possibly better documented. This backend is mostly for cases |
|
when the OpenGL backend cannot be used for some reason (insufficient |
|
performance, no usable OpenGL support at all, etc.) |
|
The plan is to keep it around as long as needed/possible, but if it |
|
proves to be too much hassle it will be dropped in the future. |
|
|
|
Docs: |
|
|
|
XRender (the protocol, but the function calls map to it): |
|
http://gitweb.freedesktop.org/?p=xorg/proto/renderproto.git;a=blob_plain;hb=HEAD;f=renderproto.txt |
|
|
|
XFixes (again, the protocol): |
|
http://gitweb.freedesktop.org/?p=xorg/proto/fixesproto.git;a=blob_plain;hb=HEAD;f=fixesproto.txt |
|
|
|
*/ |
|
|
|
#include "scene_xrender.h" |
|
|
|
#ifdef KWIN_HAVE_XRENDER_COMPOSITING |
|
|
|
#include "toplevel.h" |
|
#include "client.h" |
|
#include "deleted.h" |
|
#include "effects.h" |
|
#include "kwinxrenderutils.h" |
|
|
|
#include <X11/extensions/Xcomposite.h> |
|
|
|
#include <kxerrorhandler.h> |
|
|
|
#include <QtGui/QPainter> |
|
|
|
namespace KWin |
|
{ |
|
|
|
//**************************************** |
|
// SceneXrender |
|
//**************************************** |
|
|
|
// kDebug() support for the XserverRegion type |
|
struct RegionDebug |
|
{ |
|
RegionDebug( XserverRegion r ) : rr( r ) {} |
|
XserverRegion rr; |
|
}; |
|
|
|
QDebug& operator<<( QDebug& stream, RegionDebug r ) |
|
{ |
|
if( r.rr == None ) |
|
return stream << "EMPTY"; |
|
int num; |
|
XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num ); |
|
if( rects == NULL || num == 0 ) |
|
return stream << "EMPTY"; |
|
for( int i = 0; |
|
i < num; |
|
++i ) |
|
stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]"; |
|
return stream; |
|
} |
|
|
|
Picture SceneXrender::buffer = None; |
|
ScreenPaintData SceneXrender::screen_paint; |
|
|
|
SceneXrender::SceneXrender( Workspace* ws ) |
|
: Scene( ws ) |
|
, front( None ) |
|
, init_ok( false ) |
|
{ |
|
if( !Extensions::renderAvailable()) |
|
{ |
|
kError( 1212 ) << "No XRender extension available"; |
|
return; |
|
} |
|
if( !Extensions::fixesRegionAvailable()) |
|
{ |
|
kError( 1212 ) << "No XFixes v3+ extension available"; |
|
return; |
|
} |
|
KXErrorHandler xerr; |
|
if( wspace->createOverlay()) |
|
{ |
|
wspace->setupOverlay( None ); |
|
XWindowAttributes attrs; |
|
XGetWindowAttributes( display(), wspace->overlayWindow(), &attrs ); |
|
format = XRenderFindVisualFormat( display(), attrs.visual ); |
|
if( format == NULL ) |
|
{ |
|
kError( 1212 ) << "Failed to find XRender format for overlay window"; |
|
return; |
|
} |
|
front = XRenderCreatePicture( display(), wspace->overlayWindow(), format, 0, NULL ); |
|
} |
|
else |
|
{ |
|
// create XRender picture for the root window |
|
format = XRenderFindVisualFormat( display(), DefaultVisual( display(), DefaultScreen( display()))); |
|
if( format == NULL ) |
|
{ |
|
kError( 1212 ) << "Failed to find XRender format for root window"; |
|
return; // error |
|
} |
|
XRenderPictureAttributes pa; |
|
pa.subwindow_mode = IncludeInferiors; |
|
front = XRenderCreatePicture( display(), rootWindow(), format, CPSubwindowMode, &pa ); |
|
} |
|
createBuffer(); |
|
if( xerr.error( true )) |
|
{ |
|
kError( 1212 ) << "XRender compositing setup failed"; |
|
return; |
|
} |
|
if( !initting ) // see comment for opengl version |
|
{ |
|
if( !selfCheck()) |
|
return; |
|
selfCheckDone = true; |
|
} |
|
init_ok = true; |
|
} |
|
|
|
SceneXrender::~SceneXrender() |
|
{ |
|
if( !init_ok ) |
|
{ |
|
// TODO this probably needs to clean up whatever has been created until the failure |
|
wspace->destroyOverlay(); |
|
return; |
|
} |
|
XRenderFreePicture( display(), front ); |
|
XRenderFreePicture( display(), buffer ); |
|
buffer = None; |
|
wspace->destroyOverlay(); |
|
foreach( Window* w, windows ) |
|
delete w; |
|
} |
|
|
|
bool SceneXrender::initFailed() const |
|
{ |
|
return !init_ok; |
|
} |
|
|
|
// Create the compositing buffer. The root window is not double-buffered, |
|
// so it is done manually using this buffer, |
|
void SceneXrender::createBuffer() |
|
{ |
|
Pixmap pixmap = XCreatePixmap( display(), rootWindow(), displayWidth(), displayHeight(), DefaultDepth( display(), DefaultScreen( display()))); |
|
buffer = XRenderCreatePicture( display(), pixmap, format, 0, 0 ); |
|
XFreePixmap( display(), pixmap ); // The picture owns the pixmap now |
|
} |
|
|
|
// Just like SceneOpenGL::selfCheck() |
|
bool SceneXrender::selfCheck() |
|
{ |
|
QRegion reg = selfCheckRegion(); |
|
if( wspace->overlayWindow()) |
|
{ // avoid covering the whole screen too soon |
|
wspace->setOverlayShape( reg ); |
|
wspace->showOverlay(); |
|
} |
|
selfCheckSetup(); |
|
flushBuffer( PAINT_SCREEN_REGION, reg ); |
|
bool ok = selfCheckFinish(); |
|
if( wspace->overlayWindow()) |
|
wspace->hideOverlay(); |
|
return ok; |
|
} |
|
|
|
void SceneXrender::selfCheckSetup() |
|
{ |
|
KXErrorHandler err; |
|
QImage img( selfCheckWidth(), selfCheckHeight(), QImage::Format_RGB32 ); |
|
img.setPixel( 0, 0, QColor( Qt::red ).rgb()); |
|
img.setPixel( 1, 0, QColor( Qt::green ).rgb()); |
|
img.setPixel( 2, 0, QColor( Qt::blue ).rgb()); |
|
img.setPixel( 0, 1, QColor( Qt::white ).rgb()); |
|
img.setPixel( 1, 1, QColor( Qt::black ).rgb()); |
|
img.setPixel( 2, 1, QColor( Qt::white ).rgb()); |
|
QPixmap pix = QPixmap::fromImage( img ); |
|
foreach( const QPoint& p, selfCheckPoints()) |
|
{ |
|
XSetWindowAttributes wa; |
|
wa.override_redirect = True; |
|
::Window window = XCreateWindow( display(), rootWindow(), 0, 0, selfCheckWidth(), selfCheckHeight(), |
|
0, QX11Info::appDepth(), CopyFromParent, CopyFromParent, CWOverrideRedirect, &wa ); |
|
XSetWindowBackgroundPixmap( display(), window, pix.handle()); |
|
XClearWindow( display(), window ); |
|
XMapWindow( display(), window ); |
|
// move the window one down to where the result will be rendered too, just in case |
|
// the render would fail completely and eventual check would try to read this window's contents |
|
XMoveWindow( display(), window, p.x() + 1, p.y()); |
|
XCompositeRedirectWindow( display(), window, CompositeRedirectAutomatic ); |
|
Pixmap wpix = XCompositeNameWindowPixmap( display(), window ); |
|
XWindowAttributes attrs; |
|
XGetWindowAttributes( display(), window, &attrs ); |
|
XRenderPictFormat* format = XRenderFindVisualFormat( display(), attrs.visual ); |
|
Picture pic = XRenderCreatePicture( display(), wpix, format, 0, 0 ); |
|
QRect rect( p.x(), p.y(), selfCheckWidth(), selfCheckHeight()); |
|
XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0, |
|
rect.x(), rect.y(), rect.width(), rect.height()); |
|
XRenderFreePicture( display(), pic ); |
|
XFreePixmap( display(), wpix ); |
|
XDestroyWindow( display(), window ); |
|
} |
|
err.error( true ); // just sync and discard |
|
} |
|
|
|
bool SceneXrender::selfCheckFinish() |
|
{ |
|
KXErrorHandler err; |
|
bool ok = true; |
|
foreach( const QPoint& p, selfCheckPoints()) |
|
{ |
|
QPixmap pix = QPixmap::grabWindow( rootWindow(), p.x(), p.y(), selfCheckWidth(), selfCheckHeight()); |
|
QImage img = pix.toImage(); |
|
// kDebug(1212) << "P:" << QColor( img.pixel( 0, 0 )).name(); |
|
// kDebug(1212) << "P:" << QColor( img.pixel( 1, 0 )).name(); |
|
// kDebug(1212) << "P:" << QColor( img.pixel( 2, 0 )).name(); |
|
// kDebug(1212) << "P:" << QColor( img.pixel( 0, 1 )).name(); |
|
// kDebug(1212) << "P:" << QColor( img.pixel( 1, 1 )).name(); |
|
// kDebug(1212) << "P:" << QColor( img.pixel( 2, 1 )).name(); |
|
if( img.pixel( 0, 0 ) != QColor( Qt::red ).rgb() |
|
|| img.pixel( 1, 0 ) != QColor( Qt::green ).rgb() |
|
|| img.pixel( 2, 0 ) != QColor( Qt::blue ).rgb() |
|
|| img.pixel( 0, 1 ) != QColor( Qt::white ).rgb() |
|
|| img.pixel( 1, 1 ) != QColor( Qt::black ).rgb() |
|
|| img.pixel( 2, 1 ) != QColor( Qt::white ).rgb()) |
|
{ |
|
kError( 1212 ) << "XRender compositing self-check failed, disabling compositing."; |
|
ok = false; |
|
break; |
|
} |
|
} |
|
if( err.error( true )) |
|
ok = false; |
|
if( ok ) |
|
kDebug( 1212 ) << "XRender compositing self-check passed."; |
|
if( !ok && options->disableCompositingChecks ) |
|
{ |
|
kWarning( 1212 ) << "Compositing checks disabled, proceeding regardless of self-check failure."; |
|
return true; |
|
} |
|
return ok; |
|
} |
|
|
|
|
|
// the entry point for painting |
|
void SceneXrender::paint( QRegion damage, ToplevelList toplevels ) |
|
{ |
|
foreach( Toplevel* c, toplevels ) |
|
{ |
|
assert( windows.contains( c )); |
|
stacking_order.append( windows[ c ] ); |
|
} |
|
int mask = 0; |
|
paintScreen( &mask, &damage ); |
|
if( wspace->overlayWindow()) // show the window only after the first pass, since |
|
wspace->showOverlay(); // that pass may take long |
|
if( !selfCheckDone ) |
|
{ |
|
selfCheckSetup(); |
|
damage |= selfCheckRegion(); |
|
} |
|
flushBuffer( mask, damage ); |
|
if( !selfCheckDone ) |
|
{ |
|
if( !selfCheckFinish()) |
|
QTimer::singleShot( 0, Workspace::self(), SLOT( finishCompositing())); |
|
selfCheckDone = true; |
|
} |
|
// do cleanup |
|
stacking_order.clear(); |
|
} |
|
|
|
void SceneXrender::flushBuffer( int mask, QRegion damage ) |
|
{ |
|
if( mask & PAINT_SCREEN_REGION ) |
|
{ |
|
// Use the damage region as the clip region for the root window |
|
XserverRegion front_region = toXserverRegion( damage ); |
|
XFixesSetPictureClipRegion( display(), front, 0, 0, front_region ); |
|
XFixesDestroyRegion( display(), front_region ); |
|
// copy composed buffer to the root window |
|
XFixesSetPictureClipRegion( display(), buffer, 0, 0, None ); |
|
XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight()); |
|
XFixesSetPictureClipRegion( display(), front, 0, 0, None ); |
|
XFlush( display()); |
|
} |
|
else |
|
{ |
|
// copy composed buffer to the root window |
|
XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight()); |
|
XFlush( display()); |
|
} |
|
} |
|
|
|
void SceneXrender::paintGenericScreen( int mask, ScreenPaintData data ) |
|
{ |
|
screen_paint = data; // save, transformations will be done when painting windows |
|
if( true ) // as long as paintTransformedScreen() doesn't work properly |
|
Scene::paintGenericScreen( mask, data ); |
|
else |
|
paintTransformedScreen( mask ); |
|
} |
|
|
|
/* |
|
TODO currently broken |
|
Try to do optimized painting even with transformations. Since only scaling |
|
and translation are supported by the painting code, clipping can be done |
|
manually to avoid having to paint everything in every pass. Whole screen |
|
still need to be painted but e.g. obscured windows don't. So this below |
|
is basically paintSimpleScreen() with extra code to compute clipping correctly. |
|
|
|
This code assumes that the only transformations possible with XRender are those |
|
provided by Window/ScreenPaintData, In the (very unlikely?) case more is needed |
|
then paintGenericScreen() needs to be used. |
|
*/ |
|
void SceneXrender::paintTransformedScreen( int orig_mask ) |
|
{ |
|
QRegion region( 0, 0, displayWidth(), displayHeight()); |
|
QList< Phase2Data > phase2; |
|
QRegion allclips; |
|
// Draw each opaque window top to bottom, subtracting the bounding rect of |
|
// each window from the clip region after it's been drawn. |
|
for( int i = stacking_order.count() - 1; // top to bottom |
|
i >= 0; |
|
--i ) |
|
{ |
|
Window* w = static_cast< Window* >( stacking_order[ i ] ); |
|
WindowPrePaintData data; |
|
data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT ); |
|
w->resetPaintingEnabled(); |
|
data.paint = region; |
|
// TODO this is wrong, transformedShape() should be used here, but is not known yet |
|
data.clip = w->isOpaque() ? region : QRegion(); |
|
data.quads = w->buildQuads(); |
|
// preparation step |
|
effects->prePaintWindow( effectWindow( w ), data, time_diff ); |
|
#ifndef NDEBUG |
|
foreach( const WindowQuad &q, data.quads ) |
|
if( q.isTransformed()) |
|
kFatal( 1212 ) << "Pre-paint calls are not allowed to transform quads!" ; |
|
#endif |
|
if( !w->isPaintingEnabled()) |
|
continue; |
|
data.paint -= allclips; // make sure to avoid already clipped areas |
|
if( data.paint.isEmpty()) // completely clipped |
|
continue; |
|
if( data.paint != region ) // prepaint added area to draw |
|
{ |
|
region |= data.paint; // make sure other windows in that area get painted too |
|
painted_region |= data.paint; // make sure it makes it to the screen |
|
} |
|
// If the window is transparent, the transparent part will be done |
|
// in the 2nd pass. |
|
if( data.mask & PAINT_WINDOW_TRANSLUCENT ) |
|
phase2.prepend( Phase2Data( w, data.paint, data.clip, data.mask, data.quads )); |
|
if( data.mask & PAINT_WINDOW_OPAQUE ) |
|
{ |
|
w->setTransformedShape( QRegion()); |
|
paintWindow( w, data.mask, data.paint, data.quads ); |
|
// The window can clip by its opaque parts the windows below. |
|
region -= w->transformedShape(); |
|
} |
|
// translucency or window transformed require window pixmap |
|
w->suspendUnredirect( data.mask & ( PAINT_WINDOW_TRANSLUCENT | PAINT_WINDOW_TRANSFORMED )); |
|
} |
|
if( !( orig_mask & PAINT_SCREEN_BACKGROUND_FIRST )) |
|
paintBackground( region ); // Fill any areas of the root window not covered by windows |
|
// Now walk the list bottom to top, drawing translucent windows. |
|
// That we draw bottom to top is important now since we're drawing translucent objects |
|
// and also are clipping only by opaque windows. |
|
QRegion add_paint; |
|
foreach( const Phase2Data &d, phase2 ) |
|
{ |
|
Scene::Window* w = d.window; |
|
paintWindow( w, d.mask, d.region | add_paint, d.quads ); |
|
// It is necessary to also add paint regions of windows below, because their |
|
// pre-paint's might have extended the paint area, so those areas need to be painted too. |
|
add_paint |= d.region; |
|
} |
|
} |
|
|
|
// fill the screen background |
|
void SceneXrender::paintBackground( QRegion region ) |
|
{ |
|
PaintClipper pc( region ); |
|
for( PaintClipper::Iterator iterator; |
|
!iterator.isDone(); |
|
iterator.next()) |
|
{ |
|
XRenderColor col = { 0, 0, 0, 0xffff }; // black |
|
XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight()); |
|
} |
|
} |
|
|
|
void SceneXrender::windowGeometryShapeChanged( Toplevel* c ) |
|
{ |
|
if( !windows.contains( c )) // this is ok, shape is not valid by default |
|
return; |
|
Window* w = windows[ c ]; |
|
w->discardPicture(); |
|
w->discardShape(); |
|
w->discardAlpha(); |
|
} |
|
|
|
void SceneXrender::windowOpacityChanged( Toplevel* c ) |
|
{ |
|
if( !windows.contains( c )) // this is ok, alpha is created on demand |
|
return; |
|
Window* w = windows[ c ]; |
|
w->discardAlpha(); |
|
} |
|
|
|
void SceneXrender::windowClosed( Toplevel* c, Deleted* deleted ) |
|
{ |
|
assert( windows.contains( c )); |
|
if( deleted != NULL ) |
|
{ // replace c with deleted |
|
Window* w = windows.take( c ); |
|
w->updateToplevel( deleted ); |
|
windows[ deleted ] = w; |
|
} |
|
else |
|
{ |
|
delete windows.take( c ); |
|
c->effectWindow()->setSceneWindow( NULL ); |
|
} |
|
} |
|
|
|
void SceneXrender::windowDeleted( Deleted* c ) |
|
{ |
|
assert( windows.contains( c )); |
|
delete windows.take( c ); |
|
c->effectWindow()->setSceneWindow( NULL ); |
|
} |
|
|
|
void SceneXrender::windowAdded( Toplevel* c ) |
|
{ |
|
assert( !windows.contains( c )); |
|
windows[ c ] = new Window( c ); |
|
c->effectWindow()->setSceneWindow( windows[ c ]); |
|
} |
|
|
|
//**************************************** |
|
// SceneXrender::Window |
|
//**************************************** |
|
|
|
QPixmap *SceneXrender::Window::temp_pixmap = 0; |
|
|
|
SceneXrender::Window::Window( Toplevel* c ) |
|
: Scene::Window( c ) |
|
, _picture( None ) |
|
, format( XRenderFindVisualFormat( display(), c->visual())) |
|
, alpha( None ) |
|
, alpha_cached_opacity(0.0) |
|
{ |
|
} |
|
|
|
SceneXrender::Window::~Window() |
|
{ |
|
discardPicture(); |
|
discardAlpha(); |
|
discardShape(); |
|
} |
|
|
|
// Create XRender picture for the pixmap with the window contents. |
|
Picture SceneXrender::Window::picture() |
|
{ |
|
if( !toplevel->damage().isEmpty() && _picture != None ) |
|
{ |
|
XRenderFreePicture( display(), _picture ); |
|
_picture = None; |
|
} |
|
if( _picture == None && format != NULL ) |
|
{ |
|
// Get the pixmap with the window contents. |
|
Pixmap pix = toplevel->windowPixmap(); |
|
if( pix == None ) |
|
return None; |
|
_picture = XRenderCreatePicture( display(), pix, format, 0, 0 ); |
|
toplevel->resetDamage( toplevel->rect()); |
|
} |
|
return _picture; |
|
} |
|
|
|
|
|
void SceneXrender::Window::discardPicture() |
|
{ |
|
if( _picture != None ) |
|
XRenderFreePicture( display(), _picture ); |
|
_picture = None; |
|
} |
|
|
|
void SceneXrender::Window::discardAlpha() |
|
{ |
|
if( alpha != None ) |
|
XRenderFreePicture( display(), alpha ); |
|
alpha = None; |
|
} |
|
|
|
// Create XRender picture for the alpha mask. |
|
Picture SceneXrender::Window::alphaMask( double opacity ) |
|
{ |
|
if( isOpaque() && qFuzzyCompare( opacity, 1.0 ) ) |
|
return None; |
|
|
|
bool created = false; |
|
if( alpha == None ) |
|
{ |
|
// Create a 1x1 8bpp pixmap containing the given opacity in the alpha channel. |
|
Pixmap pixmap = XCreatePixmap( display(), rootWindow(), 1, 1, 8 ); |
|
XRenderPictFormat* format = XRenderFindStandardFormat( display(), PictStandardA8 ); |
|
XRenderPictureAttributes pa; |
|
pa.repeat = True; |
|
alpha = XRenderCreatePicture( display(), pixmap, format, CPRepeat, &pa ); |
|
XFreePixmap( display(), pixmap ); |
|
created = true; |
|
} |
|
if( created || !qFuzzyCompare( alpha_cached_opacity + 1.0, opacity + 1.0 ) ) |
|
{ |
|
XRenderColor col; |
|
col.alpha = int( opacity * 0xffff ); |
|
XRenderFillRectangle( display(), PictOpSrc, alpha, &col, 0, 0, 1, 1 ); |
|
alpha_cached_opacity = opacity; |
|
} |
|
return alpha; |
|
} |
|
|
|
// Maps window coordinates to screen coordinates |
|
QRect SceneXrender::Window::mapToScreen( int mask, const WindowPaintData &data, const QRect &rect ) const |
|
{ |
|
QRect r = rect; |
|
|
|
if( mask & PAINT_WINDOW_TRANSFORMED ) |
|
{ |
|
// Apply the window transformation |
|
r.moveTo( r.x() * data.xScale + data.xTranslate, |
|
r.y() * data.yScale + data.yTranslate ); |
|
r.setWidth( r.width() * data.xScale ); |
|
r.setHeight( r.height() * data.yScale ); |
|
} |
|
|
|
// Move the rectangle to the screen position |
|
r.translate( x(), y() ); |
|
|
|
if( mask & PAINT_SCREEN_TRANSFORMED ) |
|
{ |
|
// Apply the screen transformation |
|
r.moveTo( r.x() * screen_paint.xScale + screen_paint.xTranslate, |
|
r.y() * screen_paint.yScale + screen_paint.yTranslate ); |
|
r.setWidth( r.width() * screen_paint.xScale ); |
|
r.setHeight( r.height() * screen_paint.yScale ); |
|
} |
|
|
|
return r; |
|
} |
|
|
|
// Maps window coordinates to screen coordinates |
|
QPoint SceneXrender::Window::mapToScreen( int mask, const WindowPaintData &data, const QPoint &point ) const |
|
{ |
|
QPoint pt = point; |
|
|
|
if( mask & PAINT_WINDOW_TRANSFORMED ) |
|
{ |
|
// Apply the window transformation |
|
pt.rx() = pt.x() * data.xScale + data.xTranslate; |
|
pt.ry() = pt.y() * data.yScale + data.yTranslate; |
|
} |
|
|
|
// Move the point to the screen position |
|
pt += QPoint(x(), y()); |
|
|
|
if( mask & PAINT_SCREEN_TRANSFORMED ) |
|
{ |
|
// Apply the screen transformation |
|
pt.rx() = pt.x() * screen_paint.xScale + screen_paint.xTranslate; |
|
pt.ry() = pt.y() * screen_paint.yScale + screen_paint.yTranslate; |
|
} |
|
|
|
return pt; |
|
} |
|
|
|
void SceneXrender::Window::prepareTempPixmap(const QPixmap *left, const QPixmap *top, |
|
const QPixmap *right, const QPixmap *bottom) |
|
{ |
|
const QRect r = static_cast<Client*>( toplevel )->decorationRect(); |
|
|
|
if( !temp_pixmap ) |
|
temp_pixmap = new QPixmap( r.width(), r.height() ); |
|
else if( temp_pixmap->width() < r.width() || temp_pixmap->height() < r.height() ) |
|
*temp_pixmap = QPixmap( r.width(), r.height() ); |
|
|
|
temp_pixmap->fill( Qt::transparent ); |
|
|
|
Display *dpy = display(); |
|
XRenderComposite( dpy, PictOpSrc, top->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), |
|
0, 0, 0, 0, 0, 0, top->width(), top->height() ); |
|
XRenderComposite( dpy, PictOpSrc, left->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), |
|
0, 0, 0, 0, 0, top->height(), left->width(), left->height() ); |
|
XRenderComposite( dpy, PictOpSrc, right->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), |
|
0, 0, 0, 0, r.width() - right->width(), top->height(), right->width(), right->height() ); |
|
XRenderComposite( dpy, PictOpSrc, bottom->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(), |
|
0, 0, 0, 0, 0, r.height() - bottom->height(), bottom->width(), bottom->height() ); |
|
} |
|
|
|
// paint the window |
|
void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintData data ) |
|
{ |
|
setTransformedShape( QRegion()); // maybe nothing will be painted |
|
// check if there is something to paint |
|
bool opaque = isOpaque() && qFuzzyCompare( data.opacity, 1.0 ); |
|
/* HACK: It seems this causes painting glitches, disable temporarily |
|
if(( mask & PAINT_WINDOW_OPAQUE ) ^ ( mask & PAINT_WINDOW_TRANSLUCENT )) |
|
{ // We are only painting either opaque OR translucent windows, not both |
|
if( mask & PAINT_WINDOW_OPAQUE && !opaque ) |
|
return; // Only painting opaque and window is translucent |
|
if( mask & PAINT_WINDOW_TRANSLUCENT && opaque ) |
|
return; // Only painting translucent and window is opaque |
|
}*/ |
|
Picture pic = picture(); // get XRender picture |
|
if( pic == None ) // The render format can be null for GL and/or Xv visuals |
|
return; |
|
// set picture filter |
|
if( options->xrenderSmoothScale ) // only when forced, it's slow |
|
{ |
|
if( mask & PAINT_WINDOW_TRANSFORMED ) |
|
filter = ImageFilterGood; |
|
else if( mask & PAINT_SCREEN_TRANSFORMED ) |
|
filter = ImageFilterGood; |
|
else |
|
filter = ImageFilterFast; |
|
} |
|
else |
|
filter = ImageFilterFast; |
|
// do required transformations |
|
const QRect wr = mapToScreen(mask, data, QRect(0, 0, width(), height())); |
|
const QRect cr = QRect(toplevel->clientPos(), toplevel->clientSize()); // Client rect (in the window) |
|
const QRect dr = mapToScreen(mask, data, cr); // Destination rect |
|
double xscale = 1; |
|
double yscale = 1; |
|
bool scaled = false; |
|
|
|
Client *client = dynamic_cast<Client*>( toplevel ); |
|
Deleted *deleted = dynamic_cast<Deleted*>( toplevel ); |
|
if ( client && Workspace::self()->decorationHasAlpha() ) |
|
transformed_shape = QRegion( client->decorationRect() ); |
|
else if( deleted && Workspace::self()->decorationHasAlpha() ) |
|
transformed_shape = QRegion( deleted->decorationRect() ); |
|
else |
|
transformed_shape = shape(); |
|
|
|
XTransform xform = {{ |
|
{ XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) }, |
|
{ XDoubleToFixed( 0 ), XDoubleToFixed( 1), XDoubleToFixed( 0 ) }, |
|
{ XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) } |
|
}}; |
|
|
|
XTransform identity = {{ |
|
{ XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) }, |
|
{ XDoubleToFixed( 0 ), XDoubleToFixed( 1), XDoubleToFixed( 0 ) }, |
|
{ XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) } |
|
}}; |
|
|
|
XRenderPictureAttributes attr; |
|
|
|
if( mask & PAINT_WINDOW_TRANSFORMED ) |
|
{ |
|
xscale = data.xScale; |
|
yscale = data.yScale; |
|
} |
|
if( mask & PAINT_SCREEN_TRANSFORMED ) |
|
{ |
|
xscale *= screen_paint.xScale; |
|
yscale *= screen_paint.yScale; |
|
} |
|
if( !qFuzzyCompare( xscale, 1.0 ) || !qFuzzyCompare( yscale, 1.0 ) ) |
|
{ |
|
scaled = true; |
|
xform.matrix[0][0] = XDoubleToFixed(1.0 / xscale); |
|
xform.matrix[1][1] = XDoubleToFixed(1.0 / yscale); |
|
|
|
XRenderSetPictureTransform( display(), pic, &xform ); |
|
if( filter == ImageFilterGood ) |
|
XRenderSetPictureFilter( display(), pic, const_cast< char* >( "good" ), NULL, 0 ); |
|
|
|
// This is needed to avoid hitting a fallback in the radeon driver. |
|
// The Render specification states that sampling pixels outside the |
|
// source picture results in alpha=0 pixels. This can be achieved by |
|
// setting the border color to transparent black, but since the border |
|
// color has the same format as the texture, it only works when the |
|
// texture has an alpha channel. So the driver falls back to software |
|
// when the repeat mode is RepeatNone, the picture has a non-identity |
|
// transformation matrix, and doesn't have an alpha channel. |
|
// Since we only scale the picture, we can work around this by setting |
|
// the repeat mode to RepeatPad. |
|
if (!window()->hasAlpha()) |
|
{ |
|
attr.repeat = RepeatPad; |
|
XRenderChangePicture( display(), pic, CPRepeat, &attr ); |
|
} |
|
|
|
// transform the shape for clipping in paintTransformedScreen() |
|
QVector< QRect > rects = transformed_shape.rects(); |
|
for( int i = 0; |
|
i < rects.count(); |
|
++i ) |
|
{ |
|
QRect& r = rects[ i ]; |
|
r = QRect( int( r.x() * xscale ), int( r.y() * yscale ), |
|
int( r.width() * xscale ), int( r.height() * xscale )); |
|
} |
|
transformed_shape.setRects( rects.constData(), rects.count()); |
|
} |
|
|
|
transformed_shape.translate( mapToScreen( mask, data, QPoint(0, 0) ) ); |
|
PaintClipper pcreg( region ); // clip by the region to paint |
|
PaintClipper pc( transformed_shape ); // clip by window's shape |
|
for( PaintClipper::Iterator iterator; |
|
!iterator.isDone(); |
|
iterator.next()) |
|
{ |
|
if( client || deleted ) |
|
{ |
|
bool noBorder = true; |
|
const QPixmap *left = NULL; |
|
const QPixmap *top = NULL; |
|
const QPixmap *right = NULL; |
|
const QPixmap *bottom = NULL; |
|
QRect tr, lr, rr, br; |
|
QRect decorationRect; |
|
if( client && !client->noBorder() ) |
|
{ |
|
noBorder = client->noBorder(); |
|
client->ensureDecorationPixmapsPainted(); |
|
|
|
left = client->leftDecoPixmap(); |
|
top = client->topDecoPixmap(); |
|
right = client->rightDecoPixmap(); |
|
bottom = client->bottomDecoPixmap(); |
|
client->layoutDecorationRects( lr, tr, rr, br, Client::WindowRelative ); |
|
decorationRect = client->decorationRect(); |
|
} |
|
if( deleted && !deleted->noBorder() ) |
|
{ |
|
noBorder = deleted->noBorder(); |
|
left = deleted->leftDecoPixmap(); |
|
top = deleted->topDecoPixmap(); |
|
right = deleted->rightDecoPixmap(); |
|
bottom = deleted->bottomDecoPixmap(); |
|
deleted->layoutDecorationRects( lr, tr, rr, br ); |
|
decorationRect = deleted->decorationRect(); |
|
} |
|
if( !noBorder ) |
|
{ |
|
// Paint the decoration |
|
Picture alpha = alphaMask( data.opacity * data.decoration_opacity ); |
|
Display *dpy = display(); |
|
|
|
if( !scaled ) |
|
{ |
|
tr = mapToScreen( mask, data, tr ); |
|
lr = mapToScreen( mask, data, lr ); |
|
rr = mapToScreen( mask, data, rr ); |
|
br = mapToScreen( mask, data, br ); |
|
|
|
XRenderComposite( dpy, PictOpOver, top->x11PictureHandle(), alpha, buffer, |
|
0, 0, 0, 0, tr.x(), tr.y(), tr.width(), tr.height()); |
|
XRenderComposite( dpy, PictOpOver, left->x11PictureHandle(), alpha, buffer, |
|
0, 0, 0, 0, lr.x(), lr.y(), lr.width(), lr.height()); |
|
XRenderComposite( dpy, PictOpOver, right->x11PictureHandle(), alpha, buffer, |
|
0, 0, 0, 0, rr.x(), rr.y(), rr.width(), rr.height()); |
|
XRenderComposite( dpy, PictOpOver, bottom->x11PictureHandle(), alpha, buffer, |
|
0, 0, 0, 0, br.x(), br.y(), br.width(), br.height()); |
|
} |
|
else |
|
{ |
|
const QRect r = mapToScreen( mask, data, decorationRect ); |
|
prepareTempPixmap( left, top, right, bottom ); |
|
XRenderSetPictureTransform( dpy, temp_pixmap->x11PictureHandle(), &xform ); |
|
XRenderComposite( dpy, PictOpOver, temp_pixmap->x11PictureHandle(), alpha, buffer, |
|
0, 0, 0, 0, r.x(), r.y(), r.width(), r.height() ); |
|
XRenderSetPictureTransform( dpy, temp_pixmap->x11PictureHandle(), &identity ); |
|
} |
|
} |
|
} |
|
if ( !(mask & PAINT_DECORATION_ONLY) ) |
|
{ |
|
// Paint the window contents |
|
if( opaque ) |
|
{ |
|
XRenderComposite( display(), PictOpSrc, pic, None, buffer, cr.x() * xscale, cr.y() * yscale, |
|
0, 0, dr.x(), dr.y(), dr.width(), dr.height()); |
|
} |
|
else |
|
{ |
|
Picture alpha = alphaMask( data.opacity ); |
|
XRenderComposite( display(), PictOpOver, pic, alpha, buffer, cr.x() * xscale, cr.y() * yscale, |
|
0, 0, dr.x(), dr.y(), dr.width(), dr.height()); |
|
transformed_shape = QRegion(); |
|
} |
|
} |
|
|
|
if( data.brightness < 1.0 ) |
|
{ |
|
// fake brightness change by overlaying black |
|
XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) * data.opacity }; |
|
XRenderFillRectangle( display(), PictOpOver, buffer, &col, wr.x(), wr.y(), wr.width(), wr.height() ); |
|
} |
|
} |
|
if( scaled ) |
|
{ |
|
XRenderSetPictureTransform( display(), pic, &identity ); |
|
if( filter == ImageFilterGood ) |
|
XRenderSetPictureFilter( display(), pic, const_cast< char* >( "fast" ), NULL, 0 ); |
|
if (!window()->hasAlpha()) |
|
{ |
|
attr.repeat = RepeatNone; |
|
XRenderChangePicture( display(), pic, CPRepeat, &attr ); |
|
} |
|
} |
|
} |
|
|
|
//**************************************** |
|
// SceneXrender::EffectFrame |
|
//**************************************** |
|
|
|
SceneXrender::EffectFrame::EffectFrame( EffectFrameImpl* frame ) |
|
: Scene::EffectFrame( frame ) |
|
{ |
|
m_picture = NULL; |
|
m_textPicture = NULL; |
|
m_iconPicture = NULL; |
|
m_selectionPicture = NULL; |
|
} |
|
|
|
SceneXrender::EffectFrame::~EffectFrame() |
|
{ |
|
delete m_picture; |
|
delete m_textPicture; |
|
delete m_iconPicture; |
|
delete m_selectionPicture; |
|
} |
|
|
|
void SceneXrender::EffectFrame::free() |
|
{ |
|
delete m_picture; |
|
m_picture = NULL; |
|
delete m_textPicture; |
|
m_textPicture = NULL; |
|
delete m_iconPicture; |
|
m_iconPicture = NULL; |
|
delete m_selectionPicture; |
|
m_selectionPicture = NULL; |
|
} |
|
|
|
void SceneXrender::EffectFrame::freeIconFrame() |
|
{ |
|
delete m_iconPicture; |
|
m_iconPicture = NULL; |
|
} |
|
|
|
void SceneXrender::EffectFrame::freeTextFrame() |
|
{ |
|
delete m_textPicture; |
|
m_textPicture = NULL; |
|
} |
|
|
|
void SceneXrender::EffectFrame::freeSelection() |
|
{ |
|
delete m_selectionPicture; |
|
m_selectionPicture = NULL; |
|
} |
|
|
|
void SceneXrender::EffectFrame::crossFadeIcon() |
|
{ |
|
// TODO: implement me |
|
} |
|
|
|
void SceneXrender::EffectFrame::crossFadeText() |
|
{ |
|
// TODO: implement me |
|
} |
|
|
|
void SceneXrender::EffectFrame::render( QRegion region, double opacity, double frameOpacity ) |
|
{ |
|
if( m_effectFrame->geometry().isEmpty() ) |
|
{ |
|
return; // Nothing to display |
|
} |
|
|
|
// Render the actual frame |
|
if( m_effectFrame->style() == EffectFrameUnstyled ) |
|
xRenderRoundBox( effects->xrenderBufferPicture(), m_effectFrame->geometry().adjusted( -5, -5, 5, 5 ), |
|
5, QColor( 0, 0, 0, int( opacity * frameOpacity * 255 ))); |
|
else if( m_effectFrame->style() == EffectFrameStyled ) |
|
{ |
|
if( !m_picture ) // Lazy creation |
|
{ |
|
updatePicture(); |
|
} |
|
qreal left, top, right, bottom; |
|
m_effectFrame->frame().getMargins( left, top, right, bottom ); // m_geometry is the inner geometry |
|
QRect geom = m_effectFrame->geometry().adjusted( -left, -top, right, bottom ); |
|
XRenderComposite( display(), PictOpOver, *m_picture, None, effects->xrenderBufferPicture(), |
|
0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height() ); |
|
|
|
if( !m_effectFrame->selection().isNull() ) |
|
{ |
|
if( !m_selectionPicture ) // Lazy creation |
|
{ |
|
m_selectionPicture = new XRenderPicture( m_effectFrame->selectionFrame().framePixmap() ); |
|
} |
|
geom = m_effectFrame->selection(); |
|
XRenderComposite( display(), PictOpOver, *m_selectionPicture, None, effects->xrenderBufferPicture(), |
|
0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height() ); |
|
} |
|
} |
|
|
|
XRenderPicture fill = xRenderBlendPicture(opacity); |
|
|
|
// Render icon |
|
if( !m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty() ) |
|
{ |
|
QPoint topLeft( m_effectFrame->geometry().x(), m_effectFrame->geometry().center().y() - m_effectFrame->iconSize().height() / 2 ); |
|
|
|
if( !m_iconPicture ) // lazy creation |
|
m_iconPicture = new XRenderPicture( m_effectFrame->icon() ); |
|
QRect geom = QRect( topLeft, m_effectFrame->iconSize() ); |
|
XRenderComposite( display(), PictOpOver, *m_iconPicture, fill, effects->xrenderBufferPicture(), |
|
0, 0, 0, 0, geom.x(), geom.y(), geom.width(), geom.height() ); |
|
} |
|
|
|
// Render text |
|
if( !m_effectFrame->text().isEmpty() ) |
|
{ |
|
if( !m_textPicture ) // Lazy creation |
|
{ |
|
updateTextPicture(); |
|
} |
|
XRenderComposite( display(), PictOpOver, *m_textPicture, fill, effects->xrenderBufferPicture(), |
|
0, 0, 0, 0, m_effectFrame->geometry().x(), m_effectFrame->geometry().y(), |
|
m_effectFrame->geometry().width(), m_effectFrame->geometry().height() ); |
|
} |
|
} |
|
|
|
void SceneXrender::EffectFrame::updatePicture() |
|
{ |
|
delete m_picture; |
|
if( m_effectFrame->style() == EffectFrameStyled ) |
|
m_picture = new XRenderPicture( m_effectFrame->frame().framePixmap() ); |
|
} |
|
|
|
void SceneXrender::EffectFrame::updateTextPicture() |
|
{ // Mostly copied from SceneOpenGL::EffectFrame::updateTextTexture() above |
|
delete m_textPicture; |
|
|
|
if( m_effectFrame->text().isEmpty() ) |
|
{ |
|
return; |
|
} |
|
|
|
// Determine position on texture to paint text |
|
QRect rect( QPoint( 0, 0 ), m_effectFrame->geometry().size() ); |
|
if( !m_effectFrame->icon().isNull() && !m_effectFrame->iconSize().isEmpty() ) |
|
{ |
|
rect.setLeft( m_effectFrame->iconSize().width() ); |
|
} |
|
|
|
// If static size elide text as required |
|
QString text = m_effectFrame->text(); |
|
if( m_effectFrame->isStatic() ) |
|
{ |
|
QFontMetrics metrics( m_effectFrame->text() ); |
|
text = metrics.elidedText( text, Qt::ElideRight, rect.width() ); |
|
} |
|
|
|
QPixmap pixmap( m_effectFrame->geometry().size() ); |
|
pixmap.fill( Qt::transparent ); |
|
QPainter p( &pixmap ); |
|
p.setFont( m_effectFrame->font() ); |
|
if( m_effectFrame->style() == EffectFrameStyled ) |
|
{ |
|
p.setPen( m_effectFrame->styledTextColor() ); |
|
} |
|
else |
|
{ |
|
// TODO: What about no frame? Custom color setting required |
|
p.setPen( Qt::white ); |
|
} |
|
p.drawText( rect, m_effectFrame->alignment(), text ); |
|
p.end(); |
|
m_textPicture = new XRenderPicture( pixmap ); |
|
} |
|
|
|
} // namespace |
|
#endif
|
|
|