Implement initial support for ARGB window decorations.

Based on earlier work done by Lubos Lunak and ideas by Lucas Murray.

svn path=/trunk/KDE/kdebase/workspace/; revision=957680
This commit is contained in:
Fredrik Höglund 2009-04-22 17:29:56 +00:00
parent 43856dc300
commit e92aab0518
10 changed files with 534 additions and 70 deletions

View file

@ -90,6 +90,7 @@ set(kwin_KDEINIT_SRCS
effects.cpp
compositingprefs.cpp
desktoplayout.cpp
paintredirector.cpp
)
qt4_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.xml workspace.h KWin::Workspace )

View file

@ -42,6 +42,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "scene.h"
#include "effects.h"
#include "deleted.h"
#include "paintredirector.h"
#include <X11/extensions/shape.h>
#include <QX11Info>
@ -50,6 +51,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <X11/extensions/sync.h>
#endif
#ifdef HAVE_XRENDER
#include <X11/extensions/Xrender.h>
#endif
// Put all externs before the namespace statement to allow the linker
// to resolve them properly
@ -110,6 +116,7 @@ Client::Client( Workspace* ws )
, border_bottom( 0 )
, sm_stacking_order( -1 )
, demandAttentionKNotifyTimer( NULL )
, paintRedirector( 0 )
{ // TODO: Do all as initialization
// Set the initial mapping state
@ -321,6 +328,9 @@ void Client::updateDecoration( bool check_workspace_pos, bool force )
move( calculateGravitation( false ));
plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
do_show = true;
paintRedirector = new PaintRedirector( decoration->widget());
connect( paintRedirector, SIGNAL( paintPending()), SLOT( repaintDecorationPending()));
resizeDecorationPixmaps();
if( compositing() )
discardWindowPixmap();
if( scene != NULL )
@ -350,6 +360,9 @@ void Client::destroyDecoration()
setMask( QRegion()); // Reset shape mask
plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
move( grav );
delete paintRedirector;
paintRedirector = NULL;
decorationPixmapLeft = decorationPixmapRight = decorationPixmapTop = decorationPixmapBottom = QPixmap();
if( compositing() )
discardWindowPixmap();
if( scene != NULL && !deleting )
@ -388,12 +401,118 @@ bool Client::checkBorderSizes( bool also_resize )
return true;
}
void Client::repaintDecoration()
void Client::triggerDecorationRepaint()
{
if( decoration != NULL )
decoration->widget()->update();
}
void Client::repaintDecorationPending()
{
if ( compositing() )
{
// The scene will update the decoration pixmaps in the next painting pass
const QRegion r = paintRedirector->pendingRegion();
Workspace::self()->addRepaint( r.translated( x(), y() ) );
}
else
ensureDecorationPixmapsPainted();
}
void Client::ensureDecorationPixmapsPainted()
{
if (!paintRedirector)
return;
QRegion r = paintRedirector->pendingRegion();
if (r.isEmpty())
return;
QPixmap p = paintRedirector->performPendingPaint();
const QRect lr( 0, border_top, border_left, height() - border_top - border_bottom);
const QRect rr( width() - border_right, border_top, border_right, height() - border_top - border_bottom);
const QRect tr( 0, 0, width(), border_top );
const QRect br( 0, height() - border_bottom, width(), border_bottom );
repaintDecorationPixmap( decorationPixmapLeft, lr, p, r );
repaintDecorationPixmap( decorationPixmapRight, rr, p, r );
repaintDecorationPixmap( decorationPixmapTop, tr, p, r );
repaintDecorationPixmap( decorationPixmapBottom, br, p, r );
if (!compositing())
{
// Blit the pixmaps to the frame window
#ifdef HAVE_XRENDER
if (Extensions::renderAvailable())
{
XRenderPictFormat* format = XRenderFindVisualFormat( display(), visual());
XRenderPictureAttributes pa;
pa.subwindow_mode = IncludeInferiors;
Picture pic = XRenderCreatePicture( display(), frameId(), format, CPSubwindowMode, &pa );
XRenderComposite( display(), PictOpSrc, decorationPixmapLeft.x11PictureHandle(), None, pic,
0, 0, 0, 0, lr.x(), lr.y(), lr.width(), lr.height() );
XRenderComposite( display(), PictOpSrc, decorationPixmapRight.x11PictureHandle(), None, pic,
0, 0, 0, 0, rr.x(), rr.y(), rr.width(), rr.height() );
XRenderComposite( display(), PictOpSrc, decorationPixmapTop.x11PictureHandle(), None, pic,
0, 0, 0, 0, tr.x(), tr.y(), tr.width(), tr.height() );
XRenderComposite( display(), PictOpSrc, decorationPixmapBottom.x11PictureHandle(), None, pic,
0, 0, 0, 0, br.x(), br.y(), br.width(), br.height() );
XRenderFreePicture( display(), pic ); // TODO don't recreate pictures all the time?
}
else
#endif
{
XGCValues values;
values.subwindow_mode = IncludeInferiors;
GC gc = XCreateGC( display(), rootWindow(), GCSubwindowMode, &values );
XCopyArea( display(), decorationPixmapLeft.handle(), frameId(), gc, 0, 0,
lr.width(), lr.height(), lr.x(), lr.y() );
XCopyArea( display(), decorationPixmapRight.handle(), frameId(), gc, 0, 0,
rr.width(), rr.height(), rr.x(), rr.y() );
XCopyArea( display(), decorationPixmapTop.handle(), frameId(), gc, 0, 0,
tr.width(), tr.height(), tr.x(), tr.y() );
XCopyArea( display(), decorationPixmapBottom.handle(), frameId(), gc, 0, 0,
br.width(), br.height(), br.x(), br.y() );
XFreeGC( display(), gc );
}
}
}
void Client::repaintDecorationPixmap( QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg )
{
if( !r.isValid())
return;
QRect b = reg.boundingRect();
reg &= r;
if( reg.isEmpty())
return;
QPainter pt( &pix );
pt.translate( -r.topLeft() );
pt.setCompositionMode( QPainter::CompositionMode_Source );
pt.setClipRegion( reg );
pt.drawPixmap( b.topLeft(), src );
pt.end();
}
void Client::resizeDecorationPixmaps()
{
decorationPixmapLeft = QPixmap( border_left, height() - border_top - border_bottom );
decorationPixmapRight = QPixmap( border_right, height() - border_top - border_bottom );
decorationPixmapTop = QPixmap( width(), border_top );
decorationPixmapBottom = QPixmap( width(), border_bottom );
#ifdef HAVE_XRENDER
if ( Extensions::renderAvailable() ) {
// Make sure the pixmaps are created with alpha channels
decorationPixmapLeft.fill( Qt::transparent );
decorationPixmapRight.fill( Qt::transparent );
decorationPixmapTop.fill( Qt::transparent );
decorationPixmapBottom.fill( Qt::transparent );
}
#endif
triggerDecorationRepaint();
}
void Client::detectNoBorder()
{
if( shape())
@ -460,6 +579,10 @@ void Client::resizeDecoration( const QSize& s )
QResizeEvent e( s, oldsize );
QApplication::sendEvent( decoration->widget(), &e );
}
else // oldsize != s
{
resizeDecorationPixmaps();
}
}
bool Client::noBorder() const

