diff --git a/kstyle/oxygenshadowhelper.h b/kstyle/oxygenshadowhelper.h --- a/kstyle/oxygenshadowhelper.h +++ b/kstyle/oxygenshadowhelper.h @@ -7,6 +7,7 @@ // ------------------- // // Copyright (c) 2010 Hugo Pereira Da Costa +// Copyright (c) 2020 Vlad Zahorodnii // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -29,26 +30,13 @@ #include "oxygen.h" #include "oxygentileset.h" -#include "config-liboxygen.h" + +#include #include #include #include - -#if OXYGEN_HAVE_X11 -#include -#endif - -#if OXYGEN_HAVE_KWAYLAND -namespace KWayland -{ -namespace Client -{ - class ShadowManager; - class ShmPool; -} -} -#endif +#include namespace Oxygen { @@ -65,11 +53,6 @@ public: - //*@name property names - //@{ - static const char netWMShadowAtomName[]; - //@} - //* constructor ShadowHelper( QObject*, StyleHelper& ); @@ -124,36 +107,17 @@ ShadowCache& shadowCache( void ) { return *_shadowCache; } - // create pixmap handles from tileset - const QVector& createPixmapHandles( bool isDockWidget ); + // create shadow tiles from tileset + const QVector& createPlatformTiles( bool isDockWidget ); - // create pixmap handle from pixmap - quint32 createPixmap( const QPixmap& ); + // create a shadow tile from pixmap + KWindowShadowTile::Ptr createPlatformTile( 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 - http://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; - - //* initializes the Wayland specific parts - void initializeWayland(); + void uninstallShadows( QWidget * ); //* gets the shadow margins for the given widget QMargins shadowMargins( QWidget* ) const; @@ -165,45 +129,29 @@ ShadowCache* _shadowCache; //* set of registered widgets - QMap _widgets; + QSet _widgets; + + //* map of managed shadows + QMap _shadows; //*@name shadow tilesets //@{ TileSet _tiles; TileSet _dockTiles; //@} - //* number of pixmaps - enum { numPixmaps = 8 }; + //* number of shadow tiles + enum { numTiles = 8 }; - //*@name pixmaps + //*@name shared shadow tiles //@{ - QVector _pixmaps; - QVector _dockPixmaps; + QVector _platformTiles; + QVector _platformDockTiles; //@} //* shadow size int _size; - #if OXYGEN_HAVE_X11 - - //* graphical context - xcb_gcontext_t _gc; - - //* shadow atom - xcb_atom_t _atom; - - #endif - - #if OXYGEN_HAVE_KWAYLAND - - //* The Wayland shadow manager to create Shadows for Surfaces (QWindow) - KWayland::Client::ShadowManager* _shadowManager; - //* The Wayland Shared memory pool to share the shadow pixmaps with compositor - KWayland::Client::ShmPool* _shmPool; - - #endif - }; } diff --git a/kstyle/oxygenshadowhelper.cpp b/kstyle/oxygenshadowhelper.cpp --- a/kstyle/oxygenshadowhelper.cpp +++ b/kstyle/oxygenshadowhelper.cpp @@ -4,6 +4,7 @@ // ------------------- // // Copyright (c) 2010 Hugo Pereira Da Costa +// Copyright (c) 2020 Vlad Zahorodnii // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -38,103 +39,32 @@ #include #include -#if OXYGEN_HAVE_X11 -#include -#endif - -#if OXYGEN_HAVE_KWAYLAND -#include -#include -#include -#include -#include -#include -#endif - namespace Oxygen { - const char ShadowHelper::netWMShadowAtomName[] = "_KDE_NET_WM_SHADOW"; - //_____________________________________________________ ShadowHelper::ShadowHelper( QObject* parent, StyleHelper& helper ): QObject( parent ), _helper( helper ), _shadowCache( new ShadowCache( helper ) ), _size( 0 ) - #if OXYGEN_HAVE_X11 - ,_gc( 0 ), - _atom( 0 ) - #endif - #if OXYGEN_HAVE_KWAYLAND - , _shadowManager( Q_NULLPTR ) - , _shmPool( Q_NULLPTR ) - #endif { - initializeWayland(); } //_______________________________________________________ ShadowHelper::~ShadowHelper( void ) { - #if OXYGEN_HAVE_X11 - if( _helper.isX11() ) - { - foreach( const quint32& value, _pixmaps ) xcb_free_pixmap( _helper.connection(), value ); - foreach( const quint32& value, _dockPixmaps ) xcb_free_pixmap( _helper.connection(), value ); - } - #endif - + qDeleteAll( _shadows ); delete _shadowCache; } - //_______________________________________________________ - void ShadowHelper::initializeWayland() - { - #if OXYGEN_HAVE_KWAYLAND - if( !Helper::isWayland() ) return; - - using namespace KWayland::Client; - auto connection = ConnectionThread::fromApplication( this ); - if( !connection ) { - return; - } - Registry *registry = new Registry( this ); - 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, this ); - } - const auto shmInterface = registry->interface( Registry::Interface::Shm ); - if( shmInterface.name != 0 ) { - _shmPool = registry->createShmPool( shmInterface.name, shmInterface.version, this ); - } - } - ); - - registry->setup(); - connection->roundtrip(); - #endif - } - //______________________________________________ void ShadowHelper::reset( void ) { - #if OXYGEN_HAVE_X11 - if( _helper.isX11() ) - { - // round pixmaps - foreach( const quint32& value, _pixmaps ) xcb_free_pixmap( _helper.connection(), value ); - foreach( const quint32& value, _dockPixmaps ) xcb_free_pixmap( _helper.connection(), value ); - } - #endif - - _pixmaps.clear(); - _dockPixmaps.clear(); + _platformTiles.clear(); + _platformDockTiles.clear(); _tiles = TileSet(); _dockTiles = TileSet(); @@ -156,8 +86,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 ); @@ -221,8 +151,8 @@ _dockTiles = TileSet( pixmap, pixmapSize.width()/2, pixmapSize.height()/2, 1, 1 ); // 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 ); } } @@ -236,17 +166,20 @@ // cast widget QWidget* widget( static_cast( object ) ); - // install shadows and update winId - if( installShadows( widget ) ) - { _widgets.insert( widget, widget->winId() ); } + // install shadows on the widget again + installShadows( widget ); return false; } //_______________________________________________________ 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 @@ -291,203 +224,95 @@ } //______________________________________________ - const QVector& ShadowHelper::createPixmapHandles( bool isDockWidget ) + const QVector& ShadowHelper::createPlatformTiles( bool isDockWidget ) { - /*! - shadow atom and property specification available at - http://community.kde.org/KWin/Shadow - */ - - // create atom - #if OXYGEN_HAVE_X11 - if( !_atom && _helper.isX11() ) _atom = _helper.createAtom( QLatin1String( netWMShadowAtomName ) ); - #endif - // make sure size is valid - if( _size <= 0 ) return _pixmaps; + if( _size <= 0 ) return _platformTiles; - // make sure pixmaps are not already initialized + // make sure shadow tiles are not already initialized if( isDockWidget ) { - // make sure pixmaps are not already initialized - if( _dockPixmaps.empty() && _dockTiles.isValid() ) + // make sure shadow tiles are not already initialized + if( _platformDockTiles.isEmpty() && _dockTiles.isValid() ) { - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 1 ) ) ); - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 2 ) ) ); - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 5 ) ) ); - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 8 ) ) ); - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 7 ) ) ); - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 6 ) ) ); - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 3 ) ) ); - _dockPixmaps.append( createPixmap( _dockTiles.pixmap( 0 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 1 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 2 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 5 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 8 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 7 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 6 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 3 ) ) ); + _platformDockTiles.append( createPlatformTile( _dockTiles.pixmap( 0 ) ) ); } - } else if( _pixmaps.empty() && _tiles.isValid() ) { + } else if( _platformTiles.isEmpty() && _tiles.isValid() ) { - _pixmaps.append( createPixmap( _tiles.pixmap( 1 ) ) ); - _pixmaps.append( createPixmap( _tiles.pixmap( 2 ) ) ); - _pixmaps.append( createPixmap( _tiles.pixmap( 5 ) ) ); - _pixmaps.append( createPixmap( _tiles.pixmap( 8 ) ) ); - _pixmaps.append( createPixmap( _tiles.pixmap( 7 ) ) ); - _pixmaps.append( createPixmap( _tiles.pixmap( 6 ) ) ); - _pixmaps.append( createPixmap( _tiles.pixmap( 3 ) ) ); - _pixmaps.append( createPixmap( _tiles.pixmap( 0 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 1 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 2 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 5 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 8 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 7 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 6 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 3 ) ) ); + _platformTiles.append( createPlatformTile( _tiles.pixmap( 0 ) ) ); } - // return relevant list of pixmap handles - return isDockWidget ? _dockPixmaps:_pixmaps; + // return relevant list of shadow tiles + return isDockWidget ? _platformDockTiles : _platformTiles; } //______________________________________________ - quint32 ShadowHelper::createPixmap( const QPixmap& source ) + KWindowShadowTile::Ptr ShadowHelper::createPlatformTile(const QPixmap& pixmap) { - // 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 OXYGEN_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, 0x0 ); - } - - // create image from QPixmap and assign to pixmap - QImage image( source.toImage() ); - xcb_put_image( _helper.connection(), XCB_IMAGE_FORMAT_Z_PIXMAP, pixmap, _gc, image.width(), image.height(), 0, 0, 0, 32, image.byteCount(), image.constBits()); - - return pixmap; - - #else - return 0; - #endif + KWindowShadowTile::Ptr tile = KWindowShadowTile::Ptr::create(); + tile->setImage( pixmap.toImage() ); + return tile; } //_______________________________________________________ - bool ShadowHelper::installShadows( QWidget* widget ) + void ShadowHelper::installShadows( QWidget* widget ) { - if( !widget ) return false; + if( !widget ) return; - /* - From bespin code. Supposibly 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; } + // only toplevel widgets can cast drop-shadows + if( !widget->isWindow() ) return; - if( Helper::isX11() ) return installX11Shadows( widget ); - if( Helper::isWayland() ) return installWaylandShadows( widget ); + // widget must have valid native window + if( !widget->testAttribute( Qt::WA_WState_Created ) ) return; - return false; - } - - //_______________________________________________________ - bool ShadowHelper::installX11Shadows( QWidget* widget ) - { - - #if OXYGEN_HAVE_X11 - #ifndef QT_NO_XRENDER - - // create pixmap handles if needed + // create shadow tiles if needed const bool isDockWidget( this->isDockWidget( widget ) || this->isToolBar( widget ) ); - const QVector& pixmaps( createPixmapHandles( isDockWidget ) ); - if( pixmaps.size() != numPixmaps ) return false; - - // create data - // add pixmap handles - QVector data; - foreach( const quint32& value, pixmaps ) - { data.append( value ); } - - const QMargins margins = shadowMargins( widget ); - data << int(margins.top()) << int(margins.right()) << int(margins.bottom()) << int(margins.left()); - - 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 - - return false; - - } - - //_______________________________________________________ - void ShadowHelper::uninstallX11Shadows( QWidget* widget ) const - { - - #if OXYGEN_HAVE_X11 - xcb_delete_property( _helper.connection(), widget->winId(), _atom); - #else - Q_UNUSED( widget ) - #endif - - } - - //_______________________________________________________ - bool ShadowHelper::installWaylandShadows( QWidget* widget ) - { - #if OXYGEN_HAVE_KWAYLAND - if( widget->windowHandle()->parent() ) return false; - if( !_shadowManager || !_shmPool ) return false; - - const bool isDockWidget( this->isDockWidget( widget ) || this->isToolBar( widget ) ); - const TileSet &tiles = isDockWidget ? _dockTiles : _tiles; - - if( !tiles.isValid() ) 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( tiles.pixmap( 1 ).toImage() ) ); - shadow->attachTopRight( _shmPool->createBuffer( tiles.pixmap( 2 ).toImage() ) ); - shadow->attachRight( _shmPool->createBuffer( tiles.pixmap( 5 ).toImage() ) ); - shadow->attachBottomRight( _shmPool->createBuffer( tiles.pixmap( 8 ).toImage() ) ); - shadow->attachBottom( _shmPool->createBuffer( tiles.pixmap( 7 ).toImage() ) ); - shadow->attachBottomLeft( _shmPool->createBuffer( tiles.pixmap( 6 ).toImage() ) ); - shadow->attachLeft( _shmPool->createBuffer( tiles.pixmap( 3 ).toImage() ) ); - shadow->attachTopLeft( _shmPool->createBuffer( tiles.pixmap( 0 ).toImage() ) ); - - shadow->setOffsets( shadowMargins( widget ) ); - shadow->commit(); - s->commit( Surface::CommitFlag::None ); - - return true; - #else - Q_UNUSED( widget ); - #endif - - return false; + const QVector& tiles = createPlatformTiles( isDockWidget ); + 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->setWindow( widget->windowHandle() ); + shadow->setPadding( shadowMargins( widget ) ); + shadow->create(); } //_______________________________________________________ @@ -549,29 +374,9 @@ } //_______________________________________________________ - void ShadowHelper::uninstallShadows( QWidget* widget ) const - { - if( !( widget && widget->testAttribute(Qt::WA_WState_Created) ) ) return; - if( Helper::isX11() ) uninstallX11Shadows( widget ); - if( Helper::isWayland() ) uninstallWaylandShadows( widget ); - } - - //_______________________________________________________ - void ShadowHelper::uninstallWaylandShadows( QWidget* widget ) const + void ShadowHelper::uninstallShadows( QWidget* widget ) { - #if OXYGEN_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 ); } }