Add support for dimming of inactive windows (accessibility).

FEATURE: 46226


svn path=/branches/work/kwin_composite/; revision=652255
This commit is contained in:
Luboš Luňák 2007-04-10 14:48:55 +00:00
parent 0353f3376e
commit d2dbc77323
11 changed files with 255 additions and 254 deletions

View file

@ -71,6 +71,8 @@ General TODO
* cursorPos() does not work reliably now (not from e.g. timers, it needs events), so it's disabled
* window grouping is not implemented for unmanaged windows (used e.g. by DimInactive)
OpenGL TODO
=================================
@ -237,3 +239,5 @@ Effects TODO
+ - something that presents all virtual desktops as being in grid (as in pager)
and zooms out of the old one and into the new one
- or whatever
* DimInactive flickers when switching between windows (temporarily no window becomes active)

View file

@ -12,6 +12,7 @@ License. See the file "COPYING" for the exact licensing terms.
#include "deleted.h"
#include "client.h"
#include "group.h"
#include "workspace.h"
#include "kdebug.h"
@ -286,6 +287,21 @@ void EffectWindowImpl::disablePainting( int reason )
sceneWindow()->disablePainting( reason );
}
void EffectWindowImpl::addRepaint( const QRect& r )
{
toplevel->addRepaint( r );
}
void EffectWindowImpl::addRepaint( int x, int y, int w, int h )
{
toplevel->addRepaint( x, y, w, h );
}
void EffectWindowImpl::addRepaintFull()
{
toplevel->addRepaintFull();
}
int EffectWindowImpl::desktop() const
{
return toplevel->desktop();
@ -304,6 +320,13 @@ QString EffectWindowImpl::caption() const
return "";
}
const EffectWindowGroup* EffectWindowImpl::group() const
{
if( Client* c = dynamic_cast< Client* >( toplevel ))
return c->group()->effectGroup();
return NULL; // TODO
}
bool EffectWindowImpl::isMinimized() const
{
if( Client* c = dynamic_cast<Client*>( toplevel ))
@ -479,5 +502,17 @@ EffectWindow* effectWindow( Scene::Window* w )
return ret;
}
//****************************************
// EffectWindowGroupImpl
//****************************************
EffectWindowList EffectWindowGroupImpl::members() const
{
EffectWindowList ret;
foreach( Toplevel* c, group->members())
ret.append( c->effectWindow());
return ret;
}
} // namespace

View file

@ -64,6 +64,9 @@ class EffectWindowImpl : public EffectWindow
virtual void enablePainting( int reason );
virtual void disablePainting( int reason );
virtual void addRepaint( const QRect& r );
virtual void addRepaint( int x, int y, int w, int h );
virtual void addRepaintFull();
virtual bool isDeleted() const;
@ -71,6 +74,7 @@ class EffectWindowImpl : public EffectWindow
virtual int desktop() const; // prefer isOnXXX()
virtual bool isMinimized() const;
virtual QString caption() const;
virtual const EffectWindowGroup* group() const;
virtual int x() const;
virtual int y() const;
@ -109,6 +113,22 @@ class EffectWindowImpl : public EffectWindow
Scene::Window* sw; // This one is used only during paint pass.
};
class EffectWindowGroupImpl
: public EffectWindowGroup
{
public:
EffectWindowGroupImpl( Group* g );
virtual EffectWindowList members() const;
private:
Group* group;
};
inline
EffectWindowGroupImpl::EffectWindowGroupImpl( Group* g )
: group( g )
{
}
EffectWindow* effectWindow( Toplevel* w );
EffectWindow* effectWindow( Scene::Window* w );

View file

