/*************************************************************************** * Copyright (C) 2004 by Hans Oischinger * * hans.oischinger@kde-mail.net * * * * 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, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #include "komposetaskmanager.h" #include "komposelayout.h" #include "komposedesktopwidget.h" #include "komposesettings.h" #include "komposetaskwidget.h" #include "komposeglobal.h" #include "komposetask.h" #include "komposeviewmanager.h" #include #include #include #include #include #include #include #include #include #include #ifdef COMPOSITE #include #include #include #include #include #include #endif static KomposeTaskManager* taskManagerInstance = 0; /** * Taskmanager is a singleton */ KomposeTaskManager* KomposeTaskManager::instance() { if ( !taskManagerInstance ) { kdDebug() << "KomposeTaskManager::instance() - Creating Singleton instance" << endl; taskManagerInstance = new KomposeTaskManager(); } return taskManagerInstance; } KomposeTaskManager::KomposeTaskManager() : TQObject() { taskManagerInstance = this; twin_module = new KWinModule(); numDesks = KWin::numberOfDesktops(); callCompositeRedirect(); // Listeners for KWinmodule signals connect( twin_module, SIGNAL(windowAdded(WId)), this, SLOT(slotWindowAdded(WId)) ); connect( twin_module, SIGNAL(windowRemoved(WId)), this, SLOT(slotWindowRemoved(WId)) ); connect( twin_module, SIGNAL(numberOfDesktopsChanged(int)), this, SLOT(slotDesktopCountChanged(int)) ); connect( twin_module, SIGNAL(currentDesktopChanged(int)), this, SLOT(slotCurrentDesktopChanged(int)) ); connect( KomposeSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(slotStartWindowListeners()) ); connect( KomposeSettings::instance(), SIGNAL(settingsChanged()), this, SLOT(callCompositeRedirect()) ); // register existing windows const TQValueList windows = twin_module->windows(); for (TQValueList::ConstIterator it = windows.begin(); it != windows.end(); ++it ) slotWindowAdded(*it); connect( twin_module, SIGNAL(activeWindowChanged(WId)), this, SLOT(slotTaskActivated(WId)) ); slotStartWindowListeners(); } KomposeTaskManager::~KomposeTaskManager() { delete twin_module; } /** * Redirect or unredirect all root windows to offscreen buffers */ void KomposeTaskManager::callCompositeRedirect() { #ifdef COMPOSITE if ( KomposeGlobal::instance()->hasXcomposite() ) { Display *dpy = TQPaintDevice::x11AppDisplay(); if ( KomposeSettings::instance()->getUseComposite() ) { // Redirect for ( int i = 0; i < ScreenCount( dpy ); i++ ) XCompositeRedirectSubwindows( dpy, RootWindow( dpy, i ), CompositeRedirectAutomatic ); } else if ( !KomposeSettings::instance()->getUseComposite() ) { // Unredirect for ( int i = 0; i < ScreenCount( dpy ); i++ ) XCompositeUnredirectSubwindows( dpy, RootWindow( dpy, i ), CompositeRedirectAutomatic ); } } #endif } /** * Helper function that finds a KomposeTask object by it's window id * @param w WindowID of the Task * @return Corresponding KomposeTask object */ KomposeTask* KomposeTaskManager::findTask(WId w, bool wmFrameIds ) { for (KomposeTask* t = tasklist.first(); t != 0; t = tasklist.next()) if ((!wmFrameIds && t->window() == w) || (wmFrameIds && t->wmFrame() == w) ) return t; return 0; } void KomposeTaskManager::slotStartWindowListeners() { disconnect( twin_module, SIGNAL(windowChanged( WId, unsigned int )), this, SLOT(slotWindowChanged( WId, unsigned int )) ); connect( twin_module, SIGNAL(windowChanged( WId, unsigned int )), this, SLOT(slotWindowChanged( WId, unsigned int )) ); } void KomposeTaskManager::slotWindowChanged( WId w, unsigned int dirty) { if( dirty & NET::WMState ) { NETWinInfo info ( tqt_xdisplay(), w, tqt_xrootwin(), NET::WMState ); if ( (info.state() & NET::SkipTaskbar) != 0 ) { slotWindowRemoved( w ); return; } else { if( !findTask( w )) slotWindowAdded( w ); } } // check if any state we are interested in is marked dirty if(!(dirty & (NET::WMVisibleName|NET::WMVisibleIconName|NET::WMName|NET::WMState|NET::WMIcon|NET::XAWMState|NET::WMDesktop)) ) return; // find task KomposeTask* t = findTask( w ); if (!t) return; int oldTaskDesktop = t->onDesktop(); // TODO: Instead of one refresh() method we could implement specific method for names and geometry, etc... // checked like this: if(dirty & (NET::WMDesktop|NET::WMState|NET::XAWMState)) t->refresh(); // Finally check if the window has been moved to a different virtual desktop if( (dirty & NET::WMDesktop ) && ( oldTaskDesktop != t->onDesktop() ) ) emit taskDesktopChanged( t, oldTaskDesktop, t->onDesktop() ); } void KomposeTaskManager::slotWindowRemoved(WId w ) { // find task KomposeTask* task = findTask( w ); if (!task) return; //kdDebug() << "KomposeTaskManager::slotWindowRemoved(WId w ) - Removing task %s", task->visibleNameWithState()); tasklist.remove( task ); delete task; } void KomposeTaskManager::slotWindowAdded(WId w ) { // ignore myself if ( TQWidget::find(w) != 0 ) return; // if ( KomposeViewManager::instance()->hasActiveView() && w == KomposeViewManager::instance()->getViewWidget()->winId() ) // { // return; // } KWin::WindowInfo info = KWin::windowInfo(w); // ignore NET::Tool and other special window types NET::WindowType mytype = info.windowType(NET::NormalMask | NET::DesktopMask | NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask); if (mytype != NET::Normal && mytype != NET::Override && mytype != NET::Unknown && mytype != NET::Dialog) return; // ignore windows that want to be ignored by the taskbar if ((info.state() & NET::SkipTaskbar) != 0) { return; } if ( !info.valid() ) return; kdDebug() << "KomposeTaskManager::slotWindowAdded(WId " << w <<" ) - Adding KomposeTask" << endl; KomposeTask* t = new KomposeTask(w, twin_module, this); tasklist.append(t); emit newTask( t ); } /** * Called when Komposé requires screenshots of all tasks */ void KomposeTaskManager::slotUpdateScreenshots( bool switchDesktops ) { kdDebug() << "KomposeTaskManager::slotUpdateScreenshots()" << endl; TQPtrListIterator it( tasklist ); KomposeTask *task; // Iterate through tasks sorted by desktops (this minimizes desktop switching if necessary) for ( int desk = -1; desk <= numDesks; ++desk ) { // Desk == 0 should not be possible, however -1 means on all desks if (desk==0 || ( !switchDesktops && desk != KomposeViewManager::instance()->getDesktopBeforeSnaps()+1 ) ) continue; it.toFirst(); while ( (task = it.current()) != 0 ) { ++it; if ( task->onDesktop() == desk ) task->slotUpdateScreenshot(); } } } /** * simulates a window activated event for the currently active window (no forcing of screenshots) */ void KomposeTaskManager::simulatePasvScreenshotEvent() { kdDebug() << "KomposeTaskManager::simulatePasvScreenshotEvent()" << endl; slotTaskActivated( twin_module->activeWindow() ); } /** * Signals the task object that it's window has been activated */ void KomposeTaskManager::slotTaskActivated(WId winId) { kdDebug() << "KomposeTaskManager::slotTaskActivated ( " << winId << " )" << endl; TQPtrListIterator it( tasklist ); KomposeTask *task; while ( (task = it.current()) != 0 ) { ++it; if ( winId == task->window() ) { task->slotActivated(); return; } } } void KomposeTaskManager::slotDesktopCountChanged(int d) { numDesks = d; } bool KomposeTaskManager::isOnTop(const KomposeTask* task) { if(!task) return false; for (TQValueList::ConstIterator it = twin_module->stackingOrder().fromLast(); it != twin_module->stackingOrder().end(); --it ) { for (KomposeTask* t = tasklist.first(); t != 0; t = tasklist.next() ) { if ( (*it) == t->window() ) { if ( t == task ) return true; if ( !t->isIconified() && (t->isAlwaysOnTop() == task->isAlwaysOnTop()) ) return false; break; } } } return false; } TQString KomposeTaskManager::getDesktopName(int desk) const { return twin_module->desktopName(desk); } /** * The kapp x11EventFilter method redirect to this method * @param event * @return */ bool KomposeTaskManager::processX11Event( XEvent *event ) { #ifdef COMPOSITE if ( KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite() ) { if ( event->type == ConfigureNotify ) { XConfigureEvent *e = &event->xconfigure; KomposeTask* t = findTask( e->window, true ); if (!t) return false; t->slotX11ConfigureNotify(); return true; } else if ( event->type == KomposeGlobal::instance()->getDamageEvent() + XDamageNotify ) { XDamageNotifyEvent *e = reinterpret_cast( event ); // e->drawable is the window ID of the damaged window // e->geometry is the geometry of the damaged window // e->area is the bounding rect for the damaged area // e->damage is the damage handle returned by XDamageCreate() // Subtract all the damage, repairing the window. XDamageSubtract( TQPaintDevice::x11AppDisplay(), e->damage, None, None ); if ( !KomposeViewManager::instance()->hasActiveView() ) return true; // FIXME: too many damage events are called. block findTask here... // FIXME: Don't try XDamage with KAsteroids! Do something to avoid 100% cpu usage KomposeTask* t = findTask( e->drawable ); if (!t) return false; t->slotX11DamageNotify(); return true; } } #endif return false; } void KomposeTaskManager::slotCurrentDesktopChanged(int d) { #ifdef COMPOSITE if ( KomposeGlobal::instance()->hasXcomposite() && KomposeSettings::instance()->getUseComposite() ) { // Strangely a ConfigureNotify is only sent when I click on a window on the new desktop // and not when I cahnge the desktop, although the windows get mapped at this point. // Is this a X bug? However the following hack helps: // Do as if we received a ConfigureNotify event to update all backing pixmaps for (KomposeTask* t = tasklist.first(); t != 0; t = tasklist.next()) if ( t->onDesktop() == d ) t->slotX11ConfigureNotify(); } #endif } int KomposeTaskManager::getCurrentDesktopNum() { return twin_module->currentDesktop(); } #include "komposetaskmanager.moc"