2 KSysGuard, the KDE System Guard
4 Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org>
5 Copyright (c) 2006-2007 John Tapsell <john.tapsell@kde.org>
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
26 #include <kapplication.h>
27 #include <kiconloader.h>
36 #define HEADING_X_ICON_SIZE 16
43 #include <sys/types.h>
46 #include "ProcessModel.moc"
47 #include "ProcessModel_p.moc"
48 #include "ProcessModel.h"
49 #include "ProcessModel_p.h"
51 #include "processcore/processes.h"
52 #include "processcore/process.h"
54 extern KApplication
* Kapp
;
56 ProcessModelPrivate::ProcessModelPrivate() : mBlankPixmap(HEADING_X_ICON_SIZE
,1)
58 mBlankPixmap
.fill(QColor(0,0,0,0));
62 mNumProcessorCores
= 1;
64 mShowChildTotals
= true;
65 mIsChangingLayout
= false;
68 ProcessModelPrivate::~ProcessModelPrivate()
71 KSysGuard::Processes::returnInstance(mHostName
);
72 foreach(WindowInfo wininfo
, mPidToWindowInfo
) {
73 delete wininfo
.netWinInfo
;
77 ProcessModel::ProcessModel(QObject
* parent
, const QString
&host
)
78 : QAbstractItemModel(parent
), d(new ProcessModelPrivate
)
80 KGlobal::locale()->insertCatalog("processui"); //Make sure we include the translation stuff. This needs to be run before any i18n call here
82 if(host
.isEmpty() || host
== "localhost") {
83 d
->mHostName
= QString();
84 d
->mIsLocalhost
= true;
87 d
->mIsLocalhost
= false;
95 ProcessModel::~ProcessModel()
100 KSysGuard::Processes
*ProcessModel::processController()
102 return d
->mProcesses
;
105 void ProcessModelPrivate::windowRemoved(WId wid
) {
107 long long pid
= mWIdToPid
.value(wid
, 0);
110 int count
= mPidToWindowInfo
.count(pid
);
111 QMultiHash
<long long, WindowInfo
>::iterator i
= mPidToWindowInfo
.find(pid
);
112 while (i
!= mPidToWindowInfo
.end() && i
.key() == pid
) {
113 if(i
.value().wid
== wid
) {
114 delete i
.value().netWinInfo
;
115 i
= mPidToWindowInfo
.erase(i
);
120 Q_ASSERT(count
-1 == mPidToWindowInfo
.count(pid
) || count
== mPidToWindowInfo
.count(pid
));
121 KSysGuard::Process
*process
= mProcesses
->getProcess(pid
);
126 row
= process
->index
;
128 row
= process
->parent
->children
.indexOf(process
);
129 QModelIndex index1
= q
->createIndex(row
, ProcessModel::HeadingName
, process
);
130 emit q
->dataChanged(index1
, index1
);
131 QModelIndex index2
= q
->createIndex(row
, ProcessModel::HeadingXTitle
, process
);
132 emit q
->dataChanged(index2
, index2
);
136 void ProcessModelPrivate::setupWindows() {
138 QList
<WId
>::ConstIterator it
;
139 for ( it
= KWindowSystem::windows().begin(); it
!= KWindowSystem::windows().end(); ++it
) {
143 connect( KWindowSystem::self(), SIGNAL( windowChanged (WId
, unsigned int )), this, SLOT(windowChanged(WId
, unsigned int)));
144 connect( KWindowSystem::self(), SIGNAL( windowAdded (WId
)), this, SLOT(windowAdded(WId
)));
145 connect( KWindowSystem::self(), SIGNAL( windowRemoved (WId
)), this, SLOT(windowRemoved(WId
)));
149 void ProcessModelPrivate::setupProcesses() {
152 mPidToWindowInfo
.clear();
153 KSysGuard::Processes::returnInstance(mHostName
);
157 mProcesses
= KSysGuard::Processes::getInstance(mHostName
);
159 connect( mProcesses
, SIGNAL( processChanged(KSysGuard::Process
*, bool)), this, SLOT(processChanged(KSysGuard::Process
*, bool)));
160 connect( mProcesses
, SIGNAL( beginAddProcess(KSysGuard::Process
*)), this, SLOT(beginInsertRow( KSysGuard::Process
*)));
161 connect( mProcesses
, SIGNAL( endAddProcess()), this, SLOT(endInsertRow()));
162 connect( mProcesses
, SIGNAL( beginRemoveProcess(KSysGuard::Process
*)), this, SLOT(beginRemoveRow( KSysGuard::Process
*)));
163 connect( mProcesses
, SIGNAL( endRemoveProcess()), this, SLOT(endRemoveRow()));
164 connect( mProcesses
, SIGNAL( beginMoveProcess(KSysGuard::Process
*, KSysGuard::Process
*)), this,
165 SLOT( beginMoveProcess(KSysGuard::Process
*, KSysGuard::Process
*)));
166 connect( mProcesses
, SIGNAL( endMoveProcess()), this, SLOT(endMoveRow()));
167 mNumProcessorCores
= mProcesses
->numberProcessorCores();
168 if(mNumProcessorCores
< 1) mNumProcessorCores
=1; //Default to 1 if there was an error getting the number
172 void ProcessModelPrivate::windowChanged(WId wid
, unsigned int properties
)
174 if(! (properties
& NET::WMVisibleName
|| properties
& NET::WMName
|| properties
& NET::WMIcon
|| properties
& NET::WMState
)) return;
179 void ProcessModelPrivate::windowAdded(WId wid
)
181 foreach(WindowInfo w
, mPidToWindowInfo
.values()) {
182 if(w
.wid
== wid
) return; //already added
185 KXErrorHandler handler
;
186 NETWinInfo
*info
= new NETWinInfo( QX11Info::display(), wid
, QX11Info::appRootWindow(),
187 NET::WMPid
| NET::WMVisibleName
| NET::WMName
| NET::WMState
);
188 if (handler
.error( false ) ) {
190 return; //info is invalid - window just closed or something probably
192 long long pid
= info
->pid();
199 w
.icon
= KWindowSystem::icon(wid
, HEADING_X_ICON_SIZE
, HEADING_X_ICON_SIZE
, true);
202 mPidToWindowInfo
.insertMulti(pid
, w
);
203 mWIdToPid
[wid
] = pid
;
205 KSysGuard::Process
*process
= mProcesses
->getProcess(pid
);
206 if(!process
) return; //shouldn't really happen.. maybe race condition etc
209 row
= process
->index
;
211 row
= process
->parent
->children
.indexOf(process
);
212 QModelIndex index1
= q
->createIndex(row
, ProcessModel::HeadingName
, process
);
213 emit q
->dataChanged(index1
, index1
);
214 QModelIndex index2
= q
->createIndex(row
, ProcessModel::HeadingXTitle
, process
);
215 emit q
->dataChanged(index2
, index2
);
219 void ProcessModel::update(int updateDurationMS
) {
220 // kDebug() << "update all processes: " << QTime::currentTime().toString("hh:mm:ss.zzz");
221 d
->mProcesses
->updateAllProcesses(updateDurationMS
);
222 if(d
->mMemTotal
<= 0)
223 d
->mMemTotal
= d
->mProcesses
->totalPhysicalMemory();
224 if(d
->mIsChangingLayout
) {
225 d
->mIsChangingLayout
= false;
226 emit
layoutChanged();
228 // kDebug() << "finished: " << QTime::currentTime().toString("hh:mm:ss.zzz");
231 QString
ProcessModelPrivate::getStatusDescription(KSysGuard::Process::ProcessStatus status
) const
234 case KSysGuard::Process::Running
:
235 return i18n("- Process is doing some work");
236 case KSysGuard::Process::Sleeping
:
237 return i18n("- Process is waiting for something to happen");
238 case KSysGuard::Process::Stopped
:
239 return i18n("- Process has been stopped. It will not respond to user input at the moment");
240 case KSysGuard::Process::Zombie
:
241 return i18n("- Process has finished and is now dead, but the parent process has not cleaned up");
247 KSysGuard::Process
*ProcessModel::getProcessAtIndex(int index
) const
249 Q_ASSERT(d
->mSimple
);
250 return d
->mProcesses
->getAllProcesses().at(index
);
252 int ProcessModel::rowCount(const QModelIndex
&parent
) const
255 if(parent
.isValid()) return 0; //In flat mode, none of the processes have children
256 return d
->mProcesses
->getAllProcesses().count();
259 //Deal with the case that we are showing it as a tree
260 KSysGuard::Process
*process
;
261 if(parent
.isValid()) {
262 if(parent
.column() != 0) return 0; //For a treeview we say that only the first column has children
263 process
= reinterpret_cast< KSysGuard::Process
* > (parent
.internalPointer()); //when parent is invalid, it must be the root level which we set as 0
265 process
= d
->mProcesses
->getProcess(0);
268 int num_rows
= process
->children
.count();
272 int ProcessModel::columnCount ( const QModelIndex
& ) const
274 return d
->mHeadings
.count();
277 bool ProcessModel::hasChildren ( const QModelIndex
& parent
= QModelIndex() ) const
281 if(parent
.isValid()) return 0; //In flat mode, none of the processes have children
282 return !d
->mProcesses
->getAllProcesses().isEmpty();
285 //Deal with the case that we are showing it as a tree
286 KSysGuard::Process
*process
;
287 if(parent
.isValid()) {
288 if(parent
.column() != 0) return false; //For a treeview we say that only the first column has children
289 process
= reinterpret_cast< KSysGuard::Process
* > (parent
.internalPointer()); //when parent is invalid, it must be the root level which we set as 0
291 process
= d
->mProcesses
->getProcess(0);
294 bool has_children
= !process
->children
.isEmpty();
296 Q_ASSERT((rowCount(parent
) > 0) == has_children
);
300 QModelIndex
ProcessModel::index ( int row
, int column
, const QModelIndex
& parent
) const
302 if(row
<0) return QModelIndex();
303 if(column
<0 || column
>= d
->mHeadings
.count() ) return QModelIndex();
306 if( parent
.isValid()) return QModelIndex();
307 if( d
->mProcesses
->getAllProcesses().count() <= row
) return QModelIndex();
308 return createIndex( row
, column
, d
->mProcesses
->getAllProcesses().at(row
));
311 //Deal with the case that we are showing it as a tree
312 KSysGuard::Process
*parent_process
= 0;
314 if(parent
.isValid()) //not valid for init, and init has ppid of 0
315 parent_process
= reinterpret_cast< KSysGuard::Process
* > (parent
.internalPointer());
317 parent_process
= d
->mProcesses
->getProcess(0);
318 Q_ASSERT(parent_process
);
320 if(parent_process
->children
.count() > row
)
321 return createIndex(row
,column
, parent_process
->children
[row
]);
324 return QModelIndex();
328 bool ProcessModel::isSimpleMode() const
333 void ProcessModelPrivate::processChanged(KSysGuard::Process
*process
, bool onlyTotalCpu
)
337 row
= process
->index
;
339 row
= process
->parent
->children
.indexOf(process
);
341 int totalUpdated
= 0;
342 Q_ASSERT(row
!= -1); //Something has gone very wrong
344 if(mShowChildTotals
) {
345 //Only the total cpu usage changed, so only update that
346 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingCPUUsage
, process
);
347 emit q
->dataChanged(index
, index
);
351 if(process
->changes
== KSysGuard::Process::Nothing
) {
352 return; //Nothing changed
354 if(process
->changes
& KSysGuard::Process::Uids
) {
356 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingUser
, process
);
357 emit q
->dataChanged(index
, index
);
359 if(process
->changes
& KSysGuard::Process::Tty
) {
361 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingTty
, process
);
362 emit q
->dataChanged(index
, index
);
364 if(process
->changes
& (KSysGuard::Process::Usage
| KSysGuard::Process::Status
) || (process
->changes
& KSysGuard::Process::TotalUsage
&& mShowChildTotals
)) {
366 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingCPUUsage
, process
);
367 emit q
->dataChanged(index
, index
);
369 if(process
->changes
& KSysGuard::Process::NiceLevels
) {
371 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingNiceness
, process
);
372 emit q
->dataChanged(index
, index
);
374 if(process
->changes
& KSysGuard::Process::VmSize
) {
376 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingVmSize
, process
);
377 emit q
->dataChanged(index
, index
);
379 if(process
->changes
& (KSysGuard::Process::VmSize
| KSysGuard::Process::VmRSS
| KSysGuard::Process::VmURSS
)) {
381 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingMemory
, process
);
382 emit q
->dataChanged(index
, index
);
383 QModelIndex index2
= q
->createIndex(row
, ProcessModel::HeadingSharedMemory
, process
);
384 emit q
->dataChanged(index2
, index2
);
387 if(process
->changes
& KSysGuard::Process::Name
) {
389 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingName
, process
);
390 emit q
->dataChanged(index
, index
);
392 if(process
->changes
& KSysGuard::Process::Command
) {
394 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingCommand
, process
);
395 emit q
->dataChanged(index
, index
);
397 if(process
->changes
& KSysGuard::Process::Login
) {
399 QModelIndex index
= q
->createIndex(row
, ProcessModel::HeadingUser
, process
);
400 emit q
->dataChanged(index
, index
);
405 void ProcessModelPrivate::beginInsertRow( KSysGuard::Process
*process
)
408 if(mIsChangingLayout
) {
409 mIsChangingLayout
= false;
410 emit q
->layoutChanged();
414 int row
= mProcesses
->getAllProcesses().count();
415 q
->beginInsertRows( QModelIndex(), row
, row
);
419 //Deal with the case that we are showing it as a tree
420 int row
= process
->parent
->children
.count();
421 QModelIndex parentModelIndex
= q
->getQModelIndex(process
->parent
, 0);
423 //Only here can we actually change the model. First notify the view/proxy models then modify
424 q
->beginInsertRows(parentModelIndex
, row
, row
);
426 void ProcessModelPrivate::endInsertRow() {
429 void ProcessModelPrivate::beginRemoveRow( KSysGuard::Process
*process
)
431 if(mIsChangingLayout
) {
432 mIsChangingLayout
= false;
433 emit q
->layoutChanged();
437 Q_ASSERT(process
->pid
> 0);
440 return q
->beginRemoveRows(QModelIndex(), process
->index
, process
->index
);
442 int row
= process
->parent
->children
.indexOf(process
);
444 kDebug(1215) << "A serious problem occurred in remove row.";
448 return q
->beginRemoveRows(q
->getQModelIndex(process
->parent
,0), row
, row
);
451 void ProcessModelPrivate::endRemoveRow()
457 void ProcessModelPrivate::beginMoveProcess(KSysGuard::Process
*process
, KSysGuard::Process
*new_parent
)
459 if(mSimple
) return; //We don't need to move processes when in simple mode
460 if(!mIsChangingLayout
) {
461 emit q
->layoutAboutToBeChanged ();
462 mIsChangingLayout
= true;
466 int current_row
= process
->parent
->children
.indexOf(process
);
467 int new_row
= new_parent
->children
.count();
468 Q_ASSERT(current_row
!= -1);
470 QList
<QModelIndex
> fromIndexes
;
471 QList
<QModelIndex
> toIndexes
;
472 for(int i
=0; i
< q
->columnCount(); i
++) {
473 fromIndexes
<< q
->createIndex(current_row
, i
, process
);
474 toIndexes
<< q
->createIndex(new_row
, i
, process
);
476 q
->changePersistentIndexList(fromIndexes
, toIndexes
);
478 void ProcessModelPrivate::endMoveRow()
483 QModelIndex
ProcessModel::getQModelIndex( KSysGuard::Process
*process
, int column
) const
486 int pid
= process
->pid
;
487 if(pid
== 0) return QModelIndex(); //pid 0 is our fake process meaning the very root (never drawn). To represent that, we return QModelIndex() which also means the top element
490 row
= process
->index
;
492 row
= process
->parent
->children
.indexOf(process
);
495 return createIndex(row
, column
, process
);
498 QModelIndex
ProcessModel::parent ( const QModelIndex
& index
) const
500 if(!index
.isValid()) return QModelIndex();
501 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
505 return QModelIndex();
507 return getQModelIndex(process
->parent
,0);
510 QVariant
ProcessModel::headerData(int section
, Qt::Orientation orientation
,
513 if(orientation
!= Qt::Horizontal
)
515 if(section
< 0 || section
>= d
->mHeadings
.count())
516 return QVariant(); //is this needed?
518 case Qt::TextAlignmentRole
:
523 case HeadingSharedMemory
:
525 // return QVariant(Qt::AlignRight);
527 case HeadingCPUUsage
:
528 return QVariant(Qt::AlignCenter
);
533 case Qt::ToolTipRole
:
537 return i18n("The process name");
539 return i18n("The user that owns this process");
541 return i18n("The controlling terminal that this process is running on.");
542 case HeadingNiceness
:
543 return i18n("The priority that this process is being run with. Ranges from 19 (very nice, least priority) to -19 (top priority)");
544 case HeadingCPUUsage
:
545 if(d
->mNumProcessorCores
== 1)
546 return i18n("The current CPU usage of the process.");
548 return i18np("The current CPU usage of the process, divided by the %1 process core in the machine.", "The current total CPU usage of the process, divided by the %1 processor cores in the machine.", d
->mNumProcessorCores
);
550 return i18n("<qt>This is the amount of virtual memory space that the process is using, included shared libraries, graphics memory, files on disk, and so on. This number is almost meaningless.</qt>");
552 return i18n("<qt>This is the amount of real physical memory that this process is using by itself. It does not include any swapped out memory, nor the code size of its shared libraries. This is often the most useful figure to judge the memory use of a program.</qt>");
553 case HeadingSharedMemory
:
554 return i18n("<qt>This is the amount of real physical memory that this process's shared libraries are using. This memory is shared among all processes that use this library</qt>");
556 return i18n("<qt>The command that this process was launched with</qt>");
558 return i18n("<qt>The title of any windows that this process is showing</qt>");
560 return i18n("The unique Process ID that identifies this process");
565 case Qt::DisplayRole
:
566 return d
->mHeadings
[section
];
571 void ProcessModel::setSimpleMode(bool simple
)
573 if(d
->mSimple
== simple
) return;
575 if(!d
->mIsChangingLayout
) {
576 emit
layoutAboutToBeChanged ();
580 d
->mIsChangingLayout
= false;
584 QList
<QModelIndex
> flatIndexes
;
585 QList
<QModelIndex
> treeIndexes
;
586 foreach( KSysGuard::Process
*process
, d
->mProcesses
->getAllProcesses()) {
587 flatrow
= process
->index
;
588 treerow
= process
->parent
->children
.indexOf(process
);
592 for(int i
=0; i
< columnCount(); i
++) {
593 flatIndexes
<< createIndex(flatrow
, i
, process
);
594 treeIndexes
<< createIndex(treerow
, i
, process
);
596 if(d
->mSimple
) //change from tree mode to flat mode
597 changePersistentIndexList(treeIndexes
, flatIndexes
);
598 else // change from flat mode to tree mode
599 changePersistentIndexList(flatIndexes
, treeIndexes
);
601 emit
layoutChanged();
605 bool ProcessModel::canUserLogin(long long uid
) const
612 if(!d
->mIsLocalhost
) return true; //We only deal with localhost. Just always return true for non localhost
614 int canLogin
= d
->mUidCanLogin
.value(uid
, -1); //Returns 0 if we cannot login, 1 if we can, and the default is -1 meaning we don't know
615 if(canLogin
!= -1) return canLogin
; //We know whether they can log in
617 //We got the default, -1, so we don't know. Look it up
620 if(!user
.isValid()) {
621 //for some reason the user isn't recognised. This might happen under certain security situations.
622 //Just return true to be safe
623 d
->mUidCanLogin
[uid
] = 1;
626 QString shell
= user
.shell();
627 if(shell
== "/bin/false" ) //FIXME - add in any other shells it could be for false
629 d
->mUidCanLogin
[uid
] = 0;
632 d
->mUidCanLogin
[uid
] = 1;
636 QString
ProcessModelPrivate::getTooltipForUser(const KSysGuard::Process
*ps
) const
640 userTooltip
= "<qt>";
641 userTooltip
+= i18n("Login Name: %1<br/>", getUsernameForUser(ps
->uid
, true));
645 userTooltip
= i18n("This user is not recognised for some reason");
647 userTooltip
= "<qt>";
648 if(!user
.property(KUser::FullName
).isValid()) userTooltip
+= i18n("<b>%1</b><br/>", user
.property(KUser::FullName
).toString());
649 userTooltip
+= i18n("Login Name: %1 (uid: %2)<br/>", user
.loginName(), ps
->uid
);
650 if(!user
.property(KUser::RoomNumber
).isValid()) userTooltip
+= i18n(" Room Number: %1<br/>", user
.property(KUser::RoomNumber
).toString());
651 if(!user
.property(KUser::WorkPhone
).isValid()) userTooltip
+= i18n(" Work Phone: %1<br/>", user
.property(KUser::WorkPhone
).toString());
654 if( (ps
->uid
!= ps
->euid
&& ps
->euid
!= -1) ||
655 (ps
->uid
!= ps
->suid
&& ps
->suid
!= -1) ||
656 (ps
->uid
!= ps
->fsuid
&& ps
->fsuid
!= -1)) {
658 userTooltip
+= i18n("Effective User: %1<br/>", getUsernameForUser(ps
->euid
, true));
660 userTooltip
+= i18n("Setuid User: %1<br/>", getUsernameForUser(ps
->suid
, true));
662 userTooltip
+= i18n("File System User: %1<br/>", getUsernameForUser(ps
->fsuid
, true));
663 userTooltip
+= "<br/>";
666 userTooltip
+= i18n("Group: %1", getGroupnameForGroup(ps
->gid
));
667 if( (ps
->gid
!= ps
->egid
&& ps
->egid
!= -1) ||
668 (ps
->gid
!= ps
->sgid
&& ps
->sgid
!= -1) ||
669 (ps
->gid
!= ps
->fsgid
&& ps
->fsgid
!= -1)) {
671 userTooltip
+= i18n("<br/>Effective Group: %1", getGroupnameForGroup(ps
->egid
));
673 userTooltip
+= i18n("<br/>Setuid Group: %1", getGroupnameForGroup(ps
->sgid
));
675 userTooltip
+= i18n("<br/>File System Group: %1", getGroupnameForGroup(ps
->fsgid
));
681 QString
ProcessModel::getStringForProcess(KSysGuard::Process
*process
) const {
682 return i18nc("Short description of a process. PID, name, user", "<numid>%1</numid>: %2, owned by user %3", (long)(process
->pid
), process
->name
, d
->getUsernameForUser(process
->uid
, false));
685 QString
ProcessModelPrivate::getGroupnameForGroup(long long gid
) const {
687 QString groupname
= KUserGroup(gid
).name();
688 if(!groupname
.isEmpty())
689 return i18n("%1 (gid: <numid>%2</numid>)", groupname
, gid
);
691 return QString::number(gid
);
694 QString
ProcessModelPrivate::getUsernameForUser(long long uid
, bool withuid
) const {
695 QString
&username
= mUserUsername
[uid
];
696 if(username
.isNull()) {
698 username
= ""; //empty, but not null
704 username
= user
.loginName();
707 if(username
.isEmpty())
708 return QString::number(uid
);
710 return i18n("%1 (uid: %2)", username
, (long int)uid
);
714 QVariant
ProcessModel::data(const QModelIndex
&index
, int role
) const
716 //This function must be super duper ultra fast because it's called thousands of times every few second :(
717 //I think it should be optomised for role first, hence the switch statement (fastest possible case)
719 if (!index
.isValid()) {
722 if (index
.column() >= d
->mHeadings
.count()) {
727 case Qt::DisplayRole
: {
728 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
729 switch(index
.column()) {
731 return process
->name
;
733 return (qlonglong
)process
->pid
;
735 if(!process
->login
.isEmpty()) return process
->login
;
736 if(process
->uid
== process
->euid
)
737 return d
->getUsernameForUser(process
->uid
, false);
739 return d
->getUsernameForUser(process
->uid
, false) + ", " + d
->getUsernameForUser(process
->euid
, false);
740 case HeadingNiceness
:
741 return process
->niceLevel
;
744 case HeadingCPUUsage
:
747 if(d
->mShowChildTotals
&& !d
->mSimple
) total
= process
->totalUserUsage
+ process
->totalSysUsage
;
748 else total
= process
->userUsage
+ process
->sysUsage
;
749 total
= total
/ d
->mNumProcessorCores
;
751 if(total
< 1 && process
->status
!= KSysGuard::Process::Sleeping
&& process
->status
!= KSysGuard::Process::Running
)
752 return process
->translatedStatus(); //tell the user when the process is a zombie or stopped
756 return QString::number((int)(total
+0.5)) + '%';
759 if(process
->vmRSS
== 0) return QVariant(QVariant::String
);
760 if(process
->vmURSS
== -1) {
761 //If we don't have the URSS (the memory used by only the process, not the shared libraries)
762 //then return the RSS (physical memory used by the process + shared library) as the next best thing
763 return formatMemoryInfo(process
->vmRSS
);
765 return formatMemoryInfo(process
->vmURSS
);
768 if(process
->vmSize
== 0) return QVariant(QVariant::String
);
769 return formatMemoryInfo(process
->vmSize
);
770 case HeadingSharedMemory
:
771 if(process
->vmRSS
- process
->vmURSS
<= 0 || process
->vmURSS
== -1) return QVariant(QVariant::String
);
772 return formatMemoryInfo(process
->vmRSS
- process
->vmURSS
);
775 return process
->command
;
776 // It would be nice to embolden the process name in command, but this requires that the itemdelegate to support html text
777 // QString command = process->command;
778 // command.replace(process->name, "<b>" + process->name + "</b>");
779 // return "<qt>" + command;
784 if(!d
->mPidToWindowInfo
.contains(process
->pid
)) return QVariant(QVariant::String
);
785 WindowInfo w
= d
->mPidToWindowInfo
.value(process
->pid
);
786 if(!w
.netWinInfo
) return QVariant(QVariant::String
);
787 const char *name
= w
.netWinInfo
->visibleName();
788 if( !name
|| name
[0] == 0 )
789 name
= w
.netWinInfo
->name();
790 if(name
&& name
[0] != 0)
791 return QString::fromUtf8(name
);
792 return QVariant(QVariant::String
);
800 case Qt::ToolTipRole
: {
801 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
803 if(process
->tracerpid
> 0) {
804 KSysGuard::Process
*process_tracer
= d
->mProcesses
->getProcess(process
->tracerpid
);
805 if(process_tracer
) { //it is possible for this to be not the case in certain race conditions
806 KSysGuard::Process
*process_tracer
= d
->mProcesses
->getProcess(process
->tracerpid
);
807 tracer
= i18nc("tooltip. name,pid ","This process is being debugged by %1 (<numid>%2</numid>)", process_tracer
->name
, (long int)process
->tracerpid
);
810 switch(index
.column()) {
812 QString tooltip
= "<qt>";
813 /*** It would be nice to be able to show the icon in the tooltip, but Qt4 won't let us put
814 * a picture in a tooltip :(
817 if(mPidToWindowInfo.contains(process->pid)) {
819 wid = mPidToWindowInfo[process->pid].wid;
820 icon = KWindowSystem::icon(wid);
823 tooltip = i18n("<qt><table><tr><td>%1", icon);
826 if(process
->parent_pid
== 0) {
827 //Give a quick explanation of init and kthreadd
828 if(process
->name
== "init") {
829 tooltip
+= i18n("<b>Init</b> is the parent of all other processes and cannot be killed.<br/>");
830 } else if(process
->name
== "kthreadd") {
831 tooltip
+= i18n("<b>KThreadd</b> manages kernel threads. The children processes run in the kernel, controlling hard disk access etc.<br/>");
833 tooltip
+= i18nc("name column tooltip. first item is the name","<b>%1</b><br />Process ID: <numid>%2</numid>", process
->name
, (long int)process
->pid
);
836 KSysGuard::Process
*parent_process
= d
->mProcesses
->getProcess(process
->parent_pid
);
837 if(parent_process
) { //it should not be possible for this process to not exist, but check just incase
838 tooltip
= i18nc("name column tooltip. first item is the name","<b>%1</b><br />Process ID: <numid>%2</numid><br />Parent: %3<br />Parent's ID: <numid>%4</numid>", process
->name
, (long int)process
->pid
, parent_process
->name
, (long int)process
->parent_pid
);
840 tooltip
= i18nc("name column tooltip. first item is the name","<b>%1</b><br />Process ID: <numid>%2</numid><br />Parent's ID: <numid>%3</numid>", process
->name
, (long int)process
->pid
, (long int)process
->parent_pid
);
843 if(!process
->command
.isEmpty()) {
844 tooltip
+= i18n("<br/>Command: %1", process
->command
);
846 if(!process
->tty
.isEmpty())
847 tooltip
+= i18n( "<br />Running on: %1", QString(process
->tty
));
848 if(!tracer
.isEmpty())
849 return tooltip
+ "<br />" + tracer
;
853 case HeadingCommand
: {
855 i18n("<qt>This process was run with the following command:<br />%1", process
->command
);
856 if(!process
->tty
.isEmpty())
857 tooltip
+= i18n( "<br /><br />Running on: %1", QString(process
->tty
));
858 if(tracer
.isEmpty()) return tooltip
;
859 return tooltip
+ "<br />" + tracer
;
862 if(!tracer
.isEmpty())
863 return d
->getTooltipForUser(process
) + "<br />" + tracer
;
864 return d
->getTooltipForUser(process
);
866 case HeadingNiceness
: {
868 switch(process
->scheduler
) {
869 case KSysGuard::Process::Other
:
870 case KSysGuard::Process::Batch
:
871 tooltip
= i18n("<qt>Nice level: %1 (%2)", process
->niceLevel
, process
->niceLevelAsString() );
873 case KSysGuard::Process::RoundRobin
:
874 case KSysGuard::Process::Fifo
:
875 tooltip
= i18n("<qt>Scheduler priority: %1", process
->niceLevel
);
878 if(process
->scheduler
!= KSysGuard::Process::Other
)
879 tooltip
+= i18n("<br/>Scheduler: %1", process
->schedulerAsString());
881 if(process
->ioPriorityClass
!= KSysGuard::Process::None
) {
882 if((process
->ioPriorityClass
== KSysGuard::Process::RealTime
|| process
->ioPriorityClass
== KSysGuard::Process::BestEffort
) && process
->ioniceLevel
!= -1)
883 tooltip
+= i18n("<br/>I/O Nice level: %1 (%2)", process
->ioniceLevel
, process
->ioniceLevelAsString() );
884 tooltip
+= i18n("<br/>I/O Class: %1", process
->ioPriorityClassAsString() );
886 if(tracer
.isEmpty()) return tooltip
;
887 return tooltip
+ "<br />" + tracer
;
889 case HeadingCPUUsage
: {
890 QString tooltip
= ki18n("<qt>Process status: %1 %2<br />"
891 "User CPU usage: %3%<br />System CPU usage: %4%</qt>")
892 .subs(process
->translatedStatus())
893 .subs(d
->getStatusDescription(process
->status
))
894 .subs((float)(process
->userUsage
) / d
->mNumProcessorCores
)
895 .subs((float)(process
->sysUsage
) / d
->mNumProcessorCores
)
898 if(process
->numChildren
> 0) {
899 tooltip
+= ki18n("<br />Number of children: %1<br />Total User CPU usage: %2%<br />"
900 "Total System CPU usage: %3%<br />Total CPU usage: %4%")
901 .subs(process
->numChildren
)
902 .subs((float)(process
->totalUserUsage
)/ d
->mNumProcessorCores
)
903 .subs((float)(process
->totalSysUsage
) / d
->mNumProcessorCores
)
904 .subs((float)(process
->totalUserUsage
+ process
->totalSysUsage
) / d
->mNumProcessorCores
)
907 if(process
->userTime
> 0)
908 tooltip
+= ki18n("<br /><br />CPU time spent running as user: %1 seconds")
909 .subs(process
->userTime
/ 100.0, 0, 'f', 1)
911 if(process
->sysTime
> 0)
912 tooltip
+= ki18n("<br />CPU time spent running in kernel: %1 seconds")
913 .subs(process
->sysTime
/ 100.0, 0, 'f', 1)
915 if(process
->niceLevel
!= 0)
916 tooltip
+= i18n("<br />Nice level: %1 (%2)", process
->niceLevel
, process
->niceLevelAsString() );
917 if(process
->ioPriorityClass
!= KSysGuard::Process::None
) {
918 if((process
->ioPriorityClass
== KSysGuard::Process::RealTime
|| process
->ioPriorityClass
== KSysGuard::Process::BestEffort
) && process
->ioniceLevel
!= -1)
919 tooltip
+= i18n("<br/>I/O Nice level: %1 (%2)", process
->ioniceLevel
, process
->ioniceLevelAsString() );
920 tooltip
+= i18n("<br/>I/O Class: %1", process
->ioPriorityClassAsString() );
923 if(!tracer
.isEmpty())
924 return tooltip
+ "<br />" + tracer
;
927 case HeadingVmSize
: {
930 case HeadingMemory
: {
931 QString tooltip
= "<qt>";
932 if(process
->vmURSS
!= -1) {
933 //We don't have information about the URSS, so just fallback to RSS
935 tooltip
+= i18n("Memory usage: %1 out of %2 (%3 %)<br />", KGlobal::locale()->formatByteSize(process
->vmURSS
* 1024), KGlobal::locale()->formatByteSize(d
->mMemTotal
*1024), process
->vmURSS
*100/d
->mMemTotal
);
937 tooltip
+= i18n("Memory usage: %1<br />", KGlobal::locale()->formatByteSize(process
->vmURSS
* 1024));
940 tooltip
+= i18n("RSS Memory usage: %1 out of %2 (%3 %)", KGlobal::locale()->formatByteSize(process
->vmRSS
* 1024), KGlobal::locale()->formatByteSize(d
->mMemTotal
*1024), process
->vmRSS
*100/d
->mMemTotal
);
942 tooltip
+= i18n("RSS Memory usage: %1", KGlobal::locale()->formatByteSize(process
->vmRSS
* 1024));
945 case HeadingSharedMemory
: {
946 if(process
->vmURSS
== -1)
947 return i18n("<qt>Your system does not seem to have this information for us to read, sorry.</qt>");
948 QString tooltip
= "<qt>";
950 tooltip
+= i18n("Shared library memory usage: %1 out of %2 (%3 %)", KGlobal::locale()->formatByteSize((process
->vmRSS
- process
->vmURSS
) * 1024), KGlobal::locale()->formatByteSize(d
->mMemTotal
*1024), (process
->vmRSS
-process
->vmURSS
)*100/d
->mMemTotal
);
952 tooltip
+= i18n("Shared library memory usage: %1", KGlobal::locale()->formatByteSize((process
->vmRSS
- process
->vmURSS
) * 1024));
956 case HeadingXTitle
: {
958 QList
<WindowInfo
> values
= d
->mPidToWindowInfo
.values(process
->pid
);
959 if(values
.isEmpty()) return QVariant(QVariant::String
);
960 for(int i
= 0; i
< values
.size(); i
++) {
961 WindowInfo w
= values
[i
];
963 const char *name
= w
.netWinInfo
->visibleName();
964 if( !name
|| name
[0] == 0 )
965 name
= w
.netWinInfo
->name();
966 if(name
&& name
[0] != 0) {
967 if( i
==0 && values
.size()==1)
968 return QString::fromUtf8(name
);
969 tooltip
+= "<li>" + QString::fromUtf8(name
) + "</li>";
973 if(!tooltip
.isEmpty())
974 return "<qt><ul>" + tooltip
+ "</ul>";
975 return QVariant(QVariant::String
);
979 return QVariant(QVariant::String
);
982 case Qt::TextAlignmentRole
:
983 switch(index
.column() ) {
985 case HeadingCPUUsage
:
986 return QVariant(Qt::AlignCenter
);
989 case HeadingSharedMemory
:
991 return QVariant(Qt::AlignRight
);
995 if(index
.column() != 0) return QVariant(); //If we query with this role, then we want the raw UID for this.
996 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
999 case SortingValueRole
: {
1000 //We have a special understanding with the filter sort. This returns an int (in a qvariant) that can be sorted by
1001 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
1003 switch(index
.column()) {
1005 //Sorting by user will be the default and the most common.
1006 //We want to sort in the most useful way that we can. We need to return a number though.
1007 //This code is based on that sorting ascendingly should put the current user at the top
1009 //First the user we are running as should be at the top. We add 0 for this
1010 //Then any other users in the system. We add 100,000,000 for this (remember it's ascendingly sorted)
1011 //Then at the bottom the 'system' processes. We add 200,000,000 for this
1013 //We subtract the uid to sort ascendingly by that, then subtract the cpu usage to sort by that, then finally subtract the memory
1016 long long memory
= 0;
1017 if(process
->vmURSS
!= -1) memory
= process
->vmURSS
;
1018 else memory
= process
->vmRSS
;
1019 if(d
->mIsLocalhost
&& process
->uid
== getuid())
1020 base
= 0; //own user
1021 else if(process
->uid
< 100 || !canUserLogin(process
->uid
))
1022 base
= 200000000 - process
->uid
* 10000; //system user
1024 base
= 100000000 - process
->uid
* 10000;
1025 //One special exception is a traced process since that's probably important. We should put that at the top
1026 if(process
->tracerpid
>0) return base
- 9999 ;
1028 if(d
->mSimple
|| !d
->mShowChildTotals
)
1029 cpu
= process
->userUsage
+ process
->sysUsage
;
1031 cpu
= process
->totalUserUsage
+ process
->totalSysUsage
;
1032 if(cpu
== 0 && process
->status
!= KSysGuard::Process::Running
&& process
->status
!= KSysGuard::Process::Sleeping
)
1033 cpu
= 1; //stopped or zombied processes should be near the top of the list
1034 bool hasWindow
= d
->mPidToWindowInfo
.contains(process
->pid
);
1035 //However we can of course have lots of processes with the same user. Next we sort by CPU.
1037 return (double)(base
- (cpu
*100) -(hasWindow
?50:0) - memory
*100.0/d
->mMemTotal
);
1039 return (double)(base
- (cpu
*100) -(hasWindow
?50:0));
1041 case HeadingCPUUsage
: {
1043 if(d
->mSimple
|| !d
->mShowChildTotals
)
1044 cpu
= process
->userUsage
+ process
->sysUsage
;
1046 cpu
= process
->totalUserUsage
+ process
->totalSysUsage
;
1047 if(cpu
== 0 && process
->status
!= KSysGuard::Process::Running
&& process
->status
!= KSysGuard::Process::Sleeping
)
1048 cpu
= 1; //stopped or zombied processes should be near the top of the list
1052 if(process
->vmURSS
== -1)
1053 return (long long)-process
->vmRSS
;
1055 return (long long)-process
->vmURSS
;
1057 return (long long)-process
->vmSize
;
1058 case HeadingSharedMemory
:
1059 if(process
->vmURSS
== -1) return (long long)0;
1060 return (long long)-(process
->vmRSS
- process
->vmURSS
);
1065 case WindowIdRole
: {
1066 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
1067 if(!d
->mPidToWindowInfo
.contains(process
->pid
)) return QVariant();
1068 WindowInfo w
= d
->mPidToWindowInfo
.value(process
->pid
);
1072 case TotalMemoryRole
: {
1073 return d
->mMemTotal
;
1075 case NumberOfProcessorsRole
: {
1076 return d
->mNumProcessorCores
;
1078 case Qt::DecorationRole
: {
1079 if(index
.column() == HeadingName
) {
1080 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
1081 if(!d
->mPidToWindowInfo
.contains(process
->pid
)) {
1082 if(d
->mSimple
) //When not in tree mode, we need to pad the name column where we do not have an icon
1083 return QIcon(d
->mBlankPixmap
);
1084 else //When in tree mode, the padding looks pad, so do not pad in this case
1088 WindowInfo w
= d
->mPidToWindowInfo
.value(process
->pid
);
1090 return QIcon(d
->mBlankPixmap
);
1093 } else if (index
.column() == HeadingCPUUsage
) {
1094 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
1095 if(process
->status
== KSysGuard::Process::Stopped
|| process
->status
== KSysGuard::Process::Zombie
) {
1096 QPixmap pix
= KIconLoader::global()->loadIcon("button_cancel", KIconLoader::Small
,
1097 KIconLoader::SizeSmall
, KIconLoader::DefaultState
, QStringList(),
1104 case Qt::BackgroundRole
: {
1105 if(index
.column() != HeadingUser
) return QVariant();
1106 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
1107 if(process
->tracerpid
>0) {
1108 //It's being debugged, so probably important. Let's mark it as such
1109 return QColor("yellow");
1111 if(d
->mIsLocalhost
&& process
->uid
== getuid()) { //own user
1112 return QColor(0, 208, 214, 50);
1114 if(process
->uid
< 100 || !canUserLogin(process
->uid
))
1115 return QColor(218, 220,215, 50); //no color for system tasks
1117 return QColor(2, 154, 54, 50);
1119 case Qt::FontRole
: {
1120 if(index
.column() == HeadingCPUUsage
) {
1121 KSysGuard::Process
*process
= reinterpret_cast< KSysGuard::Process
* > (index
.internalPointer());
1122 if(process
->userUsage
== 0) {
1124 font
.setItalic(true);
1130 default: //This is a very very common case, so the route to this must be very minimal
1134 return QVariant(); //never get here, but make compilier happy
1137 bool ProcessModel::hasGUIWindow(long long pid
) const
1139 return d
->mPidToWindowInfo
.contains(pid
);
1142 bool ProcessModel::isLocalhost() const
1144 return d
->mIsLocalhost
;
1148 void ProcessModel::setupHeader() {
1149 QStringList headings
;
1150 headings
<< i18nc("process heading", "Name");
1151 headings
<< i18nc("process heading", "User Name");
1152 headings
<< i18nc("process heading", "Pid");
1153 headings
<< i18nc("process heading", "Tty");
1154 headings
<< i18nc("process heading", "Niceness");
1155 // xgettext: no-c-format
1156 headings
<< i18nc("process heading", "CPU %");
1157 headings
<< i18nc("process heading", "Virtual Size");
1158 headings
<< i18nc("process heading", "Memory");
1159 headings
<< i18nc("process heading", "Shared Mem");
1160 headings
<< i18nc("process heading", "Command");
1162 headings
<< i18nc("process heading", "Window Title");
1165 if(d
->mHeadings
.isEmpty()) { // If it's empty, this is the first time this has been called, so insert the headings
1166 beginInsertColumns(QModelIndex(), 0, headings
.count()-1);
1167 d
->mHeadings
= headings
;
1170 // This was called to retranslate the headings. Just use the new translations and call headerDataChanged
1171 Q_ASSERT(d
->mHeadings
.count() == headings
.count());
1172 d
->mHeadings
= headings
;
1173 headerDataChanged(Qt::Horizontal
, 0 , headings
.count()-1);
1178 void ProcessModel::retranslateUi()
1183 KSysGuard::Process
*ProcessModel::getProcess(long long pid
) {
1184 return d
->mProcesses
->getProcess(pid
);
1187 bool ProcessModel::showTotals() const {
1188 return d
->mShowChildTotals
;
1191 void ProcessModel::setShowTotals(bool showTotals
) //slot
1193 if(showTotals
== d
->mShowChildTotals
) return;
1194 d
->mShowChildTotals
= showTotals
;
1197 foreach( KSysGuard::Process
*process
, d
->mProcesses
->getAllProcesses()) {
1198 if(process
->numChildren
> 0) {
1201 row
= process
->index
;
1203 row
= process
->parent
->children
.indexOf(process
);
1204 index
= createIndex(row
, HeadingCPUUsage
, process
);
1205 emit
dataChanged(index
, index
);
1210 long long ProcessModel::totalMemory() const
1212 return d
->mMemTotal
;
1214 void ProcessModel::setUnits(Units units
)
1218 ProcessModel::Units
ProcessModel::units() const
1220 return (Units
) d
->mUnits
;
1223 QString
ProcessModel::formatMemoryInfo(long amountInKB
) const
1227 return i18n("%1 k", amountInKB
);
1229 return i18n("%1 m", (amountInKB
+512)/1024); //Round to nearest megabyte
1231 return i18n("%1 g", (amountInKB
+512*1024)/(1024*1024)); //Round to nearest gigabyte
1236 QString
ProcessModel::hostName() const {
1237 return d
->mHostName
;