@ -11,7 +11,7 @@ include_directories(
${CMAKE_SOURCE_DIR}/workspace/kwin/lib
)
KWIN4_ADD_EFFECT(builtins presentwindows.cpp shadow.cpp)
KWIN4_ADD_EFFECT(builtins presentwindows.cpp shadow.cpp diminactive.cpp)
install( FILES presentwindows.desktop shadow.desktop
install( FILES presentwindows.desktop shadow.desktop diminactive.desktop
DESTINATION ${DATA_INSTALL_DIR}/kwin/effects )

76
effects/diminactive.cpp Normal file
View file

@ -0,0 +1,76 @@
/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/
#include "diminactive.h"
namespace KWin
{
KWIN_EFFECT( DimInactive, DimInactiveEffect )
DimInactiveEffect::DimInactiveEffect()
: active( NULL )
{
dim_panels = true; // TODO config option
dim_by_group = true; // TODO config option
}
void DimInactiveEffect::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
{
bool dim = false;
if( active == NULL )
dim = true;
else if( dim_by_group && active->group() == w->group())
dim = false;
else if( !dim_by_group && active == w )
dim = false;
else if( w->isDock())
dim = dim_panels;
else
dim = true;
if( dim )
{
data.brightness *= 0.75;
data.saturation *= 0.75;
}
effects->paintWindow( w, mask, region, data );
}
void DimInactiveEffect::windowActivated( EffectWindow* w )
{
if( active != NULL )
{
if( dim_by_group )
{
if(( w == NULL || w->group() != active->group()) && active->group() != NULL )
{ // repaint windows that are not longer in active group
foreach( EffectWindow* tmp, active->group()->members())
tmp->addRepaintFull();
}
}
else
active->addRepaintFull();
}
active = w;
if( active != NULL )
if( dim_by_group )
{
if( active->group() != NULL )
{ // repaint newly active windows
foreach( EffectWindow* tmp, active->group()->members())
tmp->addRepaintFull();
}
}
else
active->addRepaintFull();
}
} // namespace

View file

@ -0,0 +1,4 @@
[Desktop Entry]
Encoding=UTF-8
Name=DimInactive
X-KDE-Library=kwin4_effect_builtins

36
effects/diminactive.h Normal file
View file

@ -0,0 +1,36 @@
/*****************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
You can Freely distribute this program under the GNU General Public
License. See the file "COPYING" for the exact licensing terms.
******************************************************************/
#ifndef KWIN_DIMINACTIVE_H
#define KWIN_DIMINACTIVE_H
// Include with base class for effects.
#include <kwineffects.h>
namespace KWin
{
class DimInactiveEffect
: public Effect
{
public:
DimInactiveEffect();
virtual void paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data );
virtual void windowActivated( EffectWindow* c );
private:
EffectWindow* active;
bool dim_panels; // do/don't dim also all panels
bool dim_by_group; // keep visible all windows from the active window's group or only the active window
};
} // namespace
#endif

298
group.cpp
View file

