/********************************************************************
 KWin - the KDE window manager
 This file is part of the KDE project.

Copyright (C) 2006 Lubos Lunak <l.lunak@kde.org>

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
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/

#include "effects.h"

#include "deleted.h"
#include "client.h"
#include "group.h"
#include "scene_xrender.h"
#include "scene_opengl.h"
#include "unmanaged.h"
#include "workspace.h"
#include "kwinglutils.h"

#include <QFile>

#include "kdebug.h"
#include "klibrary.h"
#include "kdesktopfile.h"
#include "kconfiggroup.h"
#include "kstandarddirs.h"
#include <kservice.h>
#include <kservicetypetrader.h>
#include <kplugininfo.h>

#include <assert.h>


namespace KWin
{

//---------------------
// Static

static QByteArray readWindowProperty( Window win, long atom, long type, int format )
    {
    int len = 32768;
    for(;;)
        {
        unsigned char* data;
        Atom rtype;
        int rformat;
        unsigned long nitems, after;
        if( XGetWindowProperty( QX11Info::display(), win,
            atom, 0, len, False, AnyPropertyType,
            &rtype, &rformat, &nitems, &after, &data ) == Success )
            {
            if( after > 0 )
                {
                XFree( data );
                len *= 2;
                continue;
                }
            if( long( rtype ) == type && rformat == format )
                {
                int bytelen = format == 8 ? nitems : format == 16 ? nitems * sizeof( short ) : nitems * sizeof( long );
                QByteArray ret( reinterpret_cast< const char* >( data ), bytelen );
                XFree( data );
                return ret;
                }
            else // wrong format, type or something
                {
                XFree( data );
                return QByteArray();
                }
            }
        else // XGetWindowProperty() failed
            return QByteArray();
        }
    }

static void deleteWindowProperty( Window win, long int atom )
    {
    XDeleteProperty( QX11Info::display(), win, atom );
    }

//---------------------

EffectsHandlerImpl::EffectsHandlerImpl(CompositingType type)
    : EffectsHandler(type)
    , keyboard_grab_effect( NULL )
    , fullscreen_effect( 0 )
    , next_window_quad_type( EFFECT_QUAD_TYPE_START )
    , mouse_poll_ref_count( 0 )
    {
    reconfigure();
    }

EffectsHandlerImpl::~EffectsHandlerImpl()
    {
    if( keyboard_grab_effect != NULL )
        ungrabKeyboard();
    foreach( const EffectPair &ep, loaded_effects )
        unloadEffect( ep.first );
    foreach( const InputWindowPair &pos, input_windows )
        XDestroyWindow( display(), pos.second );
    }

void EffectsHandlerImpl::reconfigure()
    {
    KSharedConfig::Ptr _config = KGlobal::config();
    KConfigGroup conf(_config, "Plugins");

    KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect");
    QStringList effectsToBeLoaded;
    // First unload necessary effects
    foreach( const KService::Ptr &service, offers )
        {
        KPluginInfo plugininfo( service );
        plugininfo.load( conf );

        bool isloaded = isEffectLoaded( plugininfo.pluginName() );
        bool shouldbeloaded = plugininfo.isPluginEnabled();
        if( !shouldbeloaded && isloaded )
            unloadEffect( plugininfo.pluginName() );
        if( shouldbeloaded )
            effectsToBeLoaded.append( plugininfo.pluginName() );
        }
    QStringList newLoaded;
    // Then load those that should be loaded
    foreach( const QString &effectName, effectsToBeLoaded )
        {
        if( !isEffectLoaded( effectName ))
            {
            loadEffect( effectName );
            newLoaded.append( effectName );
            }
        }
    foreach( const EffectPair &ep, loaded_effects )
        {
        if( !newLoaded.contains( ep.first )) // don't reconfigure newly loaded effects
            ep.second->reconfigure( Effect::ReconfigureAll );
        }
    }

// the idea is that effects call this function again which calls the next one
void EffectsHandlerImpl::prePaintScreen( ScreenPrePaintData& data, int time )
    {
    if( current_paint_screen < loaded_effects.size())
        {
        loaded_effects[current_paint_screen++].second->prePaintScreen( data, time );
        --current_paint_screen;
        }
    // no special final code
    }

void EffectsHandlerImpl::paintScreen( int mask, QRegion region, ScreenPaintData& data )
    {
    if( current_paint_screen < loaded_effects.size())
        {
        loaded_effects[current_paint_screen++].second->paintScreen( mask, region, data );
        --current_paint_screen;
        }
    else
        scene->finalPaintScreen( mask, region, data );
    }

void EffectsHandlerImpl::postPaintScreen()
    {
    if( current_paint_screen < loaded_effects.size())
        {
        loaded_effects[current_paint_screen++].second->postPaintScreen();
        --current_paint_screen;
        }
    // no special final code
    }

void EffectsHandlerImpl::prePaintWindow( EffectWindow* w, WindowPrePaintData& data, int time )
    {
    if( current_paint_window < loaded_effects.size())
        {
        loaded_effects[current_paint_window++].second->prePaintWindow( w, data, time );
        --current_paint_window;
        }
    // no special final code
    }

void EffectsHandlerImpl::paintWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
    {
    if( current_paint_window < loaded_effects.size())
        {
        loaded_effects[current_paint_window++].second->paintWindow( w, mask, region, data );
        --current_paint_window;
        }
    else
        scene->finalPaintWindow( static_cast<EffectWindowImpl*>( w ), mask, region, data );
    }

void EffectsHandlerImpl::postPaintWindow( EffectWindow* w )
    {
    if( current_paint_window < loaded_effects.size())
        {
        loaded_effects[current_paint_window++].second->postPaintWindow( w );
        --current_paint_window;
        }
    // no special final code
    }

bool EffectsHandlerImpl::providesResizeEffect()
    {
    for( int i = 0; i < loaded_effects.size(); ++i )
        if( loaded_effects.at(i).second->isResizeEffect() )
            return true;
    return false;
    }

void EffectsHandlerImpl::drawWindow( EffectWindow* w, int mask, QRegion region, WindowPaintData& data )
    {
    if( current_draw_window < loaded_effects.size())
        {
        loaded_effects[current_draw_window++].second->drawWindow( w, mask, region, data );
        --current_draw_window;
        }
    else
        scene->finalDrawWindow( static_cast<EffectWindowImpl*>( w ), mask, region, data );
    }

void EffectsHandlerImpl::buildQuads( EffectWindow* w, WindowQuadList& quadList )
    {
    if( current_build_quads < loaded_effects.size())
        {
        loaded_effects[current_build_quads++].second->buildQuads( w, quadList );
        --current_build_quads;
        }
    }

bool EffectsHandlerImpl::hasDecorationShadows() const
    {
    return Workspace::self()->hasDecorationShadows();
    }

bool EffectsHandlerImpl::decorationsHaveAlpha() const
    {
    return Workspace::self()->decorationHasAlpha();
    }

// start another painting pass
void EffectsHandlerImpl::startPaint()
    {
    assert( current_paint_screen == 0 );
    assert( current_paint_window == 0 );
    assert( current_draw_window == 0 );
    assert( current_build_quads == 0 );
    assert( current_transform == 0 );
    }

void EffectsHandlerImpl::windowUserMovedResized( EffectWindow* c, bool first, bool last )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowUserMovedResized( c, first, last );
    }

void EffectsHandlerImpl::windowMoveResizeGeometryUpdate( EffectWindow* c, const QRect& geometry )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowMoveResizeGeometryUpdate( c, geometry );
    }

void EffectsHandlerImpl::windowOpacityChanged( EffectWindow* c, double old_opacity )
    {
        if (!c)
            return;
    if( static_cast<EffectWindowImpl*>(c)->window()->opacity() == old_opacity )
        return;
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowOpacityChanged( c, old_opacity );
    }

void EffectsHandlerImpl::windowAdded( EffectWindow* c )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowAdded( c );
    }

void EffectsHandlerImpl::windowDeleted( EffectWindow* c )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowDeleted( c );
    elevated_windows.removeAll( c );
    }

void EffectsHandlerImpl::windowClosed( EffectWindow* c )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowClosed( c );
    }

void EffectsHandlerImpl::windowActivated( EffectWindow* c )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowActivated( c );
    }

void EffectsHandlerImpl::windowMinimized( EffectWindow* c )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowMinimized( c );
    }

void EffectsHandlerImpl::windowUnminimized( EffectWindow* c )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowUnminimized( c );
    }

void EffectsHandlerImpl::clientGroupItemSwitched( EffectWindow* from, EffectWindow* to )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->clientGroupItemSwitched( from, to );
    }

void EffectsHandlerImpl::clientGroupItemAdded( EffectWindow* from, EffectWindow* to )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->clientGroupItemAdded( from, to );
    }

void EffectsHandlerImpl::clientGroupItemRemoved( EffectWindow* c, EffectWindow* group )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->clientGroupItemRemoved( c, group );
    }

void EffectsHandlerImpl::desktopChanged( int old )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->desktopChanged( old );
    }

void EffectsHandlerImpl::windowDamaged( EffectWindow* w, const QRect& r )
    {
    if( w == NULL )
        return;
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowDamaged( w, r );
    }

void EffectsHandlerImpl::windowGeometryShapeChanged( EffectWindow* w, const QRect& old )
    {
    if( w == NULL ) // during late cleanup effectWindow() may be already NULL
        return;     // in some functions that may still call this
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->windowGeometryShapeChanged( w, old );
    }

void EffectsHandlerImpl::tabBoxAdded( int mode )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->tabBoxAdded( mode );
    }

void EffectsHandlerImpl::tabBoxClosed()
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->tabBoxClosed();
    }

void EffectsHandlerImpl::tabBoxUpdated()
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->tabBoxUpdated();
    }

void EffectsHandlerImpl::tabBoxKeyEvent( QKeyEvent* event )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->tabBoxKeyEvent( event );
    }

void EffectsHandlerImpl::setActiveFullScreenEffect( Effect* e )
    {
    fullscreen_effect = e;
    Workspace::self()->checkUnredirect();
    }

Effect* EffectsHandlerImpl::activeFullScreenEffect() const
    {
    return fullscreen_effect;
    }

bool EffectsHandlerImpl::borderActivated( ElectricBorder border )
    {
    bool ret = false;
    foreach( const EffectPair &ep, loaded_effects )
        if( ep.second->borderActivated( border ))
            ret = true; // bail out or tell all?
    return ret;
    }

void EffectsHandlerImpl::mouseChanged( const QPoint& pos, const QPoint& oldpos,
    Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons,
    Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->mouseChanged( pos, oldpos, buttons, oldbuttons, modifiers, oldmodifiers );
    }

bool EffectsHandlerImpl::grabKeyboard( Effect* effect )
    {
    if( keyboard_grab_effect != NULL )
        return false;
    bool ret = grabXKeyboard();
    if( !ret )
        return false;
    keyboard_grab_effect = effect;
    return true;
    }

void EffectsHandlerImpl::ungrabKeyboard()
    {
    assert( keyboard_grab_effect != NULL );
    ungrabXKeyboard();
    keyboard_grab_effect = NULL;
    }

void EffectsHandlerImpl::grabbedKeyboardEvent( QKeyEvent* e )
    {
    if( keyboard_grab_effect != NULL )
        keyboard_grab_effect->grabbedKeyboardEvent( e );
    }

void* EffectsHandlerImpl::getProxy( QString name )
    {
    // All effects start with "kwin4_effect_", prepend it to the name
    name.prepend( "kwin4_effect_" );

    for( QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it)
        if ( (*it).first == name )
            return (*it).second->proxy();

    return NULL;
    }

void EffectsHandlerImpl::startMousePolling()
    {
    if( !mouse_poll_ref_count ) // Start timer if required
        Workspace::self()->startMousePolling();
    mouse_poll_ref_count++;
    }

void EffectsHandlerImpl::stopMousePolling()
    {
    assert( mouse_poll_ref_count );
    mouse_poll_ref_count--;
    if( !mouse_poll_ref_count ) // Stop timer if required
        Workspace::self()->stopMousePolling();
    }

bool EffectsHandlerImpl::hasKeyboardGrab() const
    {
    return keyboard_grab_effect != NULL;
    }

void EffectsHandlerImpl::propertyNotify( EffectWindow* c, long atom )
    {
    if( !registered_atoms.contains( atom ))
        return;
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->propertyNotify( c, atom );
    }

void EffectsHandlerImpl::numberDesktopsChanged( int old )
    {
    foreach( const EffectPair &ep, loaded_effects )
        ep.second->numberDesktopsChanged( old );
    }

void EffectsHandlerImpl::registerPropertyType( long atom, bool reg )
    {
    if( reg )
        ++registered_atoms[ atom ]; // initialized to 0 if not present yet
    else
        {
        if( --registered_atoms[ atom ] == 0 )
            registered_atoms.remove( atom );
        }
    }

QByteArray EffectsHandlerImpl::readRootProperty( long atom, long type, int format ) const
    {
    return readWindowProperty( rootWindow(), atom, type, format );
    }

void EffectsHandlerImpl::deleteRootProperty( long atom ) const
    {
    deleteWindowProperty( rootWindow(), atom );
    }

void EffectsHandlerImpl::activateWindow( EffectWindow* c )
    {
    if( Client* cl = dynamic_cast< Client* >( static_cast<EffectWindowImpl*>(c)->window()))
        Workspace::self()->activateClient( cl, true );
    }

EffectWindow* EffectsHandlerImpl::activeWindow() const
    {
    return Workspace::self()->activeClient() ? Workspace::self()->activeClient()->effectWindow() : NULL;
    }

void EffectsHandlerImpl::moveWindow( EffectWindow* w, const QPoint& pos, bool snap, double snapAdjust )
    {
    Client* cl = dynamic_cast< Client* >( static_cast<EffectWindowImpl*>(w)->window());
    if (!cl || !cl->isMovable())
        return;

    if (snap)
        cl->move(Workspace::self()->adjustClientPosition(cl, pos, true, snapAdjust));
    else
        cl->move( pos );
    }

void EffectsHandlerImpl::windowToDesktop( EffectWindow* w, int desktop )
    {
    Client* cl = dynamic_cast< Client* >( static_cast<EffectWindowImpl*>(w)->window());
    if( cl && !cl->isDesktop() && !cl->isDock() && !cl->isTopMenu())
        Workspace::self()->sendClientToDesktop( cl, desktop, true );
    }

void EffectsHandlerImpl::windowToScreen( EffectWindow* w, int screen )
    {
    Client* cl = dynamic_cast< Client* >( static_cast<EffectWindowImpl*>(w)->window());
    if( cl && !cl->isDesktop() && !cl->isDock() && !cl->isTopMenu())
        Workspace::self()->sendClientToScreen( cl, screen );
    }

void EffectsHandlerImpl::setShowingDesktop( bool showing )
    {
    Workspace::self()->setShowingDesktop( showing );
    }


int EffectsHandlerImpl::currentDesktop() const
    {
    return Workspace::self()->currentDesktop();
    }

int EffectsHandlerImpl::numberOfDesktops() const
    {
    return Workspace::self()->numberOfDesktops();
    }

void EffectsHandlerImpl::setCurrentDesktop( int desktop )
    {
    Workspace::self()->setCurrentDesktop( desktop );
    }

void EffectsHandlerImpl::setNumberOfDesktops( int desktops )
    {
    Workspace::self()->setNumberOfDesktops( desktops );
    }

QSize EffectsHandlerImpl::desktopGridSize() const
    {
    return Workspace::self()->desktopGridSize();
    }

int EffectsHandlerImpl::desktopGridWidth() const
    {
    return Workspace::self()->desktopGridWidth();
    }

int EffectsHandlerImpl::desktopGridHeight() const
    {
    return Workspace::self()->desktopGridHeight();
    }

int EffectsHandlerImpl::workspaceWidth() const
    {
    return Workspace::self()->workspaceWidth();
    }

int EffectsHandlerImpl::workspaceHeight() const
    {
    return Workspace::self()->workspaceHeight();
    }

int EffectsHandlerImpl::desktopAtCoords( QPoint coords ) const
    {
    return Workspace::self()->desktopAtCoords( coords );
    }

QPoint EffectsHandlerImpl::desktopGridCoords( int id ) const
    {
    return Workspace::self()->desktopGridCoords( id );
    }

QPoint EffectsHandlerImpl::desktopCoords( int id ) const
    {
    return Workspace::self()->desktopCoords( id );
    }

int EffectsHandlerImpl::desktopAbove( int desktop, bool wrap ) const
    {
    return Workspace::self()->desktopAbove( desktop, wrap );
    }

int EffectsHandlerImpl::desktopToRight( int desktop, bool wrap ) const
    {
    return Workspace::self()->desktopToRight( desktop, wrap );
    }

int EffectsHandlerImpl::desktopBelow( int desktop, bool wrap ) const
    {
    return Workspace::self()->desktopBelow( desktop, wrap );
    }

int EffectsHandlerImpl::desktopToLeft( int desktop, bool wrap ) const
    {
    return Workspace::self()->desktopToLeft( desktop, wrap );
    }

bool EffectsHandlerImpl::isDesktopLayoutDynamic() const
    {
    return Workspace::self()->isDesktopLayoutDynamic();
    }

int EffectsHandlerImpl::addDesktop( QPoint coords )
    {
    return Workspace::self()->addDesktop( coords );
    }

void EffectsHandlerImpl::deleteDesktop( int id )
    {
    Workspace::self()->deleteDesktop( id );
    }

QString EffectsHandlerImpl::desktopName( int desktop ) const
    {
    return Workspace::self()->desktopName( desktop );
    }

bool EffectsHandlerImpl::optionRollOverDesktops() const
    {
    return options->rollOverDesktops;
    }

double EffectsHandlerImpl::animationTimeFactor() const
    {
    return options->animationTimeFactor();
    }

WindowQuadType EffectsHandlerImpl::newWindowQuadType()
    {
    return WindowQuadType( next_window_quad_type++ );
    }

int EffectsHandlerImpl::displayWidth() const
    {
    return KWin::displayWidth();
    }

int EffectsHandlerImpl::displayHeight() const
    {
    return KWin::displayWidth();
    }

EffectWindow* EffectsHandlerImpl::findWindow( WId id ) const
    {
    if( Client* w = Workspace::self()->findClient( WindowMatchPredicate( id )))
        return w->effectWindow();
    if( Unmanaged* w = Workspace::self()->findUnmanaged( WindowMatchPredicate( id )))
        return w->effectWindow();
    return NULL;
    }

EffectWindowList EffectsHandlerImpl::stackingOrder() const
    {
    ClientList list = Workspace::self()->stackingOrder();
    EffectWindowList ret;
    foreach( Client* c, list )
        ret.append( effectWindow( c ));
    return ret;
    }

void EffectsHandlerImpl::setElevatedWindow( EffectWindow* w, bool set )
    {
    elevated_windows.removeAll( w );
    if( set )
        elevated_windows.append( w );
    }

void EffectsHandlerImpl::setTabBoxWindow(EffectWindow* w)
    {
    if( Client* c = dynamic_cast< Client* >( static_cast< EffectWindowImpl* >( w )->window()))
        Workspace::self()->setTabBoxClient( c );
    }

void EffectsHandlerImpl::setTabBoxDesktop(int desktop)
    {
    Workspace::self()->setTabBoxDesktop( desktop );
    }

EffectWindowList EffectsHandlerImpl::currentTabBoxWindowList() const
    {
    EffectWindowList ret;
    ClientList clients = Workspace::self()->currentTabBoxClientList();
    foreach( Client* c, clients )
        ret.append( c->effectWindow());
    return ret;
    }

void EffectsHandlerImpl::refTabBox()
    {
    Workspace::self()->refTabBox();
    }

void EffectsHandlerImpl::unrefTabBox()
    {
    Workspace::self()->unrefTabBox();
    }

void EffectsHandlerImpl::closeTabBox()
    {
    Workspace::self()->closeTabBox();
    }

QList< int > EffectsHandlerImpl::currentTabBoxDesktopList() const
    {
    return Workspace::self()->currentTabBoxDesktopList();
    }

int EffectsHandlerImpl::currentTabBoxDesktop() const
    {
    return Workspace::self()->currentTabBoxDesktop();
    }

EffectWindow* EffectsHandlerImpl::currentTabBoxWindow() const
    {
    if( Client* c = Workspace::self()->currentTabBoxClient())
        return c->effectWindow();
    return NULL;
    }

void EffectsHandlerImpl::pushRenderTarget(GLRenderTarget* target)
{
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
    target->enable();
    render_targets.push(target);
#endif
}

GLRenderTarget* EffectsHandlerImpl::popRenderTarget()
{
#ifdef KWIN_HAVE_OPENGL_COMPOSITING
    GLRenderTarget* ret = render_targets.pop();
    ret->disable();
    if( !render_targets.isEmpty() )
        render_targets.top()->enable();
    return ret;
#else
    return 0;
#endif
}

void EffectsHandlerImpl::addRepaintFull()
    {
    Workspace::self()->addRepaintFull();
    }

void EffectsHandlerImpl::addRepaint( const QRect& r )
    {
    Workspace::self()->addRepaint( r );
    }

void EffectsHandlerImpl::addRepaint( const QRegion& r )
    {
    Workspace::self()->addRepaint( r );
    }

void EffectsHandlerImpl::addRepaint( int x, int y, int w, int h )
    {
    Workspace::self()->addRepaint( x, y, w, h );
    }

int EffectsHandlerImpl::activeScreen() const
    {
    return Workspace::self()->activeScreen();
    }

int EffectsHandlerImpl::numScreens() const
    {
    return Workspace::self()->numScreens();
    }

int EffectsHandlerImpl::screenNumber( const QPoint& pos ) const
    {
    return Workspace::self()->screenNumber( pos );
    }

QRect EffectsHandlerImpl::clientArea( clientAreaOption opt, int screen, int desktop ) const
    {
    return Workspace::self()->clientArea( opt, screen, desktop );
    }

QRect EffectsHandlerImpl::clientArea( clientAreaOption opt, const EffectWindow* c ) const
    {
    const Toplevel* t = static_cast< const EffectWindowImpl* >(c)->window();
    if( const Client* cl = dynamic_cast< const Client* >( t ))
        return Workspace::self()->clientArea( opt, cl );
    else
        return Workspace::self()->clientArea( opt, t->geometry().center(), Workspace::self()->currentDesktop());
    }

QRect EffectsHandlerImpl::clientArea( clientAreaOption opt, const QPoint& p, int desktop ) const
    {
    return Workspace::self()->clientArea( opt, p, desktop );
    }

Window EffectsHandlerImpl::createInputWindow( Effect* e, int x, int y, int w, int h, const QCursor& cursor )
    {
    XSetWindowAttributes attrs;
    attrs.override_redirect = True;
    Window win = XCreateWindow( display(), rootWindow(), x, y, w, h, 0, 0, InputOnly, CopyFromParent,
        CWOverrideRedirect, &attrs );
    // TODO keeping on top?
    // TODO enter/leave notify?
    XSelectInput( display(), win, ButtonPressMask | ButtonReleaseMask | PointerMotionMask );
    XDefineCursor( display(), win, cursor.handle());
    XMapWindow( display(), win );
    input_windows.append( qMakePair( e, win ));

    // Raise electric border windows above the input windows
    // so they can still be triggered.
    Workspace::self()->raiseElectricBorderWindows();

    return win;
    }

void EffectsHandlerImpl::destroyInputWindow( Window w )
    {
    foreach( const InputWindowPair &pos, input_windows )
        {
        if( pos.second == w )
            {
            input_windows.removeAll( pos );
            XDestroyWindow( display(), w );
            return;
            }
        }
    abort();
    }

bool EffectsHandlerImpl::checkInputWindowEvent( XEvent* e )
    {
    if( e->type != ButtonPress && e->type != ButtonRelease && e->type != MotionNotify )
        return false;
    foreach( const InputWindowPair &pos, input_windows )
        {
        if( pos.second == e->xany.window )
            {
            switch( e->type )
                {
                case ButtonPress:
                    {
                    XButtonEvent* e2 = &e->xbutton;
                    Qt::MouseButton button = x11ToQtMouseButton( e2->button );
                    Qt::MouseButtons buttons = x11ToQtMouseButtons( e2->state ) | button;
                    QMouseEvent ev( QEvent::MouseButtonPress,
                        QPoint( e2->x, e2->y ), QPoint( e2->x_root, e2->y_root ),
                        button, buttons, x11ToQtKeyboardModifiers( e2->state ));
                    pos.first->windowInputMouseEvent( pos.second, &ev );
                    break; // --->
                    }
                case ButtonRelease:
                    {
                    XButtonEvent* e2 = &e->xbutton;
                    Qt::MouseButton button = x11ToQtMouseButton( e2->button );
                    Qt::MouseButtons buttons = x11ToQtMouseButtons( e2->state ) & ~button;
                    QMouseEvent ev( QEvent::MouseButtonRelease,
                        QPoint( e2->x, e2->y ), QPoint( e2->x_root, e2->y_root ),
                        button, buttons, x11ToQtKeyboardModifiers( e2->state ));
                    pos.first->windowInputMouseEvent( pos.second, &ev );
                    break; // --->
                    }
                case MotionNotify:
                    {
                    XMotionEvent* e2 = &e->xmotion;
                    QMouseEvent ev( QEvent::MouseMove, QPoint( e2->x, e2->y ), QPoint( e2->x_root, e2->y_root ),
                        Qt::NoButton, x11ToQtMouseButtons( e2->state ), x11ToQtKeyboardModifiers( e2->state ));
                    pos.first->windowInputMouseEvent( pos.second, &ev );
                    break; // --->
                    }
                }
            return true; // eat event
            }
        }
    return false;
    }

void EffectsHandlerImpl::checkInputWindowStacking()
    {
    if( input_windows.count() == 0 )
        return;
    Window* wins = new Window[ input_windows.count() ];
    int pos = 0;
    foreach( const InputWindowPair &it, input_windows )
        wins[ pos++ ] = it.second;
    XRaiseWindow( display(), wins[ 0 ] );
    XRestackWindows( display(), wins, pos );
    delete[] wins;
    // Raise electric border windows above the input windows
    // so they can still be triggered. TODO: Do both at once.
    Workspace::self()->raiseElectricBorderWindows();
    }

QPoint EffectsHandlerImpl::cursorPos() const
    {
    return Workspace::self()->cursorPos();
    }

void EffectsHandlerImpl::checkElectricBorder(const QPoint &pos, Time time)
    {
    Workspace::self()->checkElectricBorder( pos, time );
    }

void EffectsHandlerImpl::reserveElectricBorder( ElectricBorder border )
    {
    Workspace::self()->reserveElectricBorder( border );
    }

void EffectsHandlerImpl::unreserveElectricBorder( ElectricBorder border )
    {
    Workspace::self()->unreserveElectricBorder( border );
    }

void EffectsHandlerImpl::reserveElectricBorderSwitching( bool reserve )
    {
    Workspace::self()->reserveElectricBorderSwitching( reserve );
    }

unsigned long EffectsHandlerImpl::xrenderBufferPicture()
    {
#ifdef KWIN_HAVE_XRENDER_COMPOSITING
    if( SceneXrender* s = dynamic_cast< SceneXrender* >( scene ))
        return s->bufferPicture();
#endif
    return None;
    }

KLibrary* EffectsHandlerImpl::findEffectLibrary( KService* service )
    {
    QString libname = service->library();
    KLibrary* library = new KLibrary(libname);
    if( !library )
        {
        kError( 1212 ) << "couldn't open library for effect '" <<
                service->name() << "'" << endl;
        return 0;
        }

    return library;
    }

void EffectsHandlerImpl::toggleEffect( const QString& name )
    {
    if( isEffectLoaded( name ))
        unloadEffect( name );
    else
        loadEffect( name );
    }

QStringList EffectsHandlerImpl::loadedEffects() const
    {
    QStringList listModules;
    for(QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it)
        {
        listModules <<(*it).first;
        }
    return listModules;
    }

QStringList EffectsHandlerImpl::listOfEffects() const
    {
    KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect");
    QStringList listOfModules;
    // First unload necessary effects
    foreach( const KService::Ptr &service, offers )
        {
        KPluginInfo plugininfo( service );
        listOfModules<<plugininfo.pluginName();
        }
    return listOfModules;
    }

bool EffectsHandlerImpl::loadEffect( const QString& name )
    {
    Workspace::self()->addRepaintFull();
    assert( current_paint_screen == 0 );
    assert( current_paint_window == 0 );
    assert( current_draw_window == 0 );
    assert( current_build_quads == 0 );
    assert( current_transform == 0 );

    if( !name.startsWith("kwin4_effect_") )
        kWarning( 1212 ) << "Effect names usually have kwin4_effect_ prefix" ;

    // Make sure a single effect won't be loaded multiple times
    for(QVector< EffectPair >::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it)
        {
        if( (*it).first == name )
            {
            kDebug( 1212 ) << "EffectsHandler::loadEffect : Effect already loaded : " << name;
            return true;
            }
        }


    kDebug( 1212 ) << "Trying to load " << name;
    QString internalname = name.toLower();

    QString constraint = QString("[X-KDE-PluginInfo-Name] == '%1'").arg(internalname);
    KService::List offers = KServiceTypeTrader::self()->query("KWin/Effect", constraint);
    if(offers.isEmpty())
    {
        kError( 1212 ) << "Couldn't find effect " << name << endl;
        return false;
    }
    KService::Ptr service = offers.first();

    KLibrary* library = findEffectLibrary( service.data() );
    if( !library )
        {
        return false;
        }

    QString version_symbol = "effect_version_" + name;
    KLibrary::void_function_ptr version_func = library->resolveFunction(version_symbol.toAscii());
    if( version_func == NULL )
        {
        kWarning( 1212 ) << "Effect " << name << " does not provide required API version, ignoring.";
        return false;
        }
    typedef int (*t_versionfunc)();
    int version = reinterpret_cast< t_versionfunc >( version_func )(); // call it
    // Version must be the same or less, but major must be the same.
    // With major 0 minor must match exactly.
    if( version > KWIN_EFFECT_API_VERSION
        || ( version >> 8 ) != KWIN_EFFECT_API_VERSION_MAJOR
        || ( KWIN_EFFECT_API_VERSION_MAJOR == 0 && version != KWIN_EFFECT_API_VERSION ))
        {
        kWarning( 1212 ) << "Effect " << name << " requires unsupported API version " << version;
        return false;
        }
    QString supported_symbol = "effect_supported_" + name;
    KLibrary::void_function_ptr supported_func = library->resolveFunction(supported_symbol.toAscii().data());
    QString create_symbol = "effect_create_" + name;
    KLibrary::void_function_ptr create_func = library->resolveFunction(create_symbol.toAscii().data());
    if( supported_func )
        {
        typedef bool (*t_supportedfunc)();
        t_supportedfunc supported = reinterpret_cast<t_supportedfunc>(supported_func);
        if(!supported())
            {
            kWarning( 1212 ) << "EffectsHandler::loadEffect : Effect " << name << " is not supported" ;
            library->unload();
            return false;
            }
        }
    if(!create_func)
        {
        kError( 1212 ) << "EffectsHandler::loadEffect : effect_create function not found" << endl;
        library->unload();
        return false;
        }
    typedef Effect* (*t_createfunc)();
    t_createfunc create = reinterpret_cast<t_createfunc>(create_func);

    // Make sure all depenedencies have been loaded
    // TODO: detect circular deps
    KPluginInfo plugininfo( service );
    QStringList dependencies = plugininfo.dependencies();
    foreach( const QString &depName, dependencies )
        {
        if( !loadEffect(depName))
            {
            kError(1212) << "EffectsHandler::loadEffect : Couldn't load dependencies for effect " << name << endl;
            library->unload();
            return false;
            }
        }

    Effect* e = create();

    effect_order.insert( service->property( "X-KDE-Ordering" ).toInt(), EffectPair( name, e ));
    effectsChanged();
    effect_libraries[ name ] = library;

    return true;
    }

void EffectsHandlerImpl::unloadEffect( const QString& name )
    {
    Workspace::self()->addRepaintFull();
    assert( current_paint_screen == 0 );
    assert( current_paint_window == 0 );
    assert( current_draw_window == 0 );
    assert( current_build_quads == 0 );
    assert( current_transform == 0 );

    for( QMap< int, EffectPair >::iterator it = effect_order.begin(); it != effect_order.end(); ++it)
        {
        if ( it.value().first == name )
            {
            kDebug( 1212 ) << "EffectsHandler::unloadEffect : Unloading Effect : " << name;
            if( activeFullScreenEffect() == it.value().second )
                {
                setActiveFullScreenEffect( 0 );
                }
            delete it.value().second;
            effect_order.erase(it);
            effectsChanged();
            effect_libraries[ name ]->unload();
            return;
            }
        }

    kDebug( 1212 ) << "EffectsHandler::unloadEffect : Effect not loaded : " << name;
    }

void EffectsHandlerImpl::reconfigureEffect( const QString& name )
    {
    for( QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it)
        if ( (*it).first == name )
            {
            (*it).second->reconfigure( Effect::ReconfigureAll );
            return;
            }
    }

bool EffectsHandlerImpl::isEffectLoaded( const QString& name )
    {
    for( QVector< EffectPair >::iterator it = loaded_effects.begin(); it != loaded_effects.end(); ++it)
        if ( (*it).first == name )
            return true;

    return false;
    }

void EffectsHandlerImpl::effectsChanged()
    {
    loaded_effects.clear();
//    kDebug(1212) << "Recreating effects' list:";
    foreach( const EffectPair &effect, effect_order )
        {
//        kDebug(1212) << effect.first;
        loaded_effects.append( effect );
        }
    }


//****************************************
// EffectWindowImpl
//****************************************

EffectWindowImpl::EffectWindowImpl() : EffectWindow()
    , toplevel( NULL )
    , sw( NULL )
    {
    }

EffectWindowImpl::~EffectWindowImpl()
    {
    }

bool EffectWindowImpl::isPaintingEnabled()
    {
    return sceneWindow()->isPaintingEnabled();
    }

void EffectWindowImpl::enablePainting( int reason )
    {
    sceneWindow()->enablePainting( reason );
    }

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();
    }

bool EffectWindowImpl::isOnAllDesktops() const
    {
    return desktop() == NET::OnAllDesktops;
    }

QString EffectWindowImpl::caption() const
    {
    if( Client* c = dynamic_cast<Client*>( toplevel ))
        return c->caption();
    else
        return "";
    }

QString EffectWindowImpl::windowClass() const
    {
    return toplevel->resourceName() + ' ' + toplevel->resourceClass();
    }

QString EffectWindowImpl::windowRole() const
    {
    return toplevel->windowRole();
    }

QPixmap EffectWindowImpl::icon() const
    {
    if( Client* c = dynamic_cast<Client*>( toplevel ))
        return c->icon();
    return QPixmap(); // TODO
    }

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 ))
        return c->isMinimized();
    else
        return false;
    }

double EffectWindowImpl::opacity() const
    {
    return toplevel->opacity();
    }

bool EffectWindowImpl::hasAlpha() const
    {
    return toplevel->hasAlpha();
    }


bool EffectWindowImpl::isDeleted() const
    {
    return (dynamic_cast<Deleted*>( toplevel ) != 0);
    }

void EffectWindowImpl::refWindow()
    {
    if( Deleted* d = dynamic_cast< Deleted* >( toplevel ))
        return d->refWindow();
    abort(); // TODO
    }

void EffectWindowImpl::unrefWindow()
    {
    if( Deleted* d = dynamic_cast< Deleted* >( toplevel ))
        return d->unrefWindow( true ); // delayed
    abort(); // TODO
    }

void EffectWindowImpl::setWindow( Toplevel* w )
    {
    toplevel = w;
    }

void EffectWindowImpl::setSceneWindow( Scene::Window* w )
    {
    sw = w;
    }

int EffectWindowImpl::x() const
    {
    return toplevel->x();
    }

int EffectWindowImpl::y() const
    {
    return toplevel->y();
    }

int EffectWindowImpl::width() const
    {
    return toplevel->width();
    }

int EffectWindowImpl::height() const
    {
    return toplevel->height();
    }

QRect EffectWindowImpl::geometry() const
    {
    return toplevel->geometry();
    }

QRegion EffectWindowImpl::shape() const
    {
    return sw ? sw->shape() : geometry();
    }

int EffectWindowImpl::screen() const
    {
    return toplevel->screen();
    }

bool EffectWindowImpl::hasOwnShape() const
    {
    return toplevel->shape();
    }

QSize EffectWindowImpl::size() const
    {
    return toplevel->size();
    }

QPoint EffectWindowImpl::pos() const
    {
    return toplevel->pos();
    }

QRect EffectWindowImpl::rect() const
    {
    return toplevel->rect();
    }

QRect EffectWindowImpl::contentsRect() const
    {
    return QRect( toplevel->clientPos(), toplevel->clientSize());
    }

QRect EffectWindowImpl::decorationInnerRect() const
    {
    Client *client = dynamic_cast<Client*>(toplevel);
    return client ? client->transparentRect() : contentsRect();
    }

QByteArray EffectWindowImpl::readProperty( long atom, long type, int format ) const
    {
    return readWindowProperty( window()->window(), atom, type, format );
    }

void EffectWindowImpl::deleteProperty(long int atom) const
    {
    deleteWindowProperty( window()->window(), atom );
    }

bool EffectWindowImpl::isMovable() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        return c->isMovable();
    return false;
    }

bool EffectWindowImpl::isMovableAcrossScreens() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        return c->isMovableAcrossScreens();
    return false;
    }

bool EffectWindowImpl::isUserMove() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        return c->isMove();
    return false;
    }

bool EffectWindowImpl::isUserResize() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        return c->isResize();
    return false;
    }

QRect EffectWindowImpl::iconGeometry() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        return c->iconGeometry();
    return QRect();
    }

bool EffectWindowImpl::isDesktop() const
    {
    return toplevel->isDesktop();
    }

bool EffectWindowImpl::isDock() const
    {
    return toplevel->isDock();
    }

bool EffectWindowImpl::isToolbar() const
    {
    return toplevel->isToolbar();
    }

bool EffectWindowImpl::isTopMenu() const
    {
    return toplevel->isTopMenu();
    }

bool EffectWindowImpl::isMenu() const
    {
    return toplevel->isMenu();
    }

bool EffectWindowImpl::isNormalWindow() const
    {
    return toplevel->isNormalWindow();
    }

bool EffectWindowImpl::isSpecialWindow() const
    {
    if( Client* c = dynamic_cast<Client*>( toplevel ))
        return c->isSpecialWindow();
    else
        return true;
    }

bool EffectWindowImpl::isDialog() const
    {
    return toplevel->isDialog();
    }

bool EffectWindowImpl::isSplash() const
    {
    return toplevel->isSplash();
    }

bool EffectWindowImpl::isUtility() const
    {
    return toplevel->isUtility();
    }

bool EffectWindowImpl::isDropdownMenu() const
    {
    return toplevel->isDropdownMenu();
    }

bool EffectWindowImpl::isPopupMenu() const
    {
    return toplevel->isPopupMenu();
    }

bool EffectWindowImpl::isTooltip() const
    {
    return toplevel->isTooltip();
    }

bool EffectWindowImpl::isNotification() const
    {
    return toplevel->isNotification();
    }

bool EffectWindowImpl::isComboBox() const
    {
    return toplevel->isComboBox();
    }

bool EffectWindowImpl::isDNDIcon() const
    {
    return toplevel->isDNDIcon();
    }

bool EffectWindowImpl::isManaged() const
    {
    return dynamic_cast< const Client* >( toplevel ) != NULL;
    }

bool EffectWindowImpl::acceptsFocus() const
    {
    const Client* client = dynamic_cast< const Client* >( toplevel );
    if( !client )
        return true; // We don't actually know...
    return client->wantsInput();
    }

bool EffectWindowImpl::keepAbove() const
    {
    const Client* client = dynamic_cast< const Client* >( toplevel );
    if( !client )
        return true;
    return client->keepAbove();
    }

bool EffectWindowImpl::isModal() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        return c->isModal();
    return false;
    }

EffectWindow* EffectWindowImpl::findModal()
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        {
        if( Client* c2 = c->findModal())
            return c2->effectWindow();
        }
    return NULL;
    }

EffectWindowList EffectWindowImpl::mainWindows() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        {
        EffectWindowList ret;
        ClientList mainclients = c->mainClients();
        foreach( Client* tmp, mainclients )
            ret.append( tmp->effectWindow());
        return ret;
        }
    return EffectWindowList();
    }

bool EffectWindowImpl::isSkipSwitcher() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        return c->skipSwitcher();
    return false;
    }

WindowQuadList EffectWindowImpl::buildQuads( bool force ) const
    {
    return sceneWindow()->buildQuads( force );
    }

void EffectWindowImpl::minimize() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        {
        c->minimize();
        }
    }

void EffectWindowImpl::unminimize() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        {
        c->unminimize();
        }
    }

void EffectWindowImpl::closeWindow() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        {
        c->closeWindow();
        }
    }

bool EffectWindowImpl::visibleInClientGroup() const
    {
    if( Client* c = dynamic_cast< Client* >( toplevel ))
        {
        if( !c->clientGroup() )
            return true;
        return c == c->clientGroup()->visible();
        }
    return false;
    }

void EffectWindowImpl::setData( int role, const QVariant &data )
    {
    if ( !data.isNull() )
        dataMap[ role ] = data;
    else
        dataMap.remove( role );
    }

QVariant EffectWindowImpl::data( int role ) const
    {
    if( !dataMap.contains( role ) )
        return QVariant();
    return dataMap[ role ];
    }

EffectWindow* effectWindow( Toplevel* w )
    {
    EffectWindowImpl* ret = w->effectWindow();
    return ret;
    }

EffectWindow* effectWindow( Scene::Window* w )
    {
    EffectWindowImpl* ret = w->window()->effectWindow();
    ret->setSceneWindow( w );
    return ret;
    }

//****************************************
// EffectWindowGroupImpl
//****************************************


EffectWindowList EffectWindowGroupImpl::members() const
    {
    EffectWindowList ret;
    foreach( Toplevel* c, group->members())
        ret.append( c->effectWindow());
    return ret;
    }

} // namespace