View file

@ -58,6 +58,7 @@ namespace KWin
class Workspace;
class Client;
class Bridge;
class PaintRedirector;
class Client
: public Toplevel
@ -201,7 +202,7 @@ class Client
void updateDecoration( bool check_workspace_pos, bool force = false );
bool checkBorderSizes( bool also_resize );
void repaintDecoration();
void triggerDecorationRepaint();
void updateShape();
@ -304,6 +305,13 @@ class Client
double shadowBrightness( ShadowType type ) const;
double shadowSaturation( ShadowType type ) const;
const QPixmap *topDecoPixmap() const { return &decorationPixmapTop; }
const QPixmap *leftDecoPixmap() const { return &decorationPixmapLeft; }
const QPixmap *bottomDecoPixmap() const { return &decorationPixmapBottom; }
const QPixmap *rightDecoPixmap() const { return &decorationPixmapRight; }
void ensureDecorationPixmapsPainted();
private slots:
void autoRaise();
void shadeHover();
@ -358,6 +366,7 @@ class Client
void demandAttentionKNotify();
void syncTimeout();
void delayedSetShortcut();
void repaintDecorationPending();
private:
void exportMappingState( int s ); // ICCCM 4.1.3.1, 4.1.4, NETWM 2.5.1
@ -421,6 +430,8 @@ class Client
void updateHiddenPreview();
void updateInputShape();
void repaintDecorationPixmap( QPixmap& pix, const QRect& r, const QPixmap& src, QRegion reg );
void resizeDecorationPixmaps();
Time readUserTimeMapTimestamp( const KStartupInfoId* asn_id, const KStartupInfoData* asn_data,
bool session ) const;
@ -551,6 +562,8 @@ class Client
friend struct ResetupRulesProcedure;
friend class GeometryUpdatesBlocker;
QTimer* demandAttentionKNotifyTimer;
QPixmap decorationPixmapLeft, decorationPixmapRight, decorationPixmapTop, decorationPixmapBottom;
PaintRedirector* paintRedirector;
friend bool performTransiencyCheck();
};

