Make it possible for the decorations to paint outside the window frame

when compositing is enabled, by letting them add additional padding
to the sides of the frame.

svn path=/trunk/KDE/kdebase/workspace/; revision=963586
This commit is contained in:
Fredrik Höglund 2009-05-04 22:35:33 +00:00
parent 1a4b08b36a
commit 8bba7aed7d
13 changed files with 159 additions and 54 deletions

View file

@ -325,6 +325,10 @@ void Client::updateDecoration( bool check_workspace_pos, bool force )
XReparentWindow( display(), decoration->widget()->winId(), frameId(), 0, 0 );
decoration->widget()->lower();
decoration->borders( border_left, border_right, border_top, border_bottom );
padding_left = padding_right = padding_top = padding_bottom = 0;
if (KDecorationUnstable *deco2 = dynamic_cast<KDecorationUnstable*>(decoration))
deco2->padding( padding_left, padding_right, padding_top, padding_bottom );
XMoveWindow( display(), decoration->widget()->winId(), -padding_left, -padding_top );
move( calculateGravitation( false ));
plainResize( sizeForClientSize( clientSize()), ForceGeometrySet );
do_show = true;
@ -376,7 +380,16 @@ bool Client::checkBorderSizes( bool also_resize )
{
if( decoration == NULL )
return false;
int new_left, new_right, new_top, new_bottom;
int new_left = 0, new_right = 0, new_top = 0, new_bottom = 0;
if (KDecorationUnstable *deco2 = dynamic_cast<KDecorationUnstable*>(decoration))
deco2->padding( new_left, new_right, new_top, new_bottom );
if (padding_left != new_left || padding_top != new_top)
XMoveWindow( display(), decoration->widget()->winId(), -new_left, -new_top );
padding_left = new_left;
padding_right = new_right;
padding_top = new_top;
padding_bottom = new_bottom;
decoration->borders( new_left, new_right, new_top, new_bottom );
if( new_left == border_left && new_right == border_right &&
new_top == border_top && new_bottom == border_bottom )
@ -407,16 +420,31 @@ void Client::triggerDecorationRepaint()
decoration->widget()->update();
}
void Client::layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, Client::CoordinateMode mode) const
{
QRect r = decoration->widget()->rect();
if (mode == WindowRelative)
r.translate(-padding_left, -padding_top);
top = QRect(r.x(), r.y(), r.width(), padding_top + border_top);
bottom = QRect(r.x(), r.y() + r.height() - padding_bottom - border_bottom,
r.width(), padding_bottom + border_bottom);
left = QRect(r.x(), r.y() + top.height(),
padding_left + border_left, r.height() - top.height() - bottom.height());
right = QRect(r.x() + r.width() - padding_right - border_right, r.y() + top.height(),
padding_right + border_right, r.height() - top.height() - bottom.height());
}
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();
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() - padding_left, y() - padding_top ) );
}
else
ensureDecorationPixmapsPainted();
}
bool Client::decorationPixmapRequiresRepaint()
@ -438,10 +466,8 @@ void Client::ensureDecorationPixmapsPainted()
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 );
QRect lr, rr, tr, br;
layoutDecorationRects( lr, tr, rr, br, DecorationRelative );
repaintDecorationPixmap( decorationPixmapLeft, lr, p, r );
repaintDecorationPixmap( decorationPixmapRight, rr, p, r );
@ -451,6 +477,7 @@ void Client::ensureDecorationPixmapsPainted()
if (!compositing())
{
// Blit the pixmaps to the frame window
layoutDecorationRects( lr, tr, rr, br, WindowRelative );
#ifdef HAVE_XRENDER
if (Extensions::renderAvailable())
{
@ -505,10 +532,13 @@ void Client::repaintDecorationPixmap( QPixmap& pix, const QRect& r, const QPixma
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 );
QRect lr, rr, tr, br;
layoutDecorationRects( lr, tr, rr, br, DecorationRelative );
decorationPixmapTop = QPixmap( tr.size() );
decorationPixmapBottom = QPixmap( br.size() );
decorationPixmapLeft = QPixmap( lr.size() );
decorationPixmapRight = QPixmap( rr.size() );
#ifdef HAVE_XRENDER
if ( Extensions::renderAvailable() ) {
// Make sure the pixmaps are created with alpha channels
@ -580,14 +610,15 @@ void Client::resizeDecoration( const QSize& s )
{
if( decoration == NULL )
return;
QSize oldsize = decoration->widget()->size();
decoration->resize( s );
if( oldsize == s )
QSize newSize = s + QSize(padding_left + padding_right, padding_top + padding_bottom);
QSize oldSize = decoration->widget()->size();
decoration->resize( newSize );
if( oldSize == newSize )
{
QResizeEvent e( s, oldsize );
QResizeEvent e( newSize, oldSize );
QApplication::sendEvent( decoration->widget(), &e );
}
else // oldsize != s
else // oldSize != newSize
{
resizeDecorationPixmaps();
}
@ -678,9 +709,10 @@ void Client::updateInputShape()
void Client::setMask( const QRegion& reg, int mode )
{
if( _mask == reg )
QRegion r = reg.translated( -padding_left, -padding_right ) & QRect( 0, 0, width(), height() );
if( _mask == r )
return;
_mask = reg;
_mask = r;
Window shape_window = frameId();
if( shape() )
{ // The same way of applying a shape without strange intermediate states like above
@ -689,13 +721,13 @@ void Client::setMask( const QRegion& reg, int mode )
0, 0, 1, 1, 0, 0, 0 );
shape_window = shape_helper_window;
}
if( reg.isEmpty() )
if( _mask.isEmpty() )
XShapeCombineMask( display(), shape_window, ShapeBounding, 0, 0, None, ShapeSet );
else if( mode == X::Unsorted )
XShapeCombineRegion( display(), shape_window, ShapeBounding, 0, 0, reg.handle(), ShapeSet );
XShapeCombineRegion( display(), shape_window, ShapeBounding, 0, 0, _mask.handle(), ShapeSet );
else
{
QVector< QRect > rects = reg.rects();
QVector< QRect > rects = _mask.rects();
XRectangle* xrects = new XRectangle[rects.count()];
for( int i = 0; i < rects.count(); ++i )
{

View file

@ -313,6 +313,17 @@ class Client
bool decorationPixmapRequiresRepaint();
void ensureDecorationPixmapsPainted();
QRect decorationRect() const {
return decoration ? decoration->widget()->rect().translated(-padding_left, -padding_top)
: QRect(0, 0, width(), height());
}
enum CoordinateMode {
DecorationRelative, // Relative to the top left corner of the decoration
WindowRelative // Relative to the top left corner of the window
};
void layoutDecorationRects(QRect &left, QRect &top, QRect &right, QRect &bottom, CoordinateMode mode) const;
private slots:
void autoRaise();
void shadeHover();
@ -554,6 +565,7 @@ class Client
QTimer* sync_timeout;
bool sync_resize_pending;
int border_left, border_right, border_top, border_bottom;
int padding_left, padding_right, padding_top, padding_bottom;
QRegion _mask;
static bool check_active_modal; ///< \see Client::checkActiveModal()
KShortcut _shortcut;

View file

@ -1162,7 +1162,7 @@ bool Client::eventFilter( QObject* o, QEvent* e )
// This will ensure that decoration->width() etc. and decoration->widget()->width() will be in sync.
// These events only seem to be delayed events from initial resizing before show() was called
// on the decoration widget.
if( ev->size() != size())
if( ev->size() != (size() + QSize( padding_left + padding_right, padding_top + padding_bottom ) ) )
return true;
// HACK: Avoid decoration redraw delays. On resize Qt sets WA_WStateConfigPending
// which delays all painting until a matching ConfigureNotify event comes.
@ -1303,7 +1303,7 @@ void Client::processDecorationButtonPress( int button, int /*state*/, int x, int
{
mode = mousePosition( QPoint( x, y ));
buttonDown = true;
moveOffset = QPoint( x, y );
moveOffset = QPoint( x - padding_left, y - padding_top );
invertedMoveOffset = rect().bottomRight() - moveOffset;
unrestrictedMoveResize = false;
startDelayedMoveResize();

View file

@ -178,7 +178,12 @@ void KCommonDecoration::borders( int& left, int& right, int& top, int& bottom )
void KCommonDecoration::updateLayout() const
{
QRect r = widget()->rect();
const int paddingLeft = layoutMetric(LM_OuterPaddingLeft);
const int paddingTop = layoutMetric(LM_OuterPaddingTop);
const int paddingRight = layoutMetric(LM_OuterPaddingRight);
const int paddingBottom = layoutMetric(LM_OuterPaddingBottom);
QRect r = widget()->rect().adjusted(paddingLeft, paddingTop, -paddingRight, -paddingBottom);
int r_x, r_y, r_x2, r_y2;
r.getCoords(&r_x, &r_y, &r_x2, &r_y2);
@ -729,7 +734,12 @@ KCommonDecoration::Position KCommonDecoration::mousePosition(const QPoint &point
const int corner = 18+3*layoutMetric(LM_BorderBottom, false)/2;
Position pos = PositionCenter;
QRect r = widget()->rect();
const int paddingLeft = layoutMetric(LM_OuterPaddingLeft);
const int paddingTop = layoutMetric(LM_OuterPaddingTop);
const int paddingRight = layoutMetric(LM_OuterPaddingRight);
const int paddingBottom = layoutMetric(LM_OuterPaddingBottom);
QRect r = widget()->rect().adjusted(paddingLeft, paddingTop, -paddingRight, -paddingBottom);
int r_x, r_y, r_x2, r_y2;
r.getCoords(&r_x, &r_y, &r_x2, &r_y2);
int p_x = point.x();
@ -876,8 +886,14 @@ bool KCommonDecoration::isToolWindow() const
QRect KCommonDecoration::titleRect() const
{
const int pl = layoutMetric(LM_OuterPaddingLeft);
const int pt = layoutMetric(LM_OuterPaddingTop);
const int pr = layoutMetric(LM_OuterPaddingRight);
const int pb = layoutMetric(LM_OuterPaddingBottom);
int r_x, r_y, r_x2, r_y2;
widget()->rect().getCoords(&r_x, &r_y, &r_x2, &r_y2);
widget()->rect().adjusted(pl, pt, -pr, -pb).getCoords(&r_x, &r_y, &r_x2, &r_y2);
const int titleEdgeLeft = layoutMetric(LM_TitleEdgeLeft);
const int titleEdgeTop = layoutMetric(LM_TitleEdgeTop);
const int titleEdgeRight = layoutMetric(LM_TitleEdgeRight);

View file

@ -119,7 +119,11 @@ class KWIN_EXPORT KCommonDecoration : public QObject, public KDecorationDefines
LM_ButtonHeight,
LM_ButtonSpacing,
LM_ExplicitButtonSpacer,
LM_ButtonMarginTop
LM_ButtonMarginTop,
LM_OuterPaddingLeft, ///< @since 4.3
LM_OuterPaddingTop, ///< @since 4.3
LM_OuterPaddingRight, ///< @since 4.3
LM_OuterPaddingBottom, ///< @since 4.4
};
enum DecorationBehaviour

View file

@ -140,3 +140,12 @@ double KCommonDecorationWrapper::shadowSaturation( ShadowType type ) const
return decoration2->shadowSaturation( type );
return 1.0;
}
void KCommonDecorationWrapper::padding(int &left, int &right, int &top, int &bottom) const
{
left = decoration->layoutMetric(KCommonDecoration::LM_OuterPaddingLeft);
right = decoration->layoutMetric(KCommonDecoration::LM_OuterPaddingRight);
top = decoration->layoutMetric(KCommonDecoration::LM_OuterPaddingTop);
bottom = decoration->layoutMetric(KCommonDecoration::LM_OuterPaddingBottom);
}

View file

@ -63,6 +63,8 @@ class KCommonDecorationWrapper
virtual double shadowOpacity( ShadowType type ) const;
virtual double shadowBrightness( ShadowType type ) const;
virtual double shadowSaturation( ShadowType type ) const;
virtual void padding( int &left, int &right, int &top, int &bottom ) const;
private:
KCommonDecoration* decoration;
};

View file

@ -440,6 +440,10 @@ double KDecorationUnstable::opacity() const
return static_cast< KDecorationBridgeUnstable* >( bridge_ )->opacity();
}
void KDecorationUnstable::padding(int &left, int &right, int &top, int &bottom) const
{
left = right = top = bottom = 0;
}
KDecorationOptions::KDecorationOptions()
: d( new KDecorationOptionsPrivate )

View file

@ -186,6 +186,10 @@ public:
ABILITYCOLOR_END, ///< @internal
// compositing
AbilityCompositingShadow = 3000, ///< decoration supports window shadows
AbilityUsesAlphaChannel = 3001, ///< The decoration isn't clipped to the mask when compositing is enabled.
/// The mask is still used to define the input region and the blurred
/// region, when the blur plugin is enabled.
/// @since 4.3
// TODO colors for individual button types
ABILITY_DUMMY = 10000000
};
@ -871,10 +875,10 @@ class KWIN_EXPORT KDecorationUnstable
: public KDecoration
{
Q_OBJECT
public:
KDecorationUnstable( KDecorationBridge* bridge, KDecorationFactory* factory );
virtual ~KDecorationUnstable();
/**
* This function should return the positions of the shadow quads to be rendered.
* All positions are relative to the window's top-left corner. Only "bordered"
@ -893,7 +897,17 @@ class KWIN_EXPORT KDecorationUnstable
* This function should return the desired saturation of the shadow.
*/
virtual double shadowSaturation( ShadowType type ) const;
/**
* This function can return additional padding values that are added outside the
* borders of the window, and can be used by the decoration if it wants to paint
* outside the frame.
*
* The typical use case is for drawing a drop shadow or glowing effect around the window.
*
* The area outside the frame cannot receive input, and when compositing is disabled,
* painting is clipped to the mask, or the window frame if no mask is defined.
*/
virtual void padding(int &left, int &right, int &top, int &bottom) const;
/**
* Force a repaint of the shadow. Automatically called when the window changes states.
*/

View file

@ -476,8 +476,10 @@ WindowQuadList Scene::Window::buildQuads( bool force ) const
ret = makeQuads( WindowQuadContents, shape()); // has no decoration
else
{
Client *client = static_cast<Client*>( toplevel );
QRegion contents = shape() & QRect( toplevel->clientPos(), toplevel->clientSize());
QRegion decoration = shape() - contents;
QRegion decoration = (Workspace::self()->decorationHasAlpha() ?
QRegion(client->decorationRect()) : shape()) - contents;
ret = makeQuads( WindowQuadContents, contents );
ret += makeQuads( WindowQuadDecoration, decoration );
}

View file

@ -1368,8 +1368,6 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat
// paint only requested areas
if( region != infiniteRegion()) // avoid integer overflow
region.translate( -x(), -y());
if(( mask & ( PAINT_SCREEN_TRANSFORMED | PAINT_WINDOW_TRANSFORMED )) == 0 )
region &= shape();
if( region.isEmpty())
return;
if( !bindTexture())
@ -1464,10 +1462,9 @@ void SceneOpenGL::Window::performPaint( int mask, QRegion region, WindowPaintDat
const QPixmap *bottom = client->bottomDecoPixmap();
WindowQuadList topList, leftList, rightList, bottomList;
QRect topRect( QPoint(0, 0), top->size() );
QRect leftRect( QPoint(0, client->clientPos().y()), left->size() );
QRect rightRect( QPoint(client->width() - right->width(), client->clientPos().y()), right->size() );
QRect bottomRect( QPoint(0, client->height()-bottom->height()), bottom->size() );
QRect topRect, leftRect, rightRect, bottomRect;
client->layoutDecorationRects(leftRect, topRect, rightRect, bottomRect, Client::WindowRelative);
foreach( WindowQuad quad, decoration )
{
if( topRect.contains( QPoint( quad.originalLeft(), quad.originalTop() ) ) )

View file

@ -606,10 +606,12 @@ QPoint SceneXrender::Window::mapToScreen( int mask, const WindowPaintData &data,
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( width(), height() );
else if( temp_pixmap->width() < width() || temp_pixmap->height() < height() )
*temp_pixmap = QPixmap( width(), height() );
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 );
@ -619,9 +621,9 @@ void SceneXrender::Window::prepareTempPixmap(const QPixmap *left, const QPixmap
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() );
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, height() - bottom->height(), bottom->width(), bottom->height() );
0, 0, 0, 0, 0, r.height() - bottom->height(), bottom->width(), bottom->height() );
}
// paint the window
@ -660,7 +662,12 @@ void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintDa
double xscale = 1;
double yscale = 1;
bool scaled = false;
transformed_shape = shape();
Client *client = dynamic_cast<Client*>( toplevel );
if ( client && Workspace::self()->decorationHasAlpha() )
transformed_shape = QRegion( client->decorationRect() );
else
transformed_shape = shape();
XTransform xform = {{
{ XDoubleToFixed( 1 ), XDoubleToFixed( 0 ), XDoubleToFixed( 0 ) },
@ -730,7 +737,7 @@ void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintDa
transformed_shape = QRegion();
}
}
if( Client *client = dynamic_cast<Client*>( toplevel ) )
if( client )
{
if( !client->noBorder() )
{
@ -747,10 +754,8 @@ void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintDa
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() );
QRect tr, lr, rr, br;
client->layoutDecorationRects( lr, tr, rr, br, Client::WindowRelative );
tr = mapToScreen( mask, data, tr );
lr = mapToScreen( mask, data, lr );
@ -768,10 +773,11 @@ void SceneXrender::Window::performPaint( int mask, QRegion region, WindowPaintDa
}
else
{
const QRect r = mapToScreen( mask, data, client->decorationRect() );
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() );
0, 0, 0, 0, r.x(), r.y(), r.width(), r.height() );
XRenderSetPictureTransform( dpy, temp_pixmap->x11PictureHandle(), &identity );
}
}

View file

@ -365,6 +365,8 @@ class Workspace : public QObject, public KDecorationDefines
bool rulesUpdatesDisabled() const;
bool hasDecorationShadows() const;
bool decorationHasAlpha() const;
QList< QList<QImage> > decorationShadowTextures();
int decorationShadowTextureList( ShadowType type ) const;
QList<QRect> decorationShadowQuads( ShadowType type, QSize size ) const;
@ -1181,6 +1183,11 @@ inline bool Workspace::hasDecorationShadows() const
return mgr->factory()->supports( AbilityCompositingShadow );
}
inline bool Workspace::decorationHasAlpha() const
{
return mgr->factory()->supports( AbilityUsesAlphaChannel );
}
inline QList< QList<QImage> > Workspace::decorationShadowTextures()
{
if( KDecorationFactoryUnstable* factory = dynamic_cast<KDecorationFactoryUnstable*>( mgr->factory() ))