1
/*****************************************************************
3
Copyright (c) 2000 Matthias Elter <elter@kde.org>
5
Permission is hereby granted, free of charge, to any person obtaining a copy
6
of this software and associated documentation files (the "Software"), to deal
7
in the Software without restriction, including without limitation the rights
8
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
copies of the Software, and to permit persons to whom the Software is
10
furnished to do so, subject to the following conditions:
12
The above copyright notice and this permission notice shall be included in
13
all copies or substantial portions of the Software.
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
******************************************************************/
25
#include "taskmanager.h"
28
#include <QApplication>
29
#include <QDesktopWidget>
34
#include <KConfigGroup>
39
#include <KStandardDirs>
50
#include <kephal/screens.h>
51
#include <kactivityconsumer.h>
56
class TaskManagerSingleton
62
K_GLOBAL_STATIC( TaskManagerSingleton, privateTaskManagerSelf )
64
TaskManager* TaskManager::self()
66
return &privateTaskManagerSelf->self;
69
class TaskManager::Private
72
Private(TaskManager *manager)
80
void onAppExitCleanup()
82
q->disconnect(KWindowSystem::self(), 0, q, 0);
88
foreach (TaskPtr task, tasksByWId) {
89
task->clearPixmapData();
92
foreach (StartupPtr startup, startups) {
93
startup->clearPixmapData();
99
KStartupInfo* startupInfo;
102
StartupList startups;
103
WindowList skiptaskbarWindows;
104
QSet<QUuid> trackGeometryTokens;
105
KActivityConsumer activityConsumer;
108
TaskManager::TaskManager()
112
KGlobal::locale()->insertCatalog("libtaskmanager");
113
connect(KWindowSystem::self(), SIGNAL(windowAdded(WId)),
114
this, SLOT(windowAdded(WId)));
115
connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)),
116
this, SLOT(windowRemoved(WId)));
117
connect(KWindowSystem::self(), SIGNAL(activeWindowChanged(WId)),
118
this, SLOT(activeWindowChanged(WId)));
119
connect(KWindowSystem::self(), SIGNAL(currentDesktopChanged(int)),
120
this, SLOT(currentDesktopChanged(int)));
121
connect(KWindowSystem::self(), SIGNAL(windowChanged(WId,const unsigned long*)),
122
this, SLOT(windowChanged(WId,const unsigned long*)));
123
connect(&d->activityConsumer, SIGNAL(currentActivityChanged(QString)),
124
this, SLOT(currentActivityChanged(QString)));
125
if (QCoreApplication::instance()) {
126
connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(onAppExitCleanup()));
129
currentActivityChanged(d->activityConsumer.currentActivity());
131
// register existing windows
132
const QList<WId> windows = KWindowSystem::windows();
133
QList<WId>::ConstIterator end(windows.end());
134
for (QList<WId>::ConstIterator it = windows.begin(); it != end; ++it)
140
WId win = KWindowSystem::activeWindow();
141
activeWindowChanged(win);
143
d->watcher = new KDirWatch(this);
144
d->watcher->addFile(KGlobal::dirs()->locateLocal("config", "klaunchrc"));
145
connect(d->watcher, SIGNAL(dirty(const QString&)), this, SLOT(configureStartup()));
146
connect(d->watcher, SIGNAL(created(const QString&)), this, SLOT(configureStartup()));
147
connect(d->watcher, SIGNAL(deleted(const QString&)), this, SLOT(configureStartup()));
152
TaskManager::~TaskManager()
154
KGlobal::locale()->removeCatalog("libtaskmanager");
158
void TaskManager::configureStartup()
160
KConfig _c("klaunchrc");
161
KConfigGroup c(&_c, "FeedbackStyle");
162
if (!c.readEntry("TaskbarButton", true)) {
163
delete d->startupInfo;
168
if (!d->startupInfo) {
169
d->startupInfo = new KStartupInfo(KStartupInfo::CleanOnCantDetect, this );
170
connect(d->startupInfo,
171
SIGNAL(gotNewStartup(const KStartupInfoId&, const KStartupInfoData&)),
172
SLOT(gotNewStartup(const KStartupInfoId&, const KStartupInfoData&)));
173
connect(d->startupInfo,
174
SIGNAL(gotStartupChange(const KStartupInfoId&, const KStartupInfoData&)),
175
SLOT(gotStartupChange(const KStartupInfoId&, const KStartupInfoData&)));
176
connect(d->startupInfo,
177
SIGNAL(gotRemoveStartup(const KStartupInfoId&, const KStartupInfoData&)),
178
SLOT(killStartup(const KStartupInfoId&)));
181
c = KConfigGroup(&_c, "TaskbarButtonSettings");
182
d->startupInfo->setTimeout(c.readEntry("Timeout", 30));
185
TaskPtr TaskManager::findTask(WId w)
187
TaskDict::const_iterator it = d->tasksByWId.constBegin();
188
TaskDict::const_iterator itEnd = d->tasksByWId.constEnd();
190
for (; it != itEnd; ++it) {
191
if (it.key() == w || it.value()->hasTransient(w)) {
199
TaskPtr TaskManager::findTask(int desktop, const QPoint& p)
201
QList<WId> list = KWindowSystem::stackingOrder();
204
int currentIndex = -1;
205
TaskDict::iterator itEnd = d->tasksByWId.end();
206
for (TaskDict::iterator it = d->tasksByWId.begin(); it != itEnd; ++it)
208
TaskPtr t = it.value();
209
if (!t->isOnAllDesktops() && t->desktop() != desktop)
215
if (t->isIconified() || t->isShaded())
220
if (t->geometry().contains(p))
222
int index = list.indexOf(t->window());
223
if (index > currentIndex)
225
currentIndex = index;
234
void TaskManager::windowAdded(WId w )
237
NETWinInfo info(QX11Info::display(), w, QX11Info::appRootWindow(),
238
NET::WMWindowType | NET::WMPid | NET::WMState);
240
// ignore NET::Tool and other special window types
241
NET::WindowType wType = info.windowType(NET::NormalMask | NET::DesktopMask | NET::DockMask |
242
NET::ToolbarMask | NET::MenuMask | NET::DialogMask |
243
NET::OverrideMask | NET::TopMenuMask |
244
NET::UtilityMask | NET::SplashMask);
246
if (wType != NET::Normal && wType != NET::Override && wType != NET::Unknown &&
247
wType != NET::Dialog && wType != NET::Utility) {
251
// ignore windows that want to be ignored by the taskbar
252
if ((info.state() & NET::SkipTaskbar) != 0) {
253
d->skiptaskbarWindows.insert(w); // remember them though
257
Window transient_for_tmp;
258
if (XGetTransientForHint(QX11Info::display(), (Window)w, &transient_for_tmp)) {
259
WId transient_for = (WId)transient_for_tmp;
261
// check if it's transient for a skiptaskbar window
262
if (d->skiptaskbarWindows.contains( transient_for )) {
266
// lets see if this is a transient for an existing task
267
if (transient_for != QX11Info::appRootWindow() &&
268
transient_for != 0 && wType != NET::Utility) {
269
TaskPtr t = findTask(transient_for);
271
if (t->window() != w) {
272
t->addTransient(w, info);
273
// kDebug() << "TM: Transient " << w << " added for Task: " << t->window();
281
TaskPtr t(new Task(w, 0));
282
d->tasksByWId[w] = t;
284
connect(t.data(), SIGNAL(changed(::TaskManager::TaskChanges)),
285
this, SLOT(taskChanged(::TaskManager::TaskChanges)));
287
if (d->startupInfo) {
288
KStartupInfoId startupInfoId;
289
// checkStartup modifies startupInfoId
290
d->startupInfo->checkStartup(w, startupInfoId);
291
foreach (StartupPtr startup, d->startups) {
292
if (startup->id() == startupInfoId) {
293
startup->addWindowMatch(w);
298
// kDebug() << "TM: Task added for WId: " << w;
302
void TaskManager::windowRemoved(WId w)
304
d->skiptaskbarWindows.remove(w);
307
TaskPtr t = findTask(w);
313
if (t->window() == w)
315
d->tasksByWId.remove(w);
323
//kDebug() << "TM: Task for WId " << w << " removed.";
328
t->removeTransient(w);
329
//kDebug() << "TM: Transient " << w << " for Task " << t->window() << " removed.";
333
void TaskManager::windowChanged(WId w, const unsigned long *dirty)
336
if (dirty[NETWinInfo::PROTOCOLS] & NET::WMState) {
337
NETWinInfo info (QX11Info::display(), w, QX11Info::appRootWindow(),
338
NET::WMState | NET::XAWMState);
340
if (info.state() & NET::SkipTaskbar) {
342
d->skiptaskbarWindows.insert(w);
345
d->skiptaskbarWindows.remove(w);
346
if (info.mappingState() != NET::Withdrawn && !findTask(w)) {
347
// skipTaskBar state was removed and the window is still
348
// mapped, so add this window
354
// check if any state we are interested in is marked dirty
355
if (!(dirty[NETWinInfo::PROTOCOLS] & (NET::WMVisibleName | NET::WMName |
356
NET::WMState | NET::WMIcon |
357
NET::XAWMState | NET::WMDesktop) ||
358
(trackGeometry() && dirty[NETWinInfo::PROTOCOLS] & NET::WMGeometry) ||
359
(dirty[NETWinInfo::PROTOCOLS2] & NET::WM2Activities))) {
364
TaskPtr t = findTask(w);
369
//kDebug() << "TaskManager::windowChanged " << w << " " << dirty[NETWinInfo::PROTOCOLS] << dirty[NETWinInfo::PROTOCOLS2];
371
unsigned long propagatedChanges = 0;
372
if ((dirty[NETWinInfo::PROTOCOLS] & NET::WMState) && t->updateDemandsAttentionState(w)) {
373
propagatedChanges = NET::WMState;
376
//kDebug() << "got changes, but will we refresh?" << dirty[NETWinInfo::PROTOCOLS] << dirty[NETWinInfo::PROTOCOLS2];
377
if (dirty[NETWinInfo::PROTOCOLS] || dirty[NETWinInfo::PROTOCOLS2]) {
378
// only refresh this stuff if we have other changes besides icons
379
t->refresh(Task::WindowProperties(dirty[NETWinInfo::PROTOCOLS] | propagatedChanges, dirty[NETWinInfo::PROTOCOLS2]));
384
void TaskManager::taskChanged(::TaskManager::TaskChanges changes)
386
Task *t = qobject_cast<Task*>(sender());
388
if (!t || changes == TaskUnchanged || !d->tasksByWId.contains(t->info().win())) {
392
emit windowChanged(d->tasksByWId[t->info().win()], changes);
395
void TaskManager::activeWindowChanged(WId w)
397
//kDebug() << "TaskManager::activeWindowChanged" << w;
398
TaskPtr t = findTask(w);
401
d->active->setActive(false);
404
//kDebug() << "no active window";
406
if (t->info().windowType(NET::UtilityMask) == NET::Utility) {
407
// we don't want to mark utility windows as active since task managers
408
// actually care about the main window and skip utility windows; utility
409
// windows are hidden when their associated window loses focus anyways
410
// see http://bugs.kde.org/show_bug.cgi?id=178509
415
d->active->setActive(false);
419
d->active->setActive(true);
420
//kDebug() << "active window is" << t->name();
424
void TaskManager::currentDesktopChanged(int desktop)
426
emit desktopChanged(desktop);
429
void TaskManager::currentActivityChanged(const QString &activity)
431
emit activityChanged(activity);
434
void TaskManager::gotNewStartup( const KStartupInfoId& id, const KStartupInfoData& data )
436
StartupPtr s( new Startup( id, data, 0 ) );
437
d->startups.append(s);
439
emit startupAdded(s);
442
void TaskManager::gotStartupChange( const KStartupInfoId& id, const KStartupInfoData& data )
444
StartupList::iterator itEnd = d->startups.end();
445
for (StartupList::iterator sIt = d->startups.begin(); sIt != itEnd; ++sIt)
447
if ((*sIt)->id() == id)
449
(*sIt)->update(data);
455
void TaskManager::killStartup( const KStartupInfoId& id )
457
StartupList::iterator sIt = d->startups.begin();
458
StartupList::iterator itEnd = d->startups.end();
460
for (; sIt != itEnd; ++sIt)
462
if ((*sIt)->id() == id)
473
d->startups.erase(sIt);
474
emit startupRemoved(s);
477
void TaskManager::killStartup(StartupPtr s)
484
StartupList::iterator sIt = d->startups.begin();
485
StartupList::iterator itEnd = d->startups.end();
486
for (; sIt != itEnd; ++sIt)
490
d->startups.erase(sIt);
495
emit startupRemoved(s);
498
QString TaskManager::desktopName(int desk) const
500
return KWindowSystem::desktopName(desk);
503
TaskDict TaskManager::tasks() const
505
return d->tasksByWId;
508
StartupList TaskManager::startups() const
513
int TaskManager::numberOfDesktops() const
515
return KWindowSystem::numberOfDesktops();
518
bool TaskManager::isOnTop(const Task* task) const
524
QList<WId> list = KWindowSystem::stackingOrder();
525
QList<WId>::const_iterator begin(list.constBegin());
526
QList<WId>::const_iterator it = list.constBegin() + (list.size() - 1);
528
TaskPtr t = d->tasksByWId.value(*it);
534
if (!t->isIconified() && (t->isAlwaysOnTop() == task->isAlwaysOnTop())) {
539
} while (it-- != begin);
544
void TaskManager::setTrackGeometry(bool track, const QUuid &token)
547
if (!d->trackGeometryTokens.contains(token)) {
548
d->trackGeometryTokens.insert(token);
551
d->trackGeometryTokens.remove(token);
555
bool TaskManager::trackGeometry() const
557
return !d->trackGeometryTokens.isEmpty();
560
bool TaskManager::isOnScreen(int screen, const WId wid)
566
KWindowInfo wi = KWindowSystem::windowInfo(wid, NET::WMFrameExtents);
568
// for window decos that fudge a bit and claim to extend beyond the
569
// edge of the screen, we just contract a bit.
570
QRect window = wi.frameGeometry();
571
QRect desktop = Kephal::ScreenUtils::screenGeometry(screen);
572
desktop.adjust(5, 5, -5, -5);
573
return window.intersects(desktop);
576
int TaskManager::currentDesktop() const
578
return KWindowSystem::currentDesktop();
581
QString TaskManager::currentActivity() const
583
return d->activityConsumer.currentActivity(); //TODO cache
586
} // TaskManager namespace
589
#include "taskmanager.moc"