|
|
|
/***************************************************************************
|
|
|
|
* 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 <tqtimer.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <tqwidget.h>
|
|
|
|
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
#include <twinmodule.h>
|
|
|
|
#include <netwm.h>
|
|
|
|
#include <twin.h>
|
|
|
|
#include <tdeapplication.h>
|
|
|
|
#include <kdebug.h>
|
|
|
|
|
|
|
|
#ifdef COMPOSITE
|
|
|
|
#include <X11/Xlib.h>
|
|
|
|
#include <X11/Xutil.h>
|
|
|
|
#include <X11/Xatom.h>
|
|
|
|
#include <X11/extensions/Xcomposite.h>
|
|
|
|
#include <X11/extensions/Xdamage.h>
|
|
|
|
#include <X11/extensions/Xrender.h>
|
|
|
|
#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, TQ_SIGNAL(windowAdded(WId)), this, TQ_SLOT(slotWindowAdded(WId)) );
|
|
|
|
connect( twin_module, TQ_SIGNAL(windowRemoved(WId)), this, TQ_SLOT(slotWindowRemoved(WId)) );
|
|
|
|
connect( twin_module, TQ_SIGNAL(numberOfDesktopsChanged(int)), this, TQ_SLOT(slotDesktopCountChanged(int)) );
|
|
|
|
connect( twin_module, TQ_SIGNAL(currentDesktopChanged(int)), this, TQ_SLOT(slotCurrentDesktopChanged(int)) );
|
|
|
|
|
|
|
|
connect( KomposeSettings::instance(), TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(slotStartWindowListeners()) );
|
|
|
|
connect( KomposeSettings::instance(), TQ_SIGNAL(settingsChanged()), this, TQ_SLOT(callCompositeRedirect()) );
|
|
|
|
|
|
|
|
// register existing windows
|
|
|
|
const TQValueList<WId> windows = twin_module->windows();
|
|
|
|
for (TQValueList<WId>::ConstIterator it = windows.begin(); it != windows.end(); ++it )
|
|
|
|
slotWindowAdded(*it);
|
|
|
|
|
|
|
|
connect( twin_module, TQ_SIGNAL(activeWindowChanged(WId)), this, TQ_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, TQ_SIGNAL(windowChanged( WId, unsigned int )), this,
|
|
|
|
TQ_SLOT(slotWindowChanged( WId, unsigned int )) );
|
|
|
|
connect( twin_module, TQ_SIGNAL(windowChanged( WId, unsigned int )), this,
|
|
|
|
TQ_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<KomposeTask> 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<KomposeTask> 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<WId>::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<XDamageNotifyEvent*>( 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"
|