Support for transparency.

svn path=/branches/work/kwin_composite/; revision=558690
icc-effect-5.14.5
Luboš Luňák 2006-07-05 20:52:57 +00:00
parent f400646a6d
commit c7f515630d
13 changed files with 315 additions and 64 deletions

View File

@ -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 <X11/extensions/shape.h>
#include <QX11Info>
@ -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() << "\'";

View File

@ -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 );

View File

@ -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())

View File

@ -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 <QWhatsThis>
#include <QApplication>
@ -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:

View File

@ -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 );

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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; }

View File

@ -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() << "\'";

View File

@ -11,6 +11,8 @@ License. See the file "COPYING" for the exact licensing terms.
#ifndef KWIN_UNMANAGED_H
#define KWIN_UNMANAGED_H
#include <netwm.h>
#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