diff --git a/client.cpp b/client.cpp index f022086db6..36a852f221 100644 --- a/client.cpp +++ b/client.cpp @@ -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(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(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 ) { diff --git a/client.h b/client.h index 773db2d58b..fa12b9ee2b 100644 --- a/client.h +++ b/client.h @@ -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; diff --git a/events.cpp b/events.cpp index b1c0c2a320..8de2e411cf 100644 --- a/events.cpp +++ b/events.cpp @@ -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(); diff --git a/lib/kcommondecoration.cpp b/lib/kcommondecoration.cpp index 35756a2a07..b275686b2d 100644 --- a/lib/kcommondecoration.cpp +++ b/lib/kcommondecoration.cpp @@ -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); diff --git a/lib/kcommondecoration.h b/lib/kcommondecoration.h index 4b2cb867db..7dc818f3d1 100644 --- a/lib/kcommondecoration.h +++ b/lib/kcommondecoration.h @@ -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 diff --git a/lib/kcommondecoration_p.cpp b/lib/kcommondecoration_p.cpp index 733b9d93d8..cc55040d1d 100644 --- a/lib/kcommondecoration_p.cpp +++ b/lib/kcommondecoration_p.cpp @@ -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); + } + diff --git a/lib/kcommondecoration_p.h b/lib/kcommondecoration_p.h index e152a28b87..5bd1954759 100644 --- a/lib/kcommondecoration_p.h +++ b/lib/kcommondecoration_p.h @@ -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; }; diff --git a/lib/kdecoration.cpp b/lib/kdecoration.cpp index b1d349907c..610dd4b748 100644 --- a/lib/kdecoration.cpp +++ b/lib/kdecoration.cpp @@ -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 ) diff --git a/lib/kdecoration.h b/lib/kdecoration.h index cf3a8abb35..7812154111 100644 --- a/lib/kdecoration.h +++ b/lib/kdecoration.h @@ -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. */ diff --git a/scene.cpp b/scene.cpp index 1aa5b67263..0cac318ab7 100644 --- a/scene.cpp +++ b/scene.cpp @@ -476,8 +476,10 @@ WindowQuadList Scene::Window::buildQuads( bool force ) const ret = makeQuads( WindowQuadContents, shape()); // has no decoration else { + Client *client = static_cast( 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 ); } diff --git a/scene_opengl.cpp b/scene_opengl.cpp index 3240ff3f0f..e15e1b04ff 100644 --- a/scene_opengl.cpp +++ b/scene_opengl.cpp @@ -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() ) ) ) diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 8638c04c4f..ddb005d12b 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -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( 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( 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( 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 ); } } diff --git a/workspace.h b/workspace.h index 6f6189b29a..d6e01b3d53 100644 --- a/workspace.h +++ b/workspace.h @@ -365,6 +365,8 @@ class Workspace : public QObject, public KDecorationDefines bool rulesUpdatesDisabled() const; bool hasDecorationShadows() const; + bool decorationHasAlpha() const; + QList< QList > decorationShadowTextures(); int decorationShadowTextureList( ShadowType type ) const; QList 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 > Workspace::decorationShadowTextures() { if( KDecorationFactoryUnstable* factory = dynamic_cast( mgr->factory() ))