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:
parent
43856dc300
commit
e92aab0518
10 changed files with 534 additions and 70 deletions
|
@ -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 )
|
||||
|
|
125
client.cpp
125
client.cpp
|
@ -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
|
||||
|
|
15
client.h
15
client.h
|
@ -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
125
paintredirector.cpp
Normal 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
60
paintredirector.h
Normal 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
|
15
scene.cpp
15
scene.cpp
|
@ -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 )
|
||||
|
|
5
scene.h
5
scene.h
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1058,7 +1058,7 @@ void Workspace::slotReconfigure()
|
|||
{
|
||||
forEachClient( CheckBorderSizesProcedure() );
|
||||
foreach( Client* c, clients )
|
||||
c->repaintDecoration();
|
||||
c->triggerDecorationRepaint();
|
||||
}
|
||||
|
||||
reserveElectricBorderActions( true );
|
||||
|
|
Loading…
Reference in a new issue