125
paintredirector.cpp Normal file
View file

@ -0,0 +1,125 @@
/*****************************************************************
This file is part of the KDE project.
Copyright (C) 2009 Lubos Lunak <l.lunak@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
******************************************************************/
#include "paintredirector.h"
#include <kdebug.h>
#include <qevent.h>
#include <qpainter.h>
namespace KWin
{
PaintRedirector::PaintRedirector( QWidget* w )
: widget( w )
, recursionCheck( false )
{
timer.setSingleShot( true );
connect( &timer, SIGNAL( timeout()), SIGNAL( paintPending()));
added( w );
}
QPixmap PaintRedirector::performPendingPaint()
{
//qDebug() << "### performing paint, pending:" << pending.boundingRect();
QPixmap pixmap( pending.boundingRect().size());
pixmap.fill( Qt::transparent );
recursionCheck = true;
// do not use DrawWindowBackground, it's ok to be transparent
widget->render( &pixmap, QPoint(), pending.boundingRect(), QWidget::DrawChildren );
recursionCheck = false;
pending = QRegion();
return pixmap;
}
bool PaintRedirector::isToolTip( QWidget *object ) const
{
// ### We need a more reliable way of doing this
return object->metaObject()->className() == QString("QWidget") &&
object->objectName() != QString("decoration widget");
}
bool PaintRedirector::eventFilter( QObject* o, QEvent* e )
{
switch( e->type())
{
case QEvent::ChildAdded:
{
QChildEvent* c = static_cast< QChildEvent* >( e );
if( c->child()->isWidgetType() && !isToolTip( static_cast< QWidget* >( c->child() ) ) )
added( static_cast< QWidget* >( c->child()));
break;
}
case QEvent::ChildRemoved:
{
QChildEvent* c = static_cast< QChildEvent* >( e );
if( c->child()->isWidgetType())
removed( static_cast< QWidget* >( c->child()));
break;
}
case QEvent::Paint:
{
if( !recursionCheck )
{
QPaintEvent* pe = static_cast< QPaintEvent* >( e );
QWidget* w = static_cast< QWidget* >( o );
pending |= pe->region().translated( w->mapTo( widget, QPoint( 0, 0 )));
timer.start( 0 );
return true; // filter out
}
}
default:
break;
}
return false;
}
QRegion PaintRedirector::pendingRegion() const
{
return pending;
}
void PaintRedirector::added( QWidget* w )
{
w->installEventFilter( this );
foreach( QObject* o, w->children())
{
if( o->isWidgetType() && !isToolTip( static_cast< QWidget* >( o ) ) )
added( static_cast< QWidget* >( o ));
}
}
void PaintRedirector::removed( QWidget* w )
{
foreach( QObject* o, w->children())
{
if( o->isWidgetType())
removed( static_cast< QWidget* >( o ));
}
w->installEventFilter( this );
}
} // namespace
#include "paintredirector.moc"