@ -21,6 +21,7 @@ License. See the file "COPYING" for the exact licensing terms.
#include "workspace.h"
#include "client.h"
#include "effects.h"
#include <assert.h>
#include <kstartupinfo.h>
@ -36,164 +37,6 @@ License. See the file "COPYING" for the exact licensing terms.
namespace KWin
{
/*
Consistency checks for window relations. Since transients are determinated
using Client::transiency_list and main windows are determined using Client::transientFor()
or the group for group transients, these have to match both ways.
*/
//#define ENABLE_TRANSIENCY_CHECK
#ifdef NDEBUG
#undef ENABLE_TRANSIENCY_CHECK
#endif
#ifdef ENABLE_TRANSIENCY_CHECK
static bool transiencyCheckNonExistent = false;
bool performTransiencyCheck()
{
bool ret = true;
ClientList clients = Workspace::self()->clients;
for( ClientList::ConstIterator it1 = clients.begin();
it1 != clients.end();
++it1 )
{
if( (*it1)->deleting )
continue;
if( (*it1)->in_group == NULL )
{
kdDebug() << "TC: " << *it1 << " in not in a group" << endl;
ret = false;
}
else if( !(*it1)->in_group->members().contains( *it1 ))
{
kdDebug() << "TC: " << *it1 << " has a group " << (*it1)->in_group << " but group does not contain it" << endl;
ret = false;
}
if( !(*it1)->isTransient())
{
if( !(*it1)->mainClients().isEmpty())
{
kdDebug() << "TC: " << *it1 << " is not transient, has main clients:" << (*it1)->mainClients() << endl;
ret = false;
}
}
else
{
ClientList mains = (*it1)->mainClients();
for( ClientList::ConstIterator it2 = mains.begin();
it2 != mains.end();
++it2 )
{
if( transiencyCheckNonExistent
&& !Workspace::self()->clients.contains( *it2 )
&& !Workspace::self()->desktops.contains( *it2 ))
{
kDebug() << "TC:" << *it1 << " has non-existent main client " << endl;
kDebug() << "TC2:" << *it2 << endl; // this may crash
ret = false;
continue;
}
if( !(*it2)->transients_list.contains( *it1 ))
{
kdDebug() << "TC:" << *it1 << " has main client " << *it2 << " but main client does not have it as a transient" << endl;
ret = false;
}
}
}
ClientList trans = (*it1)->transients_list;
for( ClientList::ConstIterator it2 = trans.begin();
it2 != trans.end();
++it2 )
{
if( transiencyCheckNonExistent
&& !Workspace::self()->clients.contains( *it2 )
&& !Workspace::self()->desktops.contains( *it2 ))
{
kDebug() << "TC:" << *it1 << " has non-existent transient " << endl;
kDebug() << "TC2:" << *it2 << endl; // this may crash
ret = false;
continue;
}
if( !(*it2)->mainClients().contains( *it1 ))
{
kdDebug() << "TC:" << *it1 << " has transient " << *it2 << " but transient does not have it as a main client" << endl;
ret = false;
}
}
}
GroupList groups = Workspace::self()->groups;
for( GroupList::ConstIterator it1 = groups.begin();
it1 != groups.end();
++it1 )
{
ClientList members = (*it1)->members();
for( ClientList::ConstIterator it2 = members.begin();
it2 != members.end();
++it2 )
{
if( (*it2)->in_group != *it1 )
{
kdDebug() << "TC: Group " << *it1 << " contains client " << *it2 << " but client is not in that group" << endl;
ret = false;
}
}
}
return ret;
}
static QString transiencyCheckStartBt;
static const Client* transiencyCheckClient;
static int transiencyCheck = 0;
static void startTransiencyCheck( const QString& bt, const Client* c, bool ne )
{
if( ++transiencyCheck == 1 )
{
transiencyCheckStartBt = bt;
transiencyCheckClient = c;
}
if( ne )
transiencyCheckNonExistent = true;
}
static void checkTransiency()
{
if( --transiencyCheck == 0 )
{
if( !performTransiencyCheck())
{
kdDebug() << "BT:" << transiencyCheckStartBt << endl;
kdDebug() << "CLIENT:" << transiencyCheckClient << endl;
assert( false );
}
transiencyCheckNonExistent = false;
}
}
class TransiencyChecker
{
public:
TransiencyChecker( const QString& bt, const Client*c ) { startTransiencyCheck( bt, c, false ); }
~TransiencyChecker() { checkTransiency(); }
};
void checkNonExistentClients()
{
startTransiencyCheck( kdBacktrace(), NULL, true );
checkTransiency();
}
#define TRANSIENCY_CHECK( c ) TransiencyChecker transiency_checker( kdBacktrace(), c )
#else
#define TRANSIENCY_CHECK( c )
void checkNonExistentClients()
{
}
#endif
//********************************************
// Group
//********************************************
@ -203,8 +46,7 @@ Group::Group( Window leader_P, Workspace* workspace_P )
leader_wid( leader_P ),
_workspace( workspace_P ),
leader_info( NULL ),
user_time( -1U ),
refcount( 0 )
user_time( -1U )
{
if( leader_P != None )
{
@ -213,12 +55,14 @@ Group::Group( Window leader_P, Workspace* workspace_P )
leader_info = new NETWinInfo( display(), leader_P, workspace()->rootWin(),
properties, 2 );
}
effect_group = new EffectWindowGroupImpl( this );
workspace()->addGroup( this, Allowed );
}
Group::~Group()
{
delete leader_info;
delete effect_group;
}
QPixmap Group::icon() const
@ -249,7 +93,6 @@ QPixmap Group::miniIcon() const
void Group::addMember( Client* member_P )
{
TRANSIENCY_CHECK( member_P );
_members.append( member_P );
// kDebug() << "GROUPADD:" << this << ":" << member_P << endl;
// kDebug() << kBacktrace() << endl;
@ -257,30 +100,11 @@ void Group::addMember( Client* member_P )
void Group::removeMember( Client* member_P )
{
TRANSIENCY_CHECK( member_P );
// kDebug() << "GROUPREMOVE:" << this << ":" << member_P << endl;
// kDebug() << kBacktrace() << endl;
Q_ASSERT( _members.contains( member_P ));
_members.removeAll( member_P );
// there are cases when automatic deleting of groups must be delayed,
// e.g. when removing a member and doing some operation on the possibly
// other members of the group (which would be however deleted already
// if there were no other members)
if( refcount == 0 && _members.isEmpty())
{
workspace()->removeGroup( this, Allowed );
delete this;
}
}
void Group::ref()
{
++refcount;
}
void Group::deref()
{
if( --refcount == 0 && _members.isEmpty())
if( _members.isEmpty())
{
workspace()->removeGroup( this, Allowed );
delete this;
@ -328,7 +152,6 @@ Group* Workspace::findGroup( Window leader ) const
// group with windows with the same client leader.
Group* Workspace::findClientLeaderGroup( const Client* c ) const
{
TRANSIENCY_CHECK( c );
Group* ret = NULL;
for( ClientList::ConstIterator it = clients.begin();
it != clients.end();
@ -346,15 +169,14 @@ Group* Workspace::findClientLeaderGroup( const Client* c ) const
// This most probably means the app uses group transients without
// setting group for its windows. Merging the two groups is a bad
// hack, but there's no really good solution for this case.
ClientList old_group = (*it)->group()->members();
Group* old_group = (*it)->group();
// old_group autodeletes when being empty
for( int pos = 0;
pos < old_group.count();
++pos )
for( int cnt = old_group->members().count();
cnt > 0;
--cnt )
{
Client* tmp = old_group[ pos ];
if( tmp != c )
tmp->changeClientLeaderGroup( ret );
Client* tmp = old_group->members().first();
tmp->checkGroup( ret ); // change group
}
}
}
@ -413,7 +235,6 @@ void Workspace::updateOnAllDesktopsOfTransients( Client* c )
// A new window has been mapped. Check if it's not a mainwindow for some already existing transient window.
void Workspace::checkTransients( Window w )
{
TRANSIENCY_CHECK( NULL );
for( ClientList::ConstIterator it = clients.begin();
it != clients.end();
++it )
@ -421,14 +242,13 @@ void Workspace::checkTransients( Window w )
}
//****************************************
// Client
// Toplevel
//****************************************
// hacks for broken apps here
// all resource classes are forced to be lowercase
bool Client::resourceMatch( const Client* c1, const Client* c2 )
bool Toplevel::resourceMatch( const Toplevel* c1, const Toplevel* c2 )
{
// xv has "xv" as resource name, and different strings starting with "XV" as resource class
if( qstrncmp( c1->resourceClass(), "xv", 2 ) == 0 && c1->resourceName() == "xv" )
@ -439,25 +259,20 @@ bool Client::resourceMatch( const Client* c1, const Client* c2 )
return c1->resourceClass() == c2->resourceClass();
}
//****************************************
// Client
//****************************************
bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool active_hack )
{
bool same_app = false;
// tests that definitely mean they belong together
if( c1 == c2 )
same_app = true;
else if( c1->isTransient() && c2->hasTransient( c1, true ))
same_app = true; // c1 has c2 as mainwindow
else if( c2->isTransient() && c1->hasTransient( c2, true ))
same_app = true; // c2 has c1 as mainwindow
else if( c1->group() == c2->group())
same_app = true; // same group
else if( c1->wmClientLeader() == c2->wmClientLeader()
&& c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
&& c2->wmClientLeader() != c2->window()) // don't use in this test then
same_app = true; // same client leader
// tests that mean they most probably don't belong together
else if( c1->pid() != c2->pid()
|| c1->wmClientMachine( false ) != c2->wmClientMachine( false ))
; // different processes
@ -469,12 +284,17 @@ bool Client::belongToSameApplication( const Client* c1, const Client* c2, bool a
; // different apps
else if( !sameAppWindowRoleMatch( c1, c2, active_hack ))
; // "different" apps
else if( c1->wmClientLeader() == c2->wmClientLeader()
&& c1->wmClientLeader() != c1->window() // if WM_CLIENT_LEADER is not set, it returns window(),
&& c2->wmClientLeader() != c2->window()) // don't use in this test then
same_app = true; // same client leader
else if( c1->group() == c2->group())
same_app = true; // same group
else if( c1->pid() == 0 || c2->pid() == 0 )
; // old apps that don't have _NET_WM_PID, consider them different
// if they weren't found to match above
else
same_app = true; // looks like it's the same app
return same_app;
}
@ -579,7 +399,6 @@ bool Client::sameAppWindowRoleMatch( const Client* c1, const Client* c2, bool ac
void Client::readTransient()
{
TRANSIENCY_CHECK( this );
Window new_transient_for_id;
if( XGetTransientForHint( display(), window(), &new_transient_for_id ))
{
@ -596,7 +415,6 @@ void Client::readTransient()
void Client::setTransient( Window new_transient_for_id )
{
TRANSIENCY_CHECK( this );
if( new_transient_for_id != transient_for_id )
{
removeFromMainClients();
@ -617,7 +435,6 @@ void Client::setTransient( Window new_transient_for_id )
void Client::removeFromMainClients()
{
TRANSIENCY_CHECK( this );
if( transientFor() != NULL )
transientFor()->removeTransient( this );
if( groupTransient())
@ -635,7 +452,6 @@ void Client::removeFromMainClients()
// related lists.
void Client::cleanGrouping()
{
TRANSIENCY_CHECK( this );
// kDebug() << "CLEANGROUPING:" << this << endl;
// for( ClientList::ConstIterator it = group()->members().begin();
// it != group()->members().end();
@ -705,7 +521,6 @@ void Client::cleanGrouping()
// Non-group transients not causing loops are checked in verifyTransientFor().
void Client::checkGroupTransients()
{
TRANSIENCY_CHECK( this );
for( ClientList::ConstIterator it1 = group()->members().begin();
it1 != group()->members().end();
++it1 )
@ -835,7 +650,6 @@ Window Client::verifyTransientFor( Window new_transient_for, bool defined )
void Client::addTransient( Client* cl )
{
TRANSIENCY_CHECK( this );
assert( !transients_list.contains( cl ));
// assert( !cl->hasTransient( this, true )); will be fixed in checkGroupTransients()
assert( cl != this );
@ -852,7 +666,6 @@ void Client::addTransient( Client* cl )
void Client::removeTransient( Client* cl )
{
TRANSIENCY_CHECK( this );
// kDebug() << "REMOVETRANS:" << this << ":" << cl << endl;
// kDebug() << kBacktrace() << endl;
transients_list.removeAll( cl );
@ -870,7 +683,6 @@ void Client::removeTransient( Client* cl )
// A new window has been mapped. Check if it's not a mainwindow for this already existing window.
void Client::checkTransient( Window w )
{
TRANSIENCY_CHECK( this );
if( original_transient_for_id != w )
return;
w = verifyTransientFor( w, true );
@ -951,10 +763,7 @@ Client* Client::findModal()
// Argument is only when some specific group needs to be set.
void Client::checkGroup( Group* set_group, bool force )
{
TRANSIENCY_CHECK( this );
Group* old_group = in_group;
if( old_group != NULL )
old_group->ref(); // turn off automatic deleting
if( set_group != NULL )
{
if( set_group != in_group )
@ -1011,21 +820,16 @@ void Client::checkGroup( Group* set_group, bool force )
in_group->addMember( this );
}
}
else // Not transient without a group, put it in its client leader group.
{ // This might be stupid if grouping was used for e.g. taskbar grouping
// or minimizing together the whole group, but as long as its used
// only for dialogs it's better to keep windows from one app in one group.
Group* new_group = workspace()->findClientLeaderGroup( this );
if( in_group != NULL && in_group != new_group )
else // not transient without a group, put it in its own group
{
if( in_group != NULL && in_group->leader() != window())
{
in_group->removeMember( this );
in_group = NULL;
}
if( new_group == NULL )
new_group = new Group( None, workspace() );
if( in_group != new_group )
if( in_group == NULL )
{
in_group = new_group;
in_group = new Group( None, workspace());
in_group->addMember( this );
}
}
@ -1042,16 +846,7 @@ void Client::checkGroup( Group* set_group, bool force )
++it;
}
if( groupTransient())
{
// no longer transient for ones in the old group
if( old_group != NULL )
{
for( ClientList::ConstIterator it = old_group->members().begin();
it != old_group->members().end();
++it )
(*it)->removeTransient( this );
}
// and make transient for all in the new group
{ // and make transient for all in the group
for( ClientList::ConstIterator it = group()->members().begin();
it != group()->members().end();
++it )
@ -1061,6 +856,25 @@ void Client::checkGroup( Group* set_group, bool force )
(*it)->addTransient( this );
}
}
#if 0 // TODO
if( groupTransient())
{
if( workspace()->findGroup( old_group )) // if it still exists
{ // it's no longer transient for windows in the old group
for( ClientList::ConstIterator it = old_group->members().begin();
it != old_group->members().end();
++it )
(*it)->removeTransient( this );
}
// and it's transiet for all windows in the new group (this one is the most recent
// in the group, so it is transient only for all previous windows)
// loops are checked in checkGroupTransients()
for( ClientList::ConstIterator it = group()->members().begin();
it != group()->members().end();
++it )
(*it)->addTransient( this );
}
#endif
// group transient splashscreens should be transient even for windows
// in group mapped later
for( ClientList::ConstIterator it = group()->members().begin();
@ -1076,25 +890,11 @@ void Client::checkGroup( Group* set_group, bool force )
addTransient( *it );
}
}
if( old_group != NULL )
old_group->deref(); // can be now deleted if empty
checkGroupTransients();
checkActiveModal();
workspace()->updateClientLayer( this );
}
// used by Workspace::findClientLeaderGroup()
void Client::changeClientLeaderGroup( Group* gr )
{
// transientFor() != NULL are in the group of their mainwindow, so keep them there
if( transientFor() != NULL )
return;
// also don't change the group for window which have group set
if( window_group )
return;
checkGroup( gr ); // change group
}
bool Client::check_active_modal = false;
void Client::checkActiveModal()

12
group.h
View file

@ -21,6 +21,7 @@ namespace KWin
class Client;
class Workspace;
class EffectWindowGroupImpl;
class Group
{
@ -41,8 +42,7 @@ class Group
bool groupEvent( XEvent* e );
void updateUserTime( Time time = CurrentTime );
Time userTime() const;
void ref();
void deref();
EffectWindowGroupImpl* effectGroup();
private:
void getIcons();
void startupIdChanged();
@ -52,7 +52,7 @@ class Group
Workspace* _workspace;
NETWinInfo* leader_info;
Time user_time;
int refcount;
EffectWindowGroupImpl* effect_group;
};
inline Window Group::leader() const
@ -85,6 +85,12 @@ inline Time Group::userTime() const
return user_time;
}
inline
EffectWindowGroupImpl* Group::effectGroup()
{
return effect_group;
}
} // namespace
#endif

View file

@ -469,4 +469,13 @@ bool EffectWindow::isOnDesktop( int d ) const
}
//****************************************
// EffectWindowGroup
//****************************************
EffectWindowGroup::~EffectWindowGroup()
{
}
} // namespace

View file

@ -32,6 +32,7 @@ namespace KWin
class EffectWindow;
class EffectWindowGroup;
class Effect;
typedef QPair< QString, Effect* > EffectPair;
@ -251,6 +252,9 @@ class KWIN_EXPORT EffectWindow
virtual void enablePainting( int reason ) = 0;
virtual void disablePainting( int reason ) = 0;
virtual void addRepaint( const QRect& r ) = 0;
virtual void addRepaint( int x, int y, int w, int h ) = 0;
virtual void addRepaintFull() = 0;
virtual bool isDeleted() const = 0;
virtual bool isMinimized() const = 0;
@ -270,6 +274,7 @@ class KWIN_EXPORT EffectWindow
virtual QRect rect() const = 0;
virtual QString caption() const = 0;
virtual const EffectWindowGroup* group() const = 0;
virtual bool isDesktop() const = 0;
virtual bool isDock() const = 0;
@ -290,6 +295,12 @@ class KWIN_EXPORT EffectWindow
};
class KWIN_EXPORT EffectWindowGroup
{
public:
virtual ~EffectWindowGroup();
virtual EffectWindowList members() const = 0;
};
extern KWIN_EXPORT EffectsHandler* effects;