2
KSysGuard, the KDE System Guard
4
Copyright (c) 2006 - 2008 John Tapsell <john.tapsell@kde.org>
5
Copyright (c) 1999 - 2001 Chris Schlaeger <cs@kde.org>
7
This program is free software; you can redistribute it and/or
8
modify it under the terms of the GNU General Public
9
License version 2 or at your option version 3 as published by
10
the Free Software Foundation.
12
This program 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
15
GNU General Public License for more details.
17
You should have received a copy of the GNU General Public License
18
along with this program; if not, write to the Free Software
19
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
KSysGuard has been written with some source code and ideas from
22
ktop (<1.0). Early versions of ktop have been written by Bernd
23
Johannes Wuebben <wuebben@math.cornell.edu> and Nicolas Leclercq
24
<nicknet@planete.net>.
35
#include <kaboutdata.h>
37
#include <kactioncollection.h>
38
#include <kapplication.h>
39
#include <kcmdlineargs.h>
41
#include <kedittoolbar.h>
43
#include <kglobalsettings.h>
46
#include <kmessagebox.h>
47
#include <ksgrd/SensorAgent.h>
48
#include <ksgrd/SensorManager.h>
49
#include <kstatusbar.h>
50
#include <kstandardaction.h>
51
#include <ktoggleaction.h>
53
#include <kwindowsystem.h>
56
#include <kdeversion.h>
57
#include "SensorBrowser.h"
58
#include "Workspace.h"
59
#include "WorkSheet.h"
60
#include "StyleEngine.h"
61
#include "HostConnector.h"
62
#include "ProcessController.h"
63
#include "ProcessTable.h"
64
#include "processui/ksysguardprocesslist.h"
66
#include "ksysguard.h"
69
ProcessController *sLocalProcessController = NULL;
71
//Comment out to stop ksysguard from forking. Good for debugging
72
//#define FORK_KSYSGUARD
74
static const char Description[] = I18N_NOOP( "KDE System Monitor" );
78
: KXmlGuiWindow( NULL, Qt::WindowFlags(KDE_DEFAULT_WINDOWFLAGS) | Qt::WindowContextHelpButtonHint)
80
QDBusConnection::sessionBus().registerObject("/", this, QDBusConnection::ExportScriptableSlots);
83
mSplitter = new QSplitter( this );
84
mSplitter->setOrientation( Qt::Horizontal );
85
mSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
86
setCentralWidget( mSplitter );
90
mWorkSpace = new Workspace( mSplitter );
91
connect( mWorkSpace, SIGNAL( setCaption( const QString&) ),
92
SLOT( setCaption( const QString&) ) );
93
connect( mWorkSpace, SIGNAL( currentChanged( int ) ),
94
SLOT( currentTabChanged( int ) ) );
96
sLocalProcessController = new ProcessController( this, NULL);
97
connect( sLocalProcessController, SIGNAL( processListChanged() ), this, SLOT( updateProcessCount()));
99
/* Create the status bar. It displays some information about the
100
* number of processes and the memory consumption of the local
102
const int STATUSBAR_STRETCH=1;
104
sbProcessCount = new QLabel();
105
statusBar()->addWidget( sbProcessCount, STATUSBAR_STRETCH );
107
sbCpuStat = new QLabel();
108
statusBar()->addWidget( sbCpuStat, STATUSBAR_STRETCH );
110
sbMemTotal = new QLabel();
111
statusBar()->addWidget( sbMemTotal, STATUSBAR_STRETCH );
113
sbSwapTotal = new QLabel();
114
statusBar()->addWidget( sbSwapTotal, STATUSBAR_STRETCH );
118
// create actions for menu entries
119
mRefreshTabAction = KStandardAction::redisplay(mWorkSpace,SLOT( refreshActiveWorksheet() ),actionCollection());
120
mNewWorksheetAction = actionCollection()->addAction("new_worksheet");
121
mNewWorksheetAction->setIcon(KIcon("tab-new"));
122
connect(mNewWorksheetAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( newWorkSheet() ));
123
mInsertWorksheetAction = actionCollection()->addAction("import_worksheet");
124
mInsertWorksheetAction->setIcon(KIcon("document-open") );
125
connect(mInsertWorksheetAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( importWorkSheet() ));
126
mTabExportAction = actionCollection()->addAction( "export_worksheet" );
127
mTabExportAction->setIcon( KIcon("document-save-as") );
128
connect(mTabExportAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( exportWorkSheet() ));
129
mTabRemoveAction = actionCollection()->addAction( "remove_worksheet" );
130
mTabRemoveAction->setIcon( KIcon("tab-close") );
131
connect(mTabRemoveAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( removeWorkSheet() ));
132
mMonitorRemoteAction = actionCollection()->addAction( "connect_host" );
133
mMonitorRemoteAction->setIcon( KIcon("network-connect") );
134
connect(mMonitorRemoteAction, SIGNAL(triggered(bool)), SLOT( connectHost() ));
136
mHotNewWorksheetAction = actionCollection()->addAction( "get_new_worksheet" );
137
mHotNewWorksheetAction->setIcon( KIcon("network-server") );
138
connect(mHotNewWorksheetAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( getHotNewWorksheet() ));
139
mHotNewWorksheetUploadAction = actionCollection()->addAction( "upload_worksheet" );
140
mHotNewWorksheetUploadAction->setIcon( KIcon("network-server") );
141
connect(mHotNewWorksheetUploadAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( uploadHotNewWorksheet() ));
145
mConfigureSheetAction = actionCollection()->addAction( "configure_sheet" );
146
mConfigureSheetAction->setIcon( KIcon("configure") );
147
connect(mConfigureSheetAction, SIGNAL(triggered(bool)), SLOT( configureCurrentSheet() ));
152
void TopLevel::retranslateUi()
154
setPlainCaption( i18n( "System Monitor" ) );
155
mRefreshTabAction->setText(i18n("&Refresh Tab"));
156
mNewWorksheetAction->setText(i18n( "&New Tab..." ));
157
mInsertWorksheetAction->setText(i18n( "Import Tab Fr&om File..." ));
158
mTabExportAction->setText( i18n( "Save Tab &As..." ) );
159
mTabRemoveAction->setText( i18n( "&Close Tab" ) );
160
mMonitorRemoteAction->setText( i18n( "Monitor &Remote Machine..." ) );
161
mHotNewWorksheetAction->setText( i18n( "&Download New Tabs..." ) );
162
mHotNewWorksheetUploadAction->setText( i18n( "&Upload Current Tab..." ) );
164
mConfigureSheetAction->setText( i18n( "Tab &Properties" ) );
166
KAction *tmpQuitAction = KStandardAction::quit( NULL, NULL, NULL );
167
mQuitAction->setText(tmpQuitAction->text());
168
mQuitAction->setWhatsThis(tmpQuitAction->whatsThis());
169
mQuitAction->setToolTip(tmpQuitAction->toolTip());
170
delete tmpQuitAction;
172
mQuitAction = KStandardAction::quit( this, SLOT( close() ), actionCollection() );
175
void TopLevel::configureCurrentSheet() {
176
mWorkSpace->configure();
177
mRefreshTabAction->setVisible( mWorkSpace->currentWorkSheet()->updateInterval() == 0 );
179
void TopLevel::currentTabChanged(int index)
181
QWidget *wdg = mWorkSpace->widget(index);
182
WorkSheet *sheet = (WorkSheet *)(wdg);
184
bool locked = !sheet || sheet->isLocked();
185
mTabRemoveAction->setVisible(!locked);
186
mTabExportAction->setVisible(!locked);
187
mHotNewWorksheetUploadAction->setVisible(!locked);
188
mMonitorRemoteAction->setVisible(!locked);
190
//only show refresh option is update interval is 0 (manual)
191
mRefreshTabAction->setVisible( sheet->updateInterval() == 0 );
193
if(!locked && !mSensorBrowser) {
194
startSensorBrowserWidget();
197
if(mSensorBrowser->isVisible() && locked) //going from visible to not visible to save the state
198
mSplitterSize = mSplitter->sizes();
199
mSensorBrowser->setVisible(!locked);
203
void TopLevel::startSensorBrowserWidget()
205
if(mSensorBrowser) return;
206
mSensorBrowser = new SensorBrowserWidget( 0, KSGRD::SensorMgr );
207
mSplitter->insertWidget(2,mSensorBrowser);
208
mSplitter->setSizes( mSplitterSize );
212
* DBUS Interface functions
215
void TopLevel::showOnCurrentDesktop()
217
KWindowSystem::setOnDesktop( winId(), KWindowSystem::currentDesktop() );
218
kapp->updateUserTimestamp();
219
KWindowSystem::forceActiveWindow( winId() );
222
void TopLevel::importWorkSheet( const QString &fileName )
224
mWorkSpace->importWorkSheet( KUrl( fileName ) );
227
void TopLevel::removeWorkSheet( const QString &fileName )
229
mWorkSpace->removeWorkSheet( fileName );
232
void TopLevel::getHotNewWorksheet()
234
mWorkSpace->getHotNewWorksheet( );
237
QStringList TopLevel::listSensors( const QString &hostName )
239
if(!mSensorBrowser) {
240
setUpdatesEnabled(false);
241
startSensorBrowserWidget();
242
mSensorBrowser->setVisible(false);
243
setUpdatesEnabled(true);
245
return mSensorBrowser->listSensors( hostName );
248
QStringList TopLevel::listHosts()
250
if(!mSensorBrowser) {
251
setUpdatesEnabled(false);
252
startSensorBrowserWidget();
253
mSensorBrowser->setVisible(false);
254
setUpdatesEnabled(true);
256
return mSensorBrowser->listHosts();
259
void TopLevel::initStatusBar()
261
KSGRD::SensorMgr->engage( "localhost", "", "ksysguardd" );
262
/* Request info about the swap space size and the units it is
263
* measured in. The requested info will be received by
264
* answerReceived(). */
265
KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/used?",
266
(KSGRD::SensorClient*)this, 7 );
268
KToggleAction *sb = dynamic_cast<KToggleAction*>(action("options_show_statusbar"));
270
connect(sb, SIGNAL(toggled(bool)), this, SLOT(updateStatusBar()));
271
setupGUI(QSize(800,600), ToolBar | Keys | StatusBar | Save | Create);
276
void TopLevel::updateStatusBar()
278
if ( mTimerId == -1 )
279
mTimerId = startTimer( 2000 );
281
// call timerEvent to fill the status bar with real values
285
void TopLevel::connectHost()
287
HostConnector hostConnector( this );
289
// hostConnector.setHostNames( mHostList );
290
// hostConnector.setCommands( mCommandList );
292
// hostConnector.setCurrentHostName( "" );
294
if ( !hostConnector.exec() )
297
// mHostList = hostConnector.hostNames();
298
// mCommandList = hostConnector.commands();
301
QString command = "";
304
/* Check which radio button is selected and set parameters
306
if ( hostConnector.useSsh() )
308
else if ( hostConnector.useRsh() )
310
else if ( hostConnector.useDaemon() )
311
port = hostConnector.port();
313
command = hostConnector.currentCommand();
315
KSGRD::SensorMgr->engage( hostConnector.currentHostName(), shell, command, port );
318
void TopLevel::disconnectHost()
321
mSensorBrowser->disconnect();
324
bool TopLevel::event( QEvent *e )
326
if ( e->type() == QEvent::User ) {
327
/* Due to the asynchronous communication between ksysguard and its
328
* back-ends, we sometimes need to show message boxes that were
329
* triggered by objects that have died already. */
330
KMessageBox::error( this, static_cast<KSGRD::SensorManager::MessageEvent*>(e)->message() );
335
return KXmlGuiWindow::event( e );
338
void TopLevel::timerEvent( QTimerEvent* )
340
if ( statusBar()->isVisibleTo( this ) ) {
341
/* Request some info about the memory status. The requested
342
* information will be received by answerReceived(). */
343
KSGRD::SensorMgr->sendRequest( "localhost", "cpu/idle",
344
(KSGRD::SensorClient*)this, 1 );
345
KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/free",
346
(KSGRD::SensorClient*)this, 2 );
347
KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/used",
348
(KSGRD::SensorClient*)this, 3 );
349
KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/application",
350
(KSGRD::SensorClient*)this, 4 );
351
KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/free",
352
(KSGRD::SensorClient*)this, 5 );
353
KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/used",
354
(KSGRD::SensorClient*)this, 6 );
358
void TopLevel::updateProcessCount() {
359
QString s = i18np( "1 process" "\xc2\x9c" "1", "%1 processes" "\xc2\x9c" "%1", sLocalProcessController->processList()->visibleProcessesCount() );
360
sbProcessCount->setText( s );
362
void TopLevel::changeEvent( QEvent * event )
364
if (event->type() == QEvent::LanguageChange) {
365
KSGRD::SensorMgr->retranslate();
366
setUpdatesEnabled(false);
367
setupGUI(ToolBar | Keys | StatusBar | Create);
369
setUpdatesEnabled(true);
371
KXmlGuiWindow::changeEvent(event);
374
bool TopLevel::queryClose()
376
if ( !mWorkSpace->saveOnQuit() )
379
KConfigGroup cg( KGlobal::config(), "MainWindow" );
380
saveProperties( cg );
381
KGlobal::config()->sync();
386
void TopLevel::readProperties( const KConfigGroup& cfg )
389
/* we can ignore 'isMaximized' because we can't set the window
390
maximized, so we save the coordinates instead */
391
// if ( cfg.readEntry( "isMinimized" , false) == true )
394
mSplitterSize = cfg.readEntry( "SplitterSizeList",QList<int>() );
395
if ( mSplitterSize.isEmpty() ) {
396
// start with a 30/70 ratio
397
mSplitterSize.append( 10 );
398
mSplitterSize.append( 90 );
401
KSGRD::SensorMgr->readProperties( cfg );
402
KSGRD::Style->readProperties( cfg );
404
mWorkSpace->readProperties( cfg );
406
QList<WorkSheet *> workSheets = mWorkSpace->getWorkSheets();
407
for(int i = 0; i < sLocalProcessController->actions().size(); i++) {
408
actionCollection()->addAction("processAction" + QString::number(i), sLocalProcessController->actions().at(i));
412
void TopLevel::saveProperties( KConfigGroup& cfg )
414
cfg.writeEntry( "isMinimized", isMinimized() );
416
if(mSensorBrowser && mSensorBrowser->isVisible())
417
cfg.writeEntry( "SplitterSizeList", mSplitter->sizes());
418
else if(mSplitterSize.size() == 2 && mSplitterSize.value(0) != 0 && mSplitterSize.value(1) != 0)
419
cfg.writeEntry( "SplitterSizeList", mSplitterSize );
421
KSGRD::Style->saveProperties( cfg );
422
KSGRD::SensorMgr->saveProperties( cfg );
424
saveMainWindowSettings( cfg );
425
mWorkSpace->saveProperties( cfg );
428
void TopLevel::answerReceived( int id, const QList<QByteArray> &answerList )
430
// we have received an answer from the daemon.
432
if(!answerList.isEmpty()) answer = answerList[0];
435
static qlonglong mFree = 0;
436
static qlonglong mUsedApplication = 0;
437
static qlonglong mUsedTotal = 0;
438
static qlonglong sUsed = 0;
439
static qlonglong sFree = 0;
443
s = i18n( "CPU: %1%\xc2\x9c%1%", (int) (100 - answer.toFloat()) );
444
sbCpuStat->setText( s );
448
mFree = answer.toLongLong();
452
mUsedTotal = answer.toLongLong();
456
mUsedApplication = answer.toLongLong();
457
//Use a multi-length string
458
s = i18nc( "Arguments are formatted byte sizes (used/total)", "Memory: %1 / %2" "\xc2\x9c" "Mem: %1 / %2" "\xc2\x9c" "Mem: %1" "\xc2\x9c" "%1",
459
KGlobal::locale()->formatByteSize( mUsedApplication*1024),
460
KGlobal::locale()->formatByteSize( (mFree+mUsedTotal)*1024 ) );
461
sbMemTotal->setText( s );
465
sFree = answer.toLongLong();
469
sUsed = answer.toLongLong();
470
setSwapInfo( sUsed, sFree, unit );
474
KSGRD::SensorIntegerInfo info( answer );
475
unit = KSGRD::SensorMgr->translateUnit( info.unit() );
481
void TopLevel::setSwapInfo( qlonglong used, qlonglong free, const QString & )
484
if ( used == 0 && free == 0 ) // no swap available
485
msg = i18n( " No swap space available " );
487
msg = i18nc( "Arguments are formatted byte sizes (used/total)", "Swap: %1 / %2" "\xc2\x9c" "Swap: %1" "\xc2\x9c" "%1" ,
488
KGlobal::locale()->formatByteSize( used*1024 ),
489
KGlobal::locale()->formatByteSize( (free+used)*1024) );
492
sbSwapTotal->setText( msg );
496
* Once upon a time...
498
extern "C" KDE_EXPORT int kdemain( int argc, char** argv )
500
// initpipe is used to keep the parent process around till the child
501
// has registered with dbus
502
#ifdef FORK_KSYSGUARD
505
/* This forking will put ksysguard in it's own session not having a
506
* controlling terminal attached to it. This prevents ssh from
507
* using this terminal for password requests. Thus, you
508
* need a ssh with ssh-askpass support to popup an X dialog to
509
* enter the password. */
511
if ( ( pid = fork() ) < 0 )
515
close( initpipe[ 1 ] );
517
// wait till init is complete
519
while( read( initpipe[ 0 ], &c, 1 ) < 0 );
522
close( initpipe[ 0 ] );
526
close( initpipe[ 0 ] );
530
KAboutData aboutData( "ksysguard", 0, ki18n( "System Monitor" ),
531
KDE_VERSION_STRING, ki18n(Description), KAboutData::License_GPL,
532
ki18n( "(c) 1996-2008 The KDE System Monitor Developers" ) );
533
aboutData.addAuthor( ki18n("John Tapsell"), ki18n("Current Maintainer"), "john.tapsell@kde.org" );
534
aboutData.addAuthor( ki18n("Chris Schlaeger"), ki18n("Previous Maintainer"), "cs@kde.org" );
535
aboutData.addAuthor( ki18n("Greg Martyn"), KLocalizedString(), "greg.martyn@gmail.com" );
536
aboutData.addAuthor( ki18n("Tobias Koenig"), KLocalizedString(), "tokoe@kde.org" );
537
aboutData.addAuthor( ki18n("Nicolas Leclercq"), KLocalizedString(), "nicknet@planete.net" );
538
aboutData.addAuthor( ki18n("Alex Sanda"), KLocalizedString(), "alex@darkstart.ping.at" );
539
aboutData.addAuthor( ki18n("Bernd Johannes Wuebben"), KLocalizedString(), "wuebben@math.cornell.edu" );
540
aboutData.addAuthor( ki18n("Ralf Mueller"), KLocalizedString(), "rlaf@bj-ig.de" );
541
aboutData.addAuthor( ki18n("Hamish Rodda"), KLocalizedString(), "rodda@kde.org" );
542
aboutData.addAuthor( ki18n("Torsten Kasch"), ki18n( "Solaris Support\n"
543
"Parts derived (by permission) from the sunos5\n"
544
"module of William LeFebvre's \"top\" utility." ),
545
"tk@Genetik.Uni-Bielefeld.DE" );
546
aboutData.setProgramIconName("utilities-system-monitor");
548
KCmdLineArgs::init( argc, argv, &aboutData );
550
KCmdLineOptions options;
551
options.add("+[worksheet]", ki18n( "Optional worksheet files to load" ));
552
KCmdLineArgs::addCmdLineOptions( options );
553
// initialize KDE application
554
KApplication *app = new KApplication;
556
KSGRD::SensorMgr = new KSGRD::SensorManager();
557
KSGRD::Style = new KSGRD::StyleEngine();
559
#ifdef FORK_KSYSGUARD
561
write( initpipe[ 1 ], &c, 1 );
562
close( initpipe[ 1 ] );
564
topLevel = new TopLevel();
567
// create top-level widget
568
topLevel->readProperties( KConfigGroup( KGlobal::config(), "MainWindow" ) );
569
// setup the statusbar, toolbar etc.
570
// Note that this comes after creating the top-level widgets whcih also
571
// sets up the various QActions that the user may have added to the toolbar
572
topLevel->initStatusBar();
574
//There seems to be some serious bugs with the session restore code. Disabling
575
// if ( app->isSessionRestored() )
576
// topLevel->restore( 1 );
579
KSGRD::SensorMgr->setBroadcaster( topLevel ); // SensorMgr uses a QPointer for toplevel, so it is okay if topLevel is deleted first
581
// run the application
582
int result = app->exec();
585
delete KSGRD::SensorMgr;
591
#include "ksysguard.moc"