60
paintredirector.h Normal file
View file

@ -0,0 +1,60 @@
/*****************************************************************
This file is part of the KDE project.
Copyright (C) 2009 Lubos Lunak <l.lunak@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
******************************************************************/
#ifndef PAINTREDIRECTOR_H
#define PAINTREDIRECTOR_H
#include <qregion.h>
#include <qtimer.h>
#include <qwidget.h>
namespace KWin
{
// This class redirects all painting of a given widget (including its children)
// into a paint device (QPixmap).
class PaintRedirector
: public QObject
{
Q_OBJECT
public:
PaintRedirector( QWidget* widget );
QPixmap performPendingPaint();
virtual bool eventFilter( QObject* o, QEvent* e );
QRegion pendingRegion() const;
signals:
void paintPending();
private:
void added( QWidget* widget );
void removed( QWidget* widget );
bool isToolTip( QWidget* widget ) const;
QWidget* widget;
QRegion pending;
bool recursionCheck;
QTimer timer;
};
} // namespace
#endif

View file

@ -232,7 +232,8 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
data.mask = orig_mask | ( w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT );
w->resetPaintingEnabled();
data.paint = region;
data.clip = w->isOpaque() ? w->shape().translated( w->x(), w->y()) : QRegion();
// Clip out the decoration for opaque windows; the decoration is drawn in the second pass
data.clip = w->isOpaque() ? QRegion(w->clientRect().translated( w->x(), w->y())) : QRegion();
data.quads = w->buildQuads();
// preparation step
effects->prePaintWindow( effectWindow( w ), data, time_diff );
@ -280,6 +281,9 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
{
// Paint the opaque window
paintWindow( d.window, d.mask, d.region, d.quads );
// Clip out the client area, so we only draw the decoration in the next pass
phase2data[w].region = d.region - d.clip;
phase2data[w].mask |= PAINT_DECORATION_ONLY;
}
}
// Fill any areas of the root window not covered by windows
@ -292,8 +296,7 @@ void Scene::paintSimpleScreen( int orig_mask, QRegion region )
if( !phase2data.contains( w ))
continue;
Phase2Data d = phase2data[w];
if( d.mask & PAINT_WINDOW_TRANSLUCENT )
paintWindow( d.window, d.mask, d.region, d.quads );
paintWindow( d.window, d.mask, d.region, d.quads );
}
}
@ -407,6 +410,12 @@ QRegion Scene::Window::shape() const
return shape_region;
}
// Returns the rectangle occupied by the client within the window geometry
QRect Scene::Window::clientRect() const
{
return QRect(toplevel->clientPos(), toplevel->clientSize());
}
bool Scene::Window::isVisible() const
{
if( dynamic_cast< Deleted* >( toplevel ) != NULL )

View file

@ -79,7 +79,9 @@ class Scene
// At least one window will be painted with transformed geometry.
PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS = 1 << 5,
// Clear whole background as the very first step, without optimizing it
PAINT_SCREEN_BACKGROUND_FIRST = 1 << 6
PAINT_SCREEN_BACKGROUND_FIRST = 1 << 6,
// Temporary solution since (_OPAQUE | _TRANSLUCENT) is not working currently.
PAINT_DECORATION_ONLY = 1 << 7
};
// types of filtering available
enum ImageFilterType { ImageFilterFast, ImageFilterGood };
@ -184,6 +186,7 @@ class Scene::Window
bool isOpaque() const;
// shape of the window
QRegion shape() const;
QRect clientRect() const;
void discardShape();
void updateToplevel( Toplevel* c );
// creates initial quad list for the window

View file

@ -3,6 +3,7 @@
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
@ -467,11 +468,14 @@ void SceneXrender::windowAdded( Toplevel* 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)
{
}
@ -520,41 +524,112 @@ void SceneXrender::Window::discardAlpha()
// Create XRender picture for the alpha mask.
Picture SceneXrender::Window::alphaMask( double opacity )
{
if( isOpaque() && opacity == 1.0 )
if( isOpaque() && qFuzzyCompare( opacity, 1.0 ) )
return None;
if( alpha != None && alpha_cached_opacity != opacity )
bool created = false;
if( alpha == None )
{
if( alpha != None )
XRenderFreePicture( display(), alpha );
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( alpha != None )
return alpha;
if( opacity == 1.0 )
{ // no need to create alpha mask
alpha_cached_opacity = 1.0;
return None;
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;
}
// 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 );
XRenderColor col;
col.alpha = int( opacity * 0xffff );
alpha_cached_opacity = opacity;
XRenderFillRectangle( display(), PictOpSrc, alpha, &col, 0, 0, 1, 1 );
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)
{
if( !temp_pixmap )
temp_pixmap = new QPixmap( width(), height() );
else if( temp_pixmap->width() < width() || temp_pixmap->height() < height() )
*temp_pixmap = QPixmap( width(), 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, width() - right->width(), top->height(), right->width(), right->height() );
XRenderComposite( dpy, PictOpSrc, bottom->x11PictureHandle(), None, temp_pixmap->x11PictureHandle(),
0, 0, 0, 0, 0, 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() && data.opacity == 1.0;
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
@ -579,41 +654,46 @@ void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintDa
else
filter = ImageFilterFast;
// do required transformations
int x = toplevel->x();
int y = toplevel->y();
int width = toplevel->width();
int height = toplevel->height();
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;
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 ) }
}};
if( mask & PAINT_WINDOW_TRANSFORMED )
{
xscale *= data.xScale;
yscale *= data.yScale;
x += data.xTranslate;
y += data.yTranslate;
xscale = data.xScale;
yscale = data.yScale;
}
if( mask & PAINT_SCREEN_TRANSFORMED )
{
xscale *= screen_paint.xScale;
yscale *= screen_paint.yScale;
x = int( x * screen_paint.xScale );
y = int( y * screen_paint.yScale );
x += screen_paint.xTranslate;
y += screen_paint.yTranslate;
}
if( yscale != 1 || xscale != 1 )
if( !qFuzzyCompare( xscale, 1.0 ) || !qFuzzyCompare( yscale, 1.0 ) )
{
XTransform xform = {{
{ XDoubleToFixed( 1 / xscale ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
{ XDoubleToFixed( 0 ), XDoubleToFixed( 1 / yscale ), XDoubleToFixed( 0 ) },
{ XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
}};
scaled = true;
xform.matrix[0][0] = XDoubleToFixed(1.0 / xscale);
xform.matrix[1][1] = XDoubleToFixed(1.0 / yscale);
XRenderSetPictureTransform( display(), pic, &xform );
width = (int)(width * xscale);
height = (int)(height * yscale);
if( filter == ImageFilterGood )
XRenderSetPictureFilter( display(), pic, const_cast< char* >( "good" ), NULL, 0 );
// transform the shape for clipping in paintTransformedScreen()
QVector< QRect > rects = transformed_shape.rects();
for( int i = 0;
@ -626,40 +706,86 @@ void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintDa
}
transformed_shape.setRects( rects.constData(), rects.count());
}
transformed_shape.translate( x, y );
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( opaque )
if ( !(mask & PAINT_DECORATION_ONLY) )
{
XRenderComposite( display(), PictOpSrc, pic, None, buffer, 0, 0, 0, 0,
x, y, width, height);
// fake brightness change by overlaying black
XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) };
XRenderFillRectangle( display(), PictOpOver, buffer, &col, x, y, width, height );
// 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();
}
}
else
if( Client *client = dynamic_cast<Client*>( toplevel ) )
{
if( !client->noBorder() )
{
// Paint the decoration
Picture alpha = alphaMask( data.opacity * data.decoration_opacity );
Display *dpy = display();
client->ensureDecorationPixmapsPainted();
const QPixmap *left = client->leftDecoPixmap();
const QPixmap *top = client->topDecoPixmap();
const QPixmap *right = client->rightDecoPixmap();
const QPixmap *bottom = client->bottomDecoPixmap();
if( !scaled )
{
QRect tr( QPoint( 0, 0 ), top->size() );
QRect lr( QPoint( 0, top->height() ), left->size() );
QRect rr( QPoint( width() - right->width(), top->height() ), right->size() );
QRect br( QPoint( 0, height() - bottom->height() ), bottom->size() );
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
{
prepareTempPixmap( left, top, right, bottom );
XRenderSetPictureTransform( dpy, temp_pixmap->x11PictureHandle(), &xform );
XRenderComposite( dpy, PictOpOver, temp_pixmap->x11PictureHandle(), alpha, buffer,
0, 0, 0, 0, wr.x(), wr.y(), wr.width(), wr.height() );
XRenderSetPictureTransform( dpy, temp_pixmap->x11PictureHandle(), &identity );
}
}
}
if( data.brightness < 1.0 )
{
Picture alpha = alphaMask( data.opacity );
XRenderComposite( display(), PictOpOver, pic, alpha, buffer, 0, 0, 0, 0,
x, y, width, height);
// fake brightness change by overlaying black
XRenderColor col = { 0, 0, 0, 0xffff * ( 1 - data.brightness ) * data.opacity };
XRenderFillRectangle( display(), PictOpOver, buffer, &col, x, y, width, height );
transformed_shape = QRegion();
XRenderFillRectangle( display(), PictOpOver, buffer, &col, wr.x(), wr.y(), wr.width(), wr.height() );
}
}
if( xscale != 1 || yscale != 1 )
if( scaled )
{
XTransform xform = {{
{ XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
{ XDoubleToFixed( 0 ), XDoubleToFixed( 1 ), XDoubleToFixed( 0 ) },
{ XDoubleToFixed( 0 ), XDoubleToFixed( 0 ), XDoubleToFixed( 1 ) }
}};
XRenderSetPictureTransform( display(), pic, &xform );
XRenderSetPictureTransform( display(), pic, &identity );
if( filter == ImageFilterGood )
XRenderSetPictureFilter( display(), pic, const_cast< char* >( "fast" ), NULL, 0 );
}

View file

@ -81,11 +81,15 @@ class SceneXrender::Window
private:
Picture picture();
Picture alphaMask( double opacity );
QRect mapToScreen(int mask, const WindowPaintData &data, const QRect &rect) const;
QPoint mapToScreen(int mask, const WindowPaintData &data, const QPoint &point) const;
void prepareTempPixmap(const QPixmap *left, const QPixmap *top, const QPixmap *right, const QPixmap *bottom);
Picture _picture;
XRenderPictFormat* format;
Picture alpha;
double alpha_cached_opacity;
QRegion transformed_shape;
static QPixmap *temp_pixmap;
};
inline

View file

@ -1058,7 +1058,7 @@ void Workspace::slotReconfigure()
{
forEachClient( CheckBorderSizesProcedure() );
foreach( Client* c, clients )
c->repaintDecoration();
c->triggerDecorationRepaint();
}
reserveElectricBorderActions( true );