/******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2006 Lubos Lunak 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 . *********************************************************************/ #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 #include "kdebug.h" #include "klibrary.h" #include "kdesktopfile.h" #include "kconfiggroup.h" #include "kstandarddirs.h" #include #include #include #include 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 ) , current_paint_effectframe( 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( w ), mask, region, data ); } void EffectsHandlerImpl::paintEffectFrame( EffectFrame* frame, QRegion region, double opacity, double frameOpacity ) { if( current_paint_effectframe < loaded_effects.size()) { loaded_effects[current_paint_effectframe++].second->paintEffectFrame( frame, region, opacity, frameOpacity ); --current_paint_effectframe; } else { const EffectFrameImpl* frameImpl = static_cast( frame ); frameImpl->finalRender( region, opacity, frameOpacity ); } } 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( 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(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(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(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(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(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<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(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(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 ); } } EffectFrame* EffectsHandlerImpl::effectFrame( EffectFrameStyle style, bool staticSize, const QPoint& position, Qt::Alignment alignment ) const { return new EffectFrameImpl( style, staticSize, position, alignment ); } //**************************************** // 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( 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( 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( 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( 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(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( 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; } //**************************************** // EffectFrameImpl //**************************************** EffectFrameImpl::EffectFrameImpl( EffectFrameStyle style, bool staticSize, QPoint position, Qt::Alignment alignment ) : QObject ( 0 ) , EffectFrame() , m_style( style ) , m_static( staticSize ) , m_point( position ) , m_alignment( alignment ) , m_shader( NULL ) { if( m_style == Styled ) { m_frame.setImagePath( "widgets/background" ); m_frame.setCacheAllRenderedFrames( true ); m_selection.setImagePath( "widgets/viewitem" ); m_selection.setElementPrefix( "hover" ); m_selection.setCacheAllRenderedFrames( true ); m_selection.setEnabledBorders( Plasma::FrameSvg::AllBorders ); connect( Plasma::Theme::defaultTheme(), SIGNAL( themeChanged() ), this, SLOT( plasmaThemeChanged() )); } if( effects->compositingType() == OpenGLCompositing ) { m_sceneFrame = new SceneOpenGL::EffectFrame( this ); } else if( effects->compositingType() == XRenderCompositing ) { m_sceneFrame = new SceneXrender::EffectFrame( this ); } else { // that should not happen and will definitely crash! m_sceneFrame = NULL; } } EffectFrameImpl::~EffectFrameImpl() { delete m_sceneFrame; } const QFont& EffectFrameImpl::font() const { return m_font; } void EffectFrameImpl::setFont( const QFont& font ) { if (m_font == font) { return; } m_font = font; QRect oldGeom = m_geometry; if( !m_text.isEmpty() ) { autoResize(); } if( oldGeom == m_geometry ) { // Wasn't updated in autoResize() m_sceneFrame->freeTextFrame(); } } void EffectFrameImpl::free() { m_sceneFrame->free(); } const QRect& EffectFrameImpl::geometry() const { return m_geometry; } void EffectFrameImpl::setGeometry( const QRect& geometry, bool force ) { QRect oldGeom = m_geometry; m_geometry = geometry; if( m_geometry == oldGeom && !force ) { return; } effects->addRepaint( oldGeom ); effects->addRepaint( m_geometry ); if( m_geometry.size() == oldGeom.size() && !force ) { return; } if( m_style == Styled ) { qreal left, top, right, bottom; m_frame.getMargins( left, top, right, bottom ); // m_geometry is the inner geometry m_frame.resizeFrame( m_geometry.adjusted( -left, -top, right, bottom ).size() ); } free(); } const QPixmap& EffectFrameImpl::icon() const { return m_icon; } void EffectFrameImpl::setIcon( const QPixmap& icon ) { m_icon = icon; if( isCrossFade() ) { m_sceneFrame->crossFadeIcon(); } if( m_iconSize.isEmpty() ) // Set a size if we don't already have one { setIconSize( m_icon.size() ); } m_sceneFrame->freeIconFrame(); } const QSize& EffectFrameImpl::iconSize() const { return m_iconSize; } void EffectFrameImpl::setIconSize( const QSize& size ) { if( m_iconSize == size ) { return; } m_iconSize = size; autoResize(); m_sceneFrame->freeIconFrame(); } void EffectFrameImpl::plasmaThemeChanged() { free(); } void EffectFrameImpl::render( QRegion region, double opacity, double frameOpacity ) { if( m_geometry.isEmpty() ) { return; // Nothing to display } m_shader = NULL; effects->paintEffectFrame( this, region, opacity, frameOpacity ); } void EffectFrameImpl::finalRender( QRegion region, double opacity, double frameOpacity ) const { region = infiniteRegion(); // TODO: Old region doesn't seem to work with OpenGL m_sceneFrame->render( region, opacity, frameOpacity ); } Qt::Alignment EffectFrameImpl::alignment() const { return m_alignment; } void EffectFrameImpl::setAlignment( Qt::Alignment alignment ) { m_alignment = alignment; } void EffectFrameImpl::setPosition( const QPoint& point ) { if( m_point == point ) { return; } m_point = point; autoResize(); free(); } const QString& EffectFrameImpl::text() const { return m_text; } void EffectFrameImpl::setText( const QString& text ) { if( m_text == text ) { return; } if( isCrossFade() ) { m_sceneFrame->crossFadeText(); } m_text = text; QRect oldGeom = m_geometry; autoResize(); if( oldGeom == m_geometry ) { // Wasn't updated in autoResize() m_sceneFrame->freeTextFrame(); } } void EffectFrameImpl::setSelection( const QRect& selection ) { if( selection == m_selectionGeometry ) { return; } m_selectionGeometry = selection; if( m_selectionGeometry.size() != m_selection.frameSize().toSize() ) { m_selection.resizeFrame( m_selectionGeometry.size() ); } // TODO; optimize to only recreate when resizing m_sceneFrame->freeSelection(); } void EffectFrameImpl::autoResize() { if( m_static ) return; // Not automatically resizing QRect geometry; // Set size if( !m_text.isEmpty() ) { QFontMetrics metrics( m_font ); geometry.setSize( metrics.size( 0, m_text )); } if( !m_icon.isNull() && !m_iconSize.isEmpty() ) { geometry.setLeft( -m_iconSize.width() ); if( m_iconSize.height() > geometry.height() ) geometry.setHeight( m_iconSize.height() ); } // Set position if( m_alignment & Qt::AlignLeft ) geometry.moveLeft( m_point.x() ); else if( m_alignment & Qt::AlignRight ) geometry.moveLeft( m_point.x() - geometry.width() ); else geometry.moveLeft( m_point.x() - geometry.width() / 2 ); if( m_alignment & Qt::AlignTop ) geometry.moveTop( m_point.y() ); else if( m_alignment & Qt::AlignBottom ) geometry.moveTop( m_point.y() - geometry.height() ); else geometry.moveTop( m_point.y() - geometry.height() / 2 ); setGeometry( geometry ); } } // namespace