From c7f515630db7e985d83991dd0e9053634ad7d034 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Wed, 5 Jul 2006 20:52:57 +0000 Subject: [PATCH] Support for transparency. svn path=/branches/work/kwin_composite/; revision=558690 --- client.cpp | 20 ++++- client.h | 2 + composite.cpp | 34 +++---- events.cpp | 21 +++++ manage.cpp | 3 + scene.cpp | 12 +++ scene.h | 3 + scene_xrender.cpp | 224 ++++++++++++++++++++++++++++++++++++++-------- scene_xrender.h | 21 +++++ toplevel.cpp | 7 +- toplevel.h | 8 ++ unmanaged.cpp | 20 +++++ unmanaged.h | 4 + 13 files changed, 315 insertions(+), 64 deletions(-) diff --git a/client.cpp b/client.cpp index 6885d8970b..c5a7143ad4 100644 --- a/client.cpp +++ b/client.cpp @@ -28,6 +28,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "atoms.h" #include "notifications.h" #include "rules.h" +#include "scene.h" #include #include @@ -279,6 +280,8 @@ void Client::updateDecoration( bool check_workspace_pos, bool force ) workarea_diff_x = save_workarea_diff_x; workarea_diff_y = save_workarea_diff_y; do_show = true; + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); } else destroyDecoration(); @@ -305,6 +308,8 @@ void Client::destroyDecoration() move( grav ); workarea_diff_x = save_workarea_diff_x; workarea_diff_y = save_workarea_diff_y; + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); } } @@ -423,16 +428,14 @@ void Client::setUserNoBorder( bool set ) void Client::updateShape() { if ( shape() ) - { XShapeCombineShape(display(), frameId(), ShapeBounding, clientPos().x(), clientPos().y(), window(), ShapeBounding, ShapeSet); - } else - { XShapeCombineMask( display(), frameId(), ShapeBounding, 0, 0, None, ShapeSet); - } + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); // workaround for #19644 - shaped windows shouldn't have decoration if( shape() && !noBorder()) { @@ -467,6 +470,8 @@ void Client::setMask( const QRegion& reg, int mode ) xrects, rects.count(), ShapeSet, mode ); delete[] xrects; } + if( scene != NULL ) + scene->windowGeometryShapeChanged( this ); } QRegion Client::mask() const @@ -1788,6 +1793,13 @@ bool Client::hasShape( Window w ) return boundingShaped != 0; } +float Client::opacity() const + { + if( info->opacity() == 0xffffffff ) + return 1.0; + return info->opacity() * 1.0 / 0xffffffff; + } + void Client::debug( kdbgstream& stream ) const { stream << "\'ID:" << window() << ";WMCLASS:" << resourceClass() << ":" << resourceName() << ";Caption:" << caption() << "\'"; diff --git a/client.h b/client.h index 2509dbf4a9..8fa6434585 100644 --- a/client.h +++ b/client.h @@ -191,6 +191,8 @@ class Client // shape extensions bool shape() const; void updateShape(); + + virtual float opacity() const; void setGeometry( int x, int y, int w, int h, ForceGeometry_t force = NormalGeometrySet ); void setGeometry( const QRect& r, ForceGeometry_t force = NormalGeometrySet ); diff --git a/composite.cpp b/composite.cpp index 7d7a97fa16..fa06b09d9f 100644 --- a/composite.cpp +++ b/composite.cpp @@ -47,11 +47,21 @@ void Workspace::finishCompositing() // TODO stop tracking unmanaged windows delete scene; scene = NULL; + for( ClientList::ConstIterator it = clients.begin(); + it != clients.end(); + ++it ) + { // forward all opacity values to the frame in case there'll be other CM running + if( (*it)->opacity() != 1.0 ) + { + NETWinInfo i( display(), (*it)->frameId(), rootWindow(), 0 ); + i.setOpacity( long((*it)->opacity() * 0xffffffff )); + } + } } void Workspace::addDamage( const QRect& r ) { - addDamage( r.x(), r.y(), r.height(), r.width()); + addDamage( r.x(), r.y(), r.width(), r.height()); } void Workspace::addDamage( int x, int y, int w, int h ) @@ -64,28 +74,6 @@ void Workspace::addDamage( int x, int y, int w, int h ) addDamage( XFixesCreateRegion( display(), &r, 1 ), true ); } -struct XXX - { - XXX( XserverRegion r ) : rr( r ) {} - XserverRegion rr; - }; - -kdbgstream& operator<<( kdbgstream& stream, XXX r ) - { - if( r.rr == None ) - return stream << "NONE"; - int num; - XRectangle* rects = XFixesFetchRegion( display(), r.rr, &num ); - if( rects == NULL || num == 0 ) - return stream << "NONE"; - for( int i = 0; - i < num; - ++i ) - stream << "[" << rects[ i ].x << "+" << rects[ i ].y << " " << rects[ i ].width << "x" << rects[ i ].height << "]"; - return stream; - } - - void Workspace::addDamage( XserverRegion r, bool destroy ) { if( !compositing()) diff --git a/events.cpp b/events.cpp index 5bf9b6e7dd..5bcb9a179d 100644 --- a/events.cpp +++ b/events.cpp @@ -22,6 +22,7 @@ License. See the file "COPYING" for the exact licensing terms. #include "group.h" #include "rules.h" #include "unmanaged.h" +#include "scene.h" #include #include @@ -572,6 +573,19 @@ bool Client::windowEvent( XEvent* e ) if( demandAttentionKNotifyTimer != NULL ) demandAttentionKNotify(); } + if( dirty[ WinInfo::PROTOCOLS2 ] & NET::WM2Opacity ) + { + if( compositing()) + { + workspace()->addDamage( geometry()); + scene->windowOpacityChanged( this ); + } + else + { // forward to the frame if there's possibly another compositing manager running + NETWinInfo i( display(), frameId(), rootWindow(), 0 ); + i.setOpacity( info->opacity()); + } + } } // TODO move all focus handling stuff to separate file? @@ -1594,6 +1608,13 @@ void Client::keyPressEvent( uint key_code ) bool Unmanaged::windowEvent( XEvent* e ) { + unsigned long dirty[ 2 ]; + info->event( e, dirty, 2 ); // pass through the NET stuff + if( dirty[ NETWinInfo::PROTOCOLS2 ] & NET::WM2Opacity ) + { + scene->windowOpacityChanged( this ); + workspace()->addDamage( geometry()); + } switch (e->type) { case UnmapNotify: diff --git a/manage.cpp b/manage.cpp index 4f96dc780c..a95c76c552 100644 --- a/manage.cpp +++ b/manage.cpp @@ -53,6 +53,8 @@ bool Client::manage( Window w, bool isMapped ) embedClient( w, attr ); + vis = attr.visual; + setupCompositing(); // SELI order all these things in some sane manner @@ -82,6 +84,7 @@ bool Client::manage( Window w, bool isMapped ) NET::WM2UserTime | NET::WM2StartupId | NET::WM2ExtendedStrut | + NET::WM2Opacity | 0; info = new WinInfo( this, display(), client, rootWindow(), properties, 2 ); diff --git a/scene.cpp b/scene.cpp index 2b1eb6e8d3..8e8f42c54d 100644 --- a/scene.cpp +++ b/scene.cpp @@ -31,6 +31,18 @@ void Scene::setWindows( const ToplevelList& list ) windows = list; } +void Scene::windowGeometryShapeChanged( Toplevel* ) + { + } + +void Scene::windowOpacityChanged( Toplevel* ) + { + } + +void Scene::windowDeleted( Toplevel* ) + { + } + Scene* scene; } // namespace diff --git a/scene.h b/scene.h index df70978836..9db48c450b 100644 --- a/scene.h +++ b/scene.h @@ -25,6 +25,9 @@ class Scene virtual ~Scene(); void setWindows( const ToplevelList& list ); virtual void paint( XserverRegion damage ) = 0; + virtual void windowGeometryShapeChanged( Toplevel* ); + virtual void windowOpacityChanged( Toplevel* ); + virtual void windowDeleted( Toplevel* ); protected: Workspace* wspace; ToplevelList windows; diff --git a/scene_xrender.cpp b/scene_xrender.cpp index 395150b21b..99f6088463 100644 --- a/scene_xrender.cpp +++ b/scene_xrender.cpp @@ -35,6 +35,178 @@ SceneXrender::~SceneXrender() { XRenderFreePicture( display(), front ); XRenderFreePicture( display(), buffer ); + for( QMap< Toplevel*, WindowData >::Iterator it = window_data.begin(); + it != window_data.end(); + ++it ) + (*it).free(); + } + +void SceneXrender::paint( XserverRegion damage ) + { + // Use the damage region as the clip region for the root window + XFixesSetPictureClipRegion( display(), front, 0, 0, damage ); + // Client list for clients that are either translucent or have a shadow + ToplevelList translucents; + // Draw each opaque window top to bottom, subtracting the bounding rect of + // each window from the clip region after it's been drawn. + for( int i = windows.count() - 1; + i >= 0; + --i ) + { + Toplevel* c = windows[ i ]; + checkWindowData( c ); + if( isOpaque( c )) + { + Picture picture = windowPicture( c ); + Picture shape = windowShape( c ); + if( picture != None && shape != None ) + { + // Set the clip region for the buffer to the damage region, and + // subtract the clients shape from the damage region + XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage ); + XFixesSubtractRegion( display(), damage, damage, shape ); + XRenderComposite( display(), PictOpSrc, picture, None, buffer, 0, 0, 0, 0, + c->x(), c->y(), c->width(), c->height()); + } + } + saveWindowClipRegion( c, damage ); + translucents.prepend( c ); + } + // Fill any areas of the root window not covered by windows + XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage ); + XRenderColor col = { 0xffff, 0xffff, 0xffff, 0xffff }; + XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight()); + // Now walk the list bottom to top, drawing translucent windows and shadows. + // That we draw bottom to top is important now since we're drawing translucent objects. + for( int i = 0; + i < translucents.count(); + ++i ) + { + Toplevel* c = translucents[ i ]; + // Restore the previously saved clip region + XserverRegion r = savedWindowClipRegion( c ); + XFixesSetPictureClipRegion( display(), buffer, 0, 0, r ); + if( !isOpaque( c )) + { + Picture picture = windowPicture( c ); + Picture alpha = windowAlphaMask( c ); + if( picture != None ) + // TODO clip also using shape? also above? + XRenderComposite( display(), PictOpOver, picture, alpha, buffer, 0, 0, 0, 0, + c->x(), c->y(), c->width(), c->height()); + } + XFixesDestroyRegion( display(), r ); + } + // copy composed buffer to the root window + XFixesSetPictureClipRegion( display(), buffer, 0, 0, None ); + XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight()); + XFlush( display()); + } + +void SceneXrender::checkWindowData( Toplevel* c ) + { + if( !window_data.contains( c )) + { + window_data[ c ] = WindowData(); + window_data[ c ].format = XRenderFindVisualFormat( display(), c->visual()); + } + } + +void SceneXrender::windowGeometryShapeChanged( Toplevel* c ) + { + if( !window_data.contains( c )) + return; + WindowData& data = window_data[ c ]; + if( data.picture != None ) + XRenderFreePicture( display(), data.picture ); + data.picture = None; + if( data.alpha != None ) + XRenderFreePicture( display(), data.alpha ); + data.alpha = None; + if( data.shape != None ) + XRenderFreePicture( display(), data.shape ); + data.shape = None; + } + +void SceneXrender::windowOpacityChanged( Toplevel* c ) + { + if( !window_data.contains( c )) + return; + WindowData& data = window_data[ c ]; + if( data.alpha != None ) + XRenderFreePicture( display(), data.alpha ); + data.alpha = None; + } + +void SceneXrender::windowDeleted( Toplevel* c ) + { + if( !window_data.contains( c )) + return; + window_data[ c ].free(); + window_data.remove( c ); + } + +Picture SceneXrender::windowPicture( Toplevel* c ) + { + WindowData& data = window_data[ c ]; + if( data.picture == None && data.format != NULL ) + data.picture = XRenderCreatePicture( display(), c->windowPixmap(), data.format, 0, 0 ); + return data.picture; + } + +void SceneXrender::saveWindowClipRegion( Toplevel* c, XserverRegion r ) + { + WindowData& data = window_data[ c ]; + data.saved_clip_region = XFixesCreateRegion( display(), NULL, 0 ); + XFixesCopyRegion( display(), data.saved_clip_region, r ); + } + +XserverRegion SceneXrender::savedWindowClipRegion( Toplevel* c ) + { // always called after saveWindowClipRegion(), also resets + WindowData& data = window_data[ c ]; + XserverRegion r = data.saved_clip_region; + data.saved_clip_region = None; + return r; + } + +bool SceneXrender::isOpaque( Toplevel* c ) const + { + const WindowData& data = window_data[ c ]; + if( data.format->type == PictTypeDirect && data.format->direct.alphaMask ) + return false; + if( c->opacity() != 1.0 ) + return false; + return true; + } + +Picture SceneXrender::windowAlphaMask( Toplevel* c ) + { + if( isOpaque( c )) + return None; + WindowData& data = window_data[ c ]; + if( data.alpha != None ) + return data.alpha; + Pixmap pixmap = XCreatePixmap( display(), rootWindow(), 1, 1, 8 ); + XRenderPictFormat* format = XRenderFindStandardFormat( display(), PictStandardA8 ); + XRenderPictureAttributes pa; + pa.repeat = True; + data.alpha = XRenderCreatePicture( display(), pixmap, format, CPRepeat, &pa ); + XFreePixmap( display(), pixmap ); + XRenderColor col; + col.alpha = int( c->opacity() * 0xffff ); + XRenderFillRectangle( display(), PictOpSrc, data.alpha, &col, 0, 0, 1, 1 ); + return data.alpha; + } + +Picture SceneXrender::windowShape( Toplevel* c ) + { + WindowData& data = window_data[ c ]; + if( data.shape == None ) + { + data.shape = XFixesCreateRegionFromWindow( display(), c->handle(), WindowRegionBounding ); + XFixesTranslateRegion( display(), data.shape, c->x(), c->y()); + } + return data.shape; } // TODO handle xrandr changes @@ -48,43 +220,23 @@ void SceneXrender::createBuffer() XFreePixmap( display(), pixmap ); // The picture owns the pixmap now } -void SceneXrender::paint( XserverRegion damage ) +SceneXrender::WindowData::WindowData() + : picture( None ) + , format( NULL ) + , saved_clip_region( None ) + , alpha( None ) + , shape( None ) { - // Use the damage region as the clip region for the root window - XFixesSetPictureClipRegion( display(), front, 0, 0, damage ); - // Draw each opaque window top to bottom, subtracting the bounding rect of - // each window from the clip region after it's been drawn. - for( int i = windows.count() - 1; - i >= 0; - --i ) - { - Toplevel* c = windows[ i ]; - XWindowAttributes attrs; - if( !XGetWindowAttributes( display(), c->handle(), &attrs )) - continue; - if( XRenderPictFormat* clientFormat = XRenderFindVisualFormat( display(), attrs.visual )) - { - Picture picture = XRenderCreatePicture( display(), c->windowPixmap(), clientFormat, 0, 0 ); - // Set the clip region for the buffer to the damage region, and - // subtract the clients shape from the damage region - XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage ); - XserverRegion cr = XFixesCreateRegionFromWindow( display(), c->handle(), WindowRegionBounding ); - XFixesTranslateRegion( display(), cr, c->x(), c->y()); - XFixesSubtractRegion( display(), damage, damage, cr ); - XFixesDestroyRegion( display(), cr ); - XRenderComposite( display(), PictOpSrc, picture, None, buffer, 0, 0, 0, 0, - c->x(), c->y(), c->width(), c->height()); - XRenderFreePicture( display(), picture ); - } - } - // fill background - XFixesSetPictureClipRegion( display(), buffer, 0, 0, damage ); - XRenderColor col = { 0xffff, 0xffff, 0xffff, 0xffff }; - XRenderFillRectangle( display(), PictOpSrc, buffer, &col, 0, 0, displayWidth(), displayHeight()); - // copy composed buffer to the root window - XFixesSetPictureClipRegion( display(), buffer, 0, 0, None ); - XRenderComposite( display(), PictOpSrc, buffer, None, front, 0, 0, 0, 0, 0, 0, displayWidth(), displayHeight()); - XFlush( display()); + } + +void SceneXrender::WindowData::free() + { + if( picture != None ) + XRenderFreePicture( display(), picture ); + if( alpha != None ) + XRenderFreePicture( display(), alpha ); + if( shape != None ) + XRenderFreePicture( display(), shape ); } } // namespace diff --git a/scene_xrender.h b/scene_xrender.h index 1865572342..e50b529fed 100644 --- a/scene_xrender.h +++ b/scene_xrender.h @@ -28,11 +28,32 @@ class SceneXrender SceneXrender( Workspace* ws ); virtual ~SceneXrender(); virtual void paint( XserverRegion damage ); + virtual void windowGeometryShapeChanged( Toplevel* ); + virtual void windowOpacityChanged( Toplevel* ); + virtual void windowDeleted( Toplevel* ); private: void createBuffer(); + void checkWindowData( Toplevel* c ); + Picture windowPicture( Toplevel* c ); + void saveWindowClipRegion( Toplevel* c, XserverRegion r ); + XserverRegion savedWindowClipRegion( Toplevel* c ); + bool isOpaque( Toplevel* c ) const; + Picture windowAlphaMask( Toplevel* c ); + Picture windowShape( Toplevel* c ); XRenderPictFormat* format; Picture front; Picture buffer; + struct WindowData + { + WindowData(); + void free(); + Picture picture; + XRenderPictFormat* format; + XserverRegion saved_clip_region; + Picture alpha; + XserverRegion shape; + }; + QMap< Toplevel*, WindowData > window_data; }; } // namespace diff --git a/toplevel.cpp b/toplevel.cpp index 766e977863..3cf5c902fe 100644 --- a/toplevel.cpp +++ b/toplevel.cpp @@ -10,6 +10,8 @@ License. See the file "COPYING" for the exact licensing terms. #include "toplevel.h" +#include "scene.h" + namespace KWinInternal { @@ -18,13 +20,16 @@ Toplevel::Toplevel( Workspace* ws ) , wspace( ws ) , damage_handle( None ) , window_pixmap( None ) + , vis( None ) { } - + Toplevel::~Toplevel() { if( window_pixmap != None ) XFreePixmap( display(), window_pixmap ); + if( scene != NULL ) + scene->windowDeleted( this ); } #ifndef NDEBUG diff --git a/toplevel.h b/toplevel.h index 7e6e5fd448..35c4e85387 100644 --- a/toplevel.h +++ b/toplevel.h @@ -40,6 +40,8 @@ class Toplevel int width() const; int height() const; Pixmap windowPixmap() const; + Visual* visual() const; + virtual float opacity() const = 0; protected: void setupCompositing(); void finishCompositing(); @@ -47,6 +49,7 @@ class Toplevel void resetWindowPixmap(); void damageNotifyEvent( XDamageNotifyEvent* e ); QRect geom; + Visual* vis; virtual void debug( kdbgstream& stream ) const = 0; friend kdbgstream& operator<<( kdbgstream& stream, const Toplevel* ); private: @@ -112,6 +115,11 @@ inline QRect Toplevel::rect() const return QRect( 0, 0, width(), height()); } +inline Visual* Toplevel::visual() const + { + return vis; + } + #ifdef NDEBUG inline kndbgstream& operator<<( kndbgstream& stream, const Toplevel* ) { return stream; } diff --git a/unmanaged.cpp b/unmanaged.cpp index 5aa448e5d9..b4a683c3db 100644 --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -17,11 +17,13 @@ namespace KWinInternal Unmanaged::Unmanaged( Workspace* ws ) : Toplevel( ws ) + , info( NULL ) { } Unmanaged::~Unmanaged() { + delete info; } bool Unmanaged::track( Window w ) @@ -32,6 +34,17 @@ bool Unmanaged::track( Window w ) return false; setHandle( w ); geom = QRect( attr.x, attr.y, attr.width, attr.height ); + vis = attr.visual; + unsigned long properties[ 2 ]; + properties[ NETWinInfo::PROTOCOLS ] = + NET::WMWindowType | + NET::WMPid | + 0; + properties[ NETWinInfo::PROTOCOLS2 ] = + NET::WM2Opacity | + 0; + info = new NETWinInfo( display(), w, rootWindow(), properties, 2 ); + setupCompositing(); resetWindowPixmap(); workspace()->addDamage( geometry()); @@ -50,6 +63,13 @@ void Unmanaged::deleteUnmanaged( Unmanaged* c, allowed_t ) delete c; } +float Unmanaged::opacity() const + { + if( info->opacity() == 0xffffffff ) + return 1.0; + return info->opacity() * 1.0 / 0xffffffff; + } + void Unmanaged::debug( kdbgstream& stream ) const { stream << "\'ID:" << handle() << "\'"; diff --git a/unmanaged.h b/unmanaged.h index e8e0843638..19451f2d85 100644 --- a/unmanaged.h +++ b/unmanaged.h @@ -11,6 +11,8 @@ License. See the file "COPYING" for the exact licensing terms. #ifndef KWIN_UNMANAGED_H #define KWIN_UNMANAGED_H +#include + #include "toplevel.h" namespace KWinInternal @@ -27,6 +29,7 @@ class Unmanaged void release(); bool track( Window w ); static void deleteUnmanaged( Unmanaged* c, allowed_t ); + virtual float opacity() const; protected: virtual void debug( kdbgstream& stream ) const; private: @@ -34,6 +37,7 @@ class Unmanaged void mapNotifyEvent( XMapEvent* e ); void unmapNotifyEvent( XUnmapEvent*e ); void configureNotifyEvent( XConfigureEvent* e ); + NETWinInfo* info; }; } // namespace