diff --git a/kstyle/breezeshadowhelper.h b/kstyle/breezeshadowhelper.h --- a/kstyle/breezeshadowhelper.h +++ b/kstyle/breezeshadowhelper.h @@ -3,6 +3,7 @@ /************************************************************************* * Copyright (C) 2014 by Hugo Pereira Da Costa * + * Copyright (C) 2020 by Vlad Zahorodnii * * * * 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 * @@ -23,26 +24,13 @@ #include "breezetileset.h" #include "config-breeze.h" +#include + #include #include #include #include - -#if BREEZE_HAVE_X11 -#include -#endif - -#if BREEZE_HAVE_KWAYLAND -namespace KWayland -{ - namespace Client - { - class ShadowManager; - class ShmPool; - class Surface; - } -} -#endif +#include namespace Breeze { @@ -131,9 +119,6 @@ //* unregister widget void objectDeleted( QObject* ); - //* initializes the Wayland specific parts - void initializeWayland(); - protected: //* true if widget is a menu @@ -151,33 +136,17 @@ //* accept widget bool acceptWidget( QWidget* ) const; - // create pixmap handles from tileset - const QVector& createPixmapHandles(); + // create shared shadow tiles from tileset + const QVector& createShadowTiles(); - // create pixmap handle from pixmap - quint32 createPixmap( const QPixmap& ); + // create shadow tile from pixmap + KWindowShadowTile::Ptr createTile( const QPixmap& ); //* installs shadow on given widget in a platform independent way - bool installShadows( QWidget * ); + void installShadows( QWidget * ); //* uninstalls shadow on given widget in a platform independent way - void uninstallShadows( QWidget * ) const; - - //* install shadow X11 property on given widget - /** - shadow atom and property specification available at - https://community.kde.org/KWin/Shadow - */ - bool installX11Shadows( QWidget* ); - - //* uninstall shadow X11 property on given widget - void uninstallX11Shadows( QWidget* ) const; - - //* install shadow on given widget for Wayland - bool installWaylandShadows( QWidget * ); - - //* uninstall shadow on given widget for Wayland - void uninstallWaylandShadows( QWidget* ) const; + void uninstallShadows( QWidget * ); //* gets the shadow margins for the given widget QMargins shadowMargins( QWidget* ) const; @@ -188,38 +157,19 @@ Helper& _helper; //* registered widgets - QMap _widgets; + QSet _widgets; + + //* managed shadows + QMap _shadows; //* tileset TileSet _shadowTiles; - //* number of pixmaps - enum { numPixmaps = 8 }; - - //* pixmaps - QVector _pixmaps; - - #if BREEZE_HAVE_X11 - - //* graphical context - xcb_gcontext_t _gc = 0; - - //* shadow atom - xcb_atom_t _atom = 0; - - #endif - - #if BREEZE_HAVE_KWAYLAND - - //* registered widgets to wayland surface mappings - QMap _widgetSurfaces; - - //* The Wayland shadow manager to create Shadows for Surfaces (QWindow) - QPointer _shadowManager; + //* number of tiles + enum { numTiles = 8 }; - //* The Wayland Shared memory pool to share the shadow pixmaps with compositor - QPointer _shmPool; - #endif + //* shared shadow tiles + QVector _tiles; }; diff --git a/kstyle/breezeshadowhelper.cpp b/kstyle/breezeshadowhelper.cpp --- a/kstyle/breezeshadowhelper.cpp +++ b/kstyle/breezeshadowhelper.cpp @@ -1,6 +1,6 @@ /************************************************************************* * Copyright (C) 2014 by Hugo Pereira Da Costa * - * Copyright (C) 2018 by Vlad Zagorodniy * + * Copyright (C) 2018, 2020 by Vlad Zahorodnii * * * * 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 * @@ -32,22 +32,10 @@ #include #include #include +#include #include #include -#if BREEZE_HAVE_X11 -#include -#endif - -#if BREEZE_HAVE_KWAYLAND -#include -#include -#include -#include -#include -#include -#endif - namespace { using Breeze::CompositeShadowParams; @@ -82,8 +70,6 @@ namespace Breeze { - const char ShadowHelper::netWMShadowAtomName[] ="_KDE_NET_WM_SHADOW"; - //_____________________________________________________ CompositeShadowParams ShadowHelper::lookupShadowParams(int shadowSizeEnum) { @@ -111,61 +97,18 @@ QObject( parent ), _helper( helper ) { - // delay till event dispatcher is running as Wayland is highly async - QMetaObject::invokeMethod(this, "initializeWayland", Qt::QueuedConnection); } //_______________________________________________________ ShadowHelper::~ShadowHelper() { - - #if BREEZE_HAVE_X11 - if( Helper::isX11() ) - { foreach( const quint32& value, _pixmaps ) xcb_free_pixmap( Helper::connection(), value ); } - #endif - - } - - //_______________________________________________________ - void ShadowHelper::initializeWayland() - { - #if BREEZE_HAVE_KWAYLAND - if( !Helper::isWayland() ) return; - - using namespace KWayland::Client; - auto connection = ConnectionThread::fromApplication( this ); - if( !connection ) { - return; - } - auto registry = new Registry( connection ); - registry->create( connection ); - connect(registry, &Registry::interfacesAnnounced, this, - [registry, this] { - const auto interface = registry->interface( Registry::Interface::Shadow ); - if( interface.name != 0 ) { - _shadowManager = registry->createShadowManager( interface.name, interface.version, registry ); - } - const auto shmInterface = registry->interface( Registry::Interface::Shm ); - if( shmInterface.name != 0 ) { - _shmPool = registry->createShmPool( shmInterface.name, shmInterface.version, registry ); - } - } - ); - - registry->setup(); - connection->roundtrip(); - #endif + qDeleteAll( _shadows ); } //______________________________________________ void ShadowHelper::reset() { - #if BREEZE_HAVE_X11 - if( Helper::isX11() ) - { foreach( const quint32& value, _pixmaps ) xcb_free_pixmap( Helper::connection(), value ); } - #endif - - _pixmaps.clear(); + _tiles.clear(); _shadowTiles = TileSet(); } @@ -181,8 +124,8 @@ { return false; } // try create shadow directly - if( installShadows( widget ) ) _widgets.insert( widget, widget->winId() ); - else _widgets.insert( widget, 0 ); + installShadows( widget ); + _widgets.insert( widget ); // install event filter widget->removeEventFilter( this ); @@ -210,51 +153,40 @@ reset(); // update property for registered widgets - for( QMap::const_iterator iter = _widgets.constBegin(); iter != _widgets.constEnd(); ++iter ) - { installShadows( iter.key() ); } + for( QWidget* widget : _widgets) + { installShadows( widget ); } } //_______________________________________________________ bool ShadowHelper::eventFilter( QObject* object, QEvent* event ) { - if( Helper::isWayland() ) + if( Helper::isX11() ) { - - #if BREEZE_HAVE_KWAYLAND - QWidget* widget( static_cast( object ) ); - if( event->type() == QEvent::Paint ) - { - - auto iter = _widgetSurfaces.constFind( widget ); - if( iter == _widgetSurfaces.constEnd() ) - { - // install shadows and update winId - installShadows( widget ); - } - - } else if( event->type() == QEvent::Hide ) { - - auto iter = _widgetSurfaces.find( widget ); - if( iter != _widgetSurfaces.end() ) - { - _widgetSurfaces.erase( iter ); - } - - } - #endif - - } else if( Helper::isX11() ) { - // check event type if( event->type() != QEvent::WinIdChange ) return false; // cast widget QWidget* widget( static_cast( object ) ); // install shadows and update winId - if( installShadows( widget ) ) - { _widgets.insert( widget, widget->winId() ); } + installShadows( widget ); + + } else { + if( event->type() != QEvent::PlatformSurface ) return false; + + QWidget* widget( static_cast( object ) ); + QPlatformSurfaceEvent* surfaceEvent( static_cast( event ) ); + + switch( surfaceEvent->surfaceEventType() ) + { + case QPlatformSurfaceEvent::SurfaceCreated: + installShadows( widget ); + break; + case QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed: + // Don't care. + break; + } } return false; @@ -347,7 +279,12 @@ //_______________________________________________________ void ShadowHelper::objectDeleted( QObject* object ) - { _widgets.remove( static_cast( object ) ); } + { + QWidget* widget( static_cast( object ) ); + _widgets.remove( widget ); + _shadows.remove( widget ); + + } //_______________________________________________________ bool ShadowHelper::isMenu( QWidget* widget ) const @@ -392,176 +329,83 @@ } //______________________________________________ - const QVector& ShadowHelper::createPixmapHandles() + const QVector& ShadowHelper::createShadowTiles() { - /** - shadow atom and property specification available at - https://community.kde.org/KWin/Shadow - */ - - // create atom - #if BREEZE_HAVE_X11 - if( !_atom && Helper::isX11() ) _atom = _helper.createAtom( QLatin1String( netWMShadowAtomName ) ); - #endif - // make sure size is valid - if( _pixmaps.empty() ) + if( _tiles.isEmpty() ) { - _pixmaps = QVector { - createPixmap( _shadowTiles.pixmap( 1 ) ), - createPixmap( _shadowTiles.pixmap( 2 ) ), - createPixmap( _shadowTiles.pixmap( 5 ) ), - createPixmap( _shadowTiles.pixmap( 8 ) ), - createPixmap( _shadowTiles.pixmap( 7 ) ), - createPixmap( _shadowTiles.pixmap( 6 ) ), - createPixmap( _shadowTiles.pixmap( 3 ) ), - createPixmap( _shadowTiles.pixmap( 0 ) ) + _tiles = { + createTile( _shadowTiles.pixmap( 1 ) ), + createTile( _shadowTiles.pixmap( 2 ) ), + createTile( _shadowTiles.pixmap( 5 ) ), + createTile( _shadowTiles.pixmap( 8 ) ), + createTile( _shadowTiles.pixmap( 7 ) ), + createTile( _shadowTiles.pixmap( 6 ) ), + createTile( _shadowTiles.pixmap( 3 ) ), + createTile( _shadowTiles.pixmap( 0 ) ) }; } - // return relevant list of pixmap handles - return _pixmaps; + // return relevant list of shadow tiles + return _tiles; } //______________________________________________ - quint32 ShadowHelper::createPixmap( const QPixmap& source ) - { - - // do nothing for invalid pixmaps - if( source.isNull() ) return 0; - if( !Helper::isX11() ) return 0; - - /* - in some cases, pixmap handle is invalid. This is the case notably - when Qt uses to RasterEngine. In this case, we create an X11 Pixmap - explicitly and draw the source pixmap on it. - */ - - #if BREEZE_HAVE_X11 - - const int width( source.width() ); - const int height( source.height() ); - - // create X11 pixmap - xcb_pixmap_t pixmap = xcb_generate_id( Helper::connection() ); - xcb_create_pixmap( Helper::connection(), 32, pixmap, QX11Info::appRootWindow(), width, height ); - - // create gc - if( !_gc ) - { - _gc = xcb_generate_id( Helper::connection() ); - xcb_create_gc( Helper::connection(), _gc, pixmap, 0, nullptr ); - } - - // create image from QPixmap and assign to pixmap - QImage image( source.toImage() ); - - - #if QT_VERSION >= 0x051000 - xcb_put_image( Helper::connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, _gc, image.width(), image.height(), 0, 0, 0, 32, image.sizeInBytes(), image.constBits()); - #else - xcb_put_image( Helper::connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, _gc, image.width(), image.height(), 0, 0, 0, 32, image.byteCount(), image.constBits()); - #endif - - return pixmap; - - #else - return 0; - #endif - - } - - //_______________________________________________________ - bool ShadowHelper::installShadows( QWidget* widget ) + KWindowShadowTile::Ptr ShadowHelper::createTile( const QPixmap& source ) { - if( !widget ) return false; - /* - From bespin code. Supposedly prevent playing with some 'pseudo-widgets' - that have winId matching some other -random- window - */ - if( !(widget->testAttribute(Qt::WA_WState_Created) && widget->internalWinId() )) - { return false; } - - // create shadow tiles if needed - shadowTiles(); + KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create(); + tile->setImage( source.toImage() ); + return tile; - if( !_shadowTiles.isValid() ) return false; - - if( Helper::isX11() ) return installX11Shadows( widget ); - if( Helper::isWayland() ) return installWaylandShadows( widget ); - - return false; } //_______________________________________________________ - bool ShadowHelper::installX11Shadows( QWidget* widget ) + void ShadowHelper::installShadows( QWidget* widget ) { - #if BREEZE_HAVE_X11 - #ifndef QT_NO_XRENDER - - // create pixmap handles if needed - QVector data( createPixmapHandles() ); - if( data.size() != numPixmaps ) return false; - - const QMargins margins = shadowMargins( widget ); - const quint32 topSize = margins.top(); - const quint32 bottomSize = margins.bottom(); - const quint32 leftSize( margins.left() ); - const quint32 rightSize( margins.right() ); - - // assign to data and xcb property - data << QVector{topSize, rightSize, bottomSize, leftSize}; - xcb_change_property( Helper::connection(), XCB_PROP_MODE_REPLACE, widget->winId(), _atom, XCB_ATOM_CARDINAL, 32, data.size(), data.constData() ); - xcb_flush( Helper::connection() ); - - return true; - - #endif - #endif + if( !widget ) return; - return false; + // only toplevel widgets can cast drop-shadows + if( !widget->isTopLevel() ) return; - } - - //_______________________________________________________ - bool ShadowHelper::installWaylandShadows( QWidget* widget ) - { - #if BREEZE_HAVE_KWAYLAND - if( widget->windowHandle()->parent() ) return false; - if( !_shadowManager || !_shmPool ) return false; - - // create shadow - using namespace KWayland::Client; - auto s = Surface::fromWindow( widget->windowHandle() ); - if( !s ) return false; - - auto shadow = _shadowManager->createShadow( s, widget ); - if( !shadow->isValid() ) return false; - - // add the shadow elements - shadow->attachTop( _shmPool->createBuffer( _shadowTiles.pixmap( 1 ).toImage() ) ); - shadow->attachTopRight( _shmPool->createBuffer( _shadowTiles.pixmap( 2 ).toImage() ) ); - shadow->attachRight( _shmPool->createBuffer( _shadowTiles.pixmap( 5 ).toImage() ) ); - shadow->attachBottomRight( _shmPool->createBuffer( _shadowTiles.pixmap( 8 ).toImage() ) ); - shadow->attachBottom( _shmPool->createBuffer( _shadowTiles.pixmap( 7 ).toImage() ) ); - shadow->attachBottomLeft( _shmPool->createBuffer( _shadowTiles.pixmap( 6 ).toImage() ) ); - shadow->attachLeft( _shmPool->createBuffer( _shadowTiles.pixmap( 3 ).toImage() ) ); - shadow->attachTopLeft( _shmPool->createBuffer( _shadowTiles.pixmap( 0 ).toImage() ) ); - - shadow->setOffsets( shadowMargins( widget ) ); - shadow->commit(); - s->commit( Surface::CommitFlag::None ); - _widgetSurfaces.insert(widget, s); + // widget must have valid native window + if( !widget->testAttribute( Qt::WA_WState_Created ) ) return; - return true; - #else - Q_UNUSED( widget ); + #if OXYGEN_HAVE_KWAYLAND + if( widget->windowHandle()->parent() ) return; #endif - return false; + // create shadow tiles if needed + shadowTiles(); + if( !_shadowTiles.isValid() ) return; + + // create platform shadow tiles if needed + const QVector& tiles = createShadowTiles(); + if( tiles.count() != numTiles ) return; + + // find a shadow associated with the widget + KWindowShadow*& shadow = _shadows[ widget ]; + + // we want the shadow to be deleted after the decorated window is destroyed + if( !shadow ) + { shadow = new KWindowShadow( widget->windowHandle() ); } + + if( shadow->isCreated() ) + { shadow->destroy(); } + + shadow->setTopTile( tiles[ 0 ] ); + shadow->setTopRightTile( tiles[ 1 ] ); + shadow->setRightTile( tiles[ 2 ] ); + shadow->setBottomRightTile( tiles[ 3 ] ); + shadow->setBottomTile( tiles[ 4 ] ); + shadow->setBottomLeftTile( tiles[ 5 ] ); + shadow->setLeftTile( tiles[ 6 ] ); + shadow->setTopLeftTile( tiles[ 7 ] ); + shadow->setPadding( shadowMargins( widget ) ); + shadow->setWindow( widget->windowHandle() ); + shadow->create(); } //_______________________________________________________ @@ -627,40 +471,9 @@ } //_______________________________________________________ - void ShadowHelper::uninstallShadows( QWidget* widget ) const + void ShadowHelper::uninstallShadows( QWidget* widget ) { - if( !( widget && widget->testAttribute(Qt::WA_WState_Created) ) ) return; - if( Helper::isX11() ) uninstallX11Shadows( widget ); - if( Helper::isWayland() ) uninstallWaylandShadows( widget ); - } - - //_______________________________________________________ - void ShadowHelper::uninstallX11Shadows( QWidget* widget ) const - { - #if BREEZE_HAVE_X11 - xcb_delete_property( Helper::connection(), widget->winId(), _atom); - #else - Q_UNUSED( widget ) - #endif - - } - - //_______________________________________________________ - void ShadowHelper::uninstallWaylandShadows( QWidget* widget ) const - { - #if BREEZE_HAVE_KWAYLAND - if( widget->windowHandle() && widget->windowHandle()->parent() ) return; - if( !_shadowManager ) return; - - using namespace KWayland::Client; - auto s = Surface::fromWindow( widget->windowHandle() ); - if( !s ) return; - - _shadowManager->removeShadow( s ); - s->commit( Surface::CommitFlag::None ); - #else - Q_UNUSED( widget ) - #endif + delete _shadows.take( widget ); } }