2
KSysGuard, the KDE System Guard
4
Copyright (c) 2006 John Tapsell <john.tapsell@kdemail.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 version 2 of the GNU General Public
9
License as published by the Free Software Foundation.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
KSysGuard has been written with some source code and ideas from
21
ktop (<1.0). Early versions of ktop have been written by Bernd
22
Johannes Wuebben <wuebben@math.cornell.edu> and Nicolas Leclercq
23
<nicknet@planete.net>.
34
#include <kaboutdata.h>
36
#include <kactioncollection.h>
37
#include <kapplication.h>
38
#include <kcmdlineargs.h>
40
#include <kedittoolbar.h>
42
#include <kglobalsettings.h>
45
#include <kmessagebox.h>
46
#include <ksgrd/SensorAgent.h>
47
#include <ksgrd/SensorManager.h>
48
#include <kstandarddirs.h>
49
#include <kstatusbar.h>
50
#include <kstandardaction.h>
51
#include <ktoggleaction.h>
54
#include <kwindowsystem.h>
57
#include "../version.h"
58
#include "SensorBrowser.h"
59
#include "Workspace.h"
60
#include "WorkSheet.h"
61
#include "StyleEngine.h"
62
#include "HostConnector.h"
64
#include "ksysguard.h"
68
//Comment out to stop ksysguard from forking. Good for debugging
69
//#define FORK_KSYSGUARD
71
static const char Description[] = I18N_NOOP( "KDE System Monitor" );
75
This is the constructor for the main widget. It sets up the menu and the
81
QDBusConnection::sessionBus().registerObject("/", this, QDBusConnection::ExportScriptableSlots);
82
setPlainCaption( i18n( "System Monitor" ) );
85
mSplitter = new QSplitter( this );
86
mSplitter->setOrientation( Qt::Horizontal );
87
mSplitter->setOpaqueResize( KGlobalSettings::opaqueResize() );
88
setCentralWidget( mSplitter );
92
mWorkSpace = new Workspace( mSplitter );
93
connect( mWorkSpace, SIGNAL( setCaption( const QString&) ),
94
SLOT( setCaption( const QString&) ) );
95
connect( mWorkSpace, SIGNAL( currentChanged( int ) ),
96
SLOT( currentTabChanged( int ) ) );
98
/* Create the status bar. It displays some information about the
99
* number of processes and the memory consumption of the local
101
const int STATUSBAR_STRETCH=1;
102
statusBar()->insertItem( i18n( "Loading Processes Count.." ), 0, STATUSBAR_STRETCH );
103
statusBar()->insertItem( i18n( "Loading CPU Stat.." ), 1, STATUSBAR_STRETCH );
104
statusBar()->insertItem( i18n( "Loading Memory Totals.." ), 2, STATUSBAR_STRETCH );
105
statusBar()->insertItem( i18n( "Loading Swap Totals.." ), 3, STATUSBAR_STRETCH);
108
// create actions for menu entries
109
QAction *action = actionCollection()->addAction("new_worksheet");
110
action->setIcon(KIcon("tab-new"));
111
action->setText(i18n( "&New Worksheet..." ));
112
connect(action, SIGNAL(triggered(bool)), mWorkSpace, SLOT( newWorkSheet() ));
113
action = actionCollection()->addAction("import_worksheet");
114
action->setIcon(KIcon("document-open") );
115
action->setText(i18n( "Import Worksheet..." ));
116
connect(action, SIGNAL(triggered(bool)), mWorkSpace, SLOT( importWorkSheet() ));
117
mTabRemoveAction = actionCollection()->addAction( "remove_worksheet" );
118
mTabRemoveAction->setIcon( KIcon("tab-remove") );
119
mTabRemoveAction->setText( i18n( "&Remove Worksheet" ) );
120
connect(mTabRemoveAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( removeWorkSheet() ));
121
mTabExportAction = actionCollection()->addAction( "export_worksheet" );
122
mTabExportAction->setIcon( KIcon("document-save-as") );
123
mTabExportAction->setText( i18n( "&Export Worksheet..." ) );
124
connect(mTabExportAction, SIGNAL(triggered(bool)), mWorkSpace, SLOT( exportWorkSheet() ));
126
KStandardAction::quit( this, SLOT( close() ), actionCollection() );
128
mMonitorRemoteAction = actionCollection()->addAction( "connect_host" );
129
mMonitorRemoteAction->setIcon( KIcon("connection-established") );
130
mMonitorRemoteAction->setText( i18n( "Monitor remote machine..." ) );
131
connect(mMonitorRemoteAction, SIGNAL(triggered(bool)), SLOT( connectHost() ));
133
action = actionCollection()->addAction( "configure_sheet" );
134
action->setIcon( KIcon("configure") );
135
action->setText( i18n( "&Worksheet Properties" ) );
136
connect(action, SIGNAL(triggered(bool)), mWorkSpace, SLOT( configure() ));
138
setupGUI(ToolBar | Keys | StatusBar | Create);
141
void TopLevel::currentTabChanged(int index)
143
kDebug() << "Current tab changed to " << index;
144
QWidget *wdg = mWorkSpace->widget(index);
145
WorkSheet *sheet = (WorkSheet *)(wdg);
147
bool locked = !sheet || sheet->isLocked();
148
mTabRemoveAction->setVisible(!locked);
149
mTabExportAction->setVisible(!locked);
150
mMonitorRemoteAction->setVisible(!locked);
152
if(!locked && !mSensorBrowser) {
153
startSensorBrowserWidget();
156
if(mSensorBrowser->isVisible() && locked) //going from visible to not visible to save the state
157
mSplitterSize = mSplitter->sizes();
158
mSensorBrowser->setVisible(!locked);
162
void TopLevel::startSensorBrowserWidget()
164
if(mSensorBrowser) return;
165
kDebug() << "Creating sensor browser";
166
mSensorBrowser = new SensorBrowserWidget( 0, KSGRD::SensorMgr );
167
mSplitter->insertWidget(0,mSensorBrowser);
168
mSplitter->setSizes( mSplitterSize );
172
* DBUS Interface functions
175
void TopLevel::showOnCurrentDesktop()
177
KWindowSystem::setOnDesktop( winId(), KWindowSystem::currentDesktop() );
178
kapp->updateUserTimestamp();
179
KWindowSystem::forceActiveWindow( winId() );
182
void TopLevel::importWorkSheet( const QString &fileName )
184
mWorkSpace->importWorkSheet( KUrl( fileName ) );
187
void TopLevel::removeWorkSheet( const QString &fileName )
189
mWorkSpace->removeWorkSheet( fileName );
192
QStringList TopLevel::listSensors( const QString &hostName )
194
startSensorBrowserWidget();
195
return mSensorBrowser->listSensors( hostName );
198
QStringList TopLevel::listHosts()
200
startSensorBrowserWidget();
201
return mSensorBrowser->listHosts();
204
void TopLevel::initStatusBar()
206
KSGRD::SensorMgr->engage( "localhost", "", "ksysguardd" );
207
/* Request info about the swap space size and the units it is
208
* measured in. The requested info will be received by
209
* answerReceived(). */
210
KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/used?",
211
(KSGRD::SensorClient*)this, 7 );
214
KToggleAction *sb = dynamic_cast<KToggleAction*>(action("options_show_statusbar"));
216
connect(sb, SIGNAL(toggled(bool)), this, SLOT(updateStatusBar()));
219
void TopLevel::updateStatusBar()
221
if ( mTimerId == -1 )
222
mTimerId = startTimer( 2000 );
224
// call timerEvent to fill the status bar with real values
228
void TopLevel::connectHost()
230
HostConnector hostConnector( this );
232
// hostConnector.setHostNames( mHostList );
233
// hostConnector.setCommands( mCommandList );
235
// hostConnector.setCurrentHostName( "" );
237
if ( !hostConnector.exec() )
240
// mHostList = hostConnector.hostNames();
241
// mCommandList = hostConnector.commands();
244
QString command = "";
247
/* Check which radio button is selected and set parameters
249
if ( hostConnector.useSsh() )
251
else if ( hostConnector.useRsh() )
253
else if ( hostConnector.useDaemon() )
254
port = hostConnector.port();
256
command = hostConnector.currentCommand();
258
KSGRD::SensorMgr->engage( hostConnector.currentHostName(), shell, command, port );
261
void TopLevel::disconnectHost()
264
mSensorBrowser->disconnect();
267
void TopLevel::editToolbars()
269
saveMainWindowSettings( KConfigGroup( KGlobal::config(), "MainWindow" ) );
270
KEditToolBar dlg( actionCollection() );
271
connect( &dlg, SIGNAL( newToolbarConfig() ), this,
272
SLOT( slotNewToolbarConfig() ) );
277
void TopLevel::slotNewToolbarConfig()
280
applyMainWindowSettings( KConfigGroup( KGlobal::config(), "MainWindow" ) );
283
bool TopLevel::event( QEvent *e )
285
if ( e->type() == QEvent::User ) {
286
/* Due to the asynchronous communication between ksysguard and its
287
* back-ends, we sometimes need to show message boxes that were
288
* triggered by objects that have died already. */
289
KMessageBox::error( this, static_cast<KSGRD::SensorManager::MessageEvent*>(e)->message() );
294
return KXmlGuiWindow::event( e );
297
void TopLevel::timerEvent( QTimerEvent* )
299
if ( statusBar()->isVisibleTo( this ) ) {
300
/* Request some info about the memory status. The requested
301
* information will be received by answerReceived(). */
302
KSGRD::SensorMgr->sendRequest( "localhost", "pscount",
303
(KSGRD::SensorClient*)this, 0 );
304
KSGRD::SensorMgr->sendRequest( "localhost", "cpu/system/idle",
305
(KSGRD::SensorClient*)this, 1 );
306
KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/free",
307
(KSGRD::SensorClient*)this, 2 );
308
KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/used",
309
(KSGRD::SensorClient*)this, 3 );
310
KSGRD::SensorMgr->sendRequest( "localhost", "mem/physical/application",
311
(KSGRD::SensorClient*)this, 4 );
312
KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/free",
313
(KSGRD::SensorClient*)this, 5 );
314
KSGRD::SensorMgr->sendRequest( "localhost", "mem/swap/used",
315
(KSGRD::SensorClient*)this, 6 );
319
bool TopLevel::queryClose()
321
if ( !mWorkSpace->saveOnQuit() )
324
KConfigGroup cg( KGlobal::config(), "MainWindow" );
325
saveProperties( cg );
326
KGlobal::config()->sync();
331
void TopLevel::readProperties( const KConfigGroup& cfg )
334
/* we can ignore 'isMaximized' because we can't set the window
335
maximized, so we save the coordinates instead */
336
// if ( cfg.readEntry( "isMinimized" , false) == true )
339
mSplitterSize = cfg.readEntry( "SplitterSizeList",QList<int>() );
340
if ( mSplitterSize.isEmpty() ) {
341
// start with a 30/70 ratio
342
mSplitterSize.append( 10 );
343
mSplitterSize.append( 90 );
346
KSGRD::SensorMgr->readProperties( cfg );
347
KSGRD::Style->readProperties( cfg );
349
mWorkSpace->readProperties( cfg );
351
applyMainWindowSettings( cfg );
354
void TopLevel::saveProperties( KConfigGroup& cfg )
356
cfg.writeEntry( "isMinimized", isMinimized() );
358
if(mSensorBrowser && mSensorBrowser->isVisible())
359
cfg.writeEntry( "SplitterSizeList", mSplitter->sizes());
360
else if(mSplitterSize.size() == 2 && mSplitterSize.value(0) != 0 && mSplitterSize.value(1) != 0)
361
cfg.writeEntry( "SplitterSizeList", mSplitterSize );
363
KSGRD::Style->saveProperties( cfg );
364
KSGRD::SensorMgr->saveProperties( cfg );
366
saveMainWindowSettings( cfg );
367
mWorkSpace->saveProperties( cfg );
370
void TopLevel::answerReceived( int id, const QList<QByteArray> &answerList )
372
// we have received an answer from the daemon.
374
if(!answerList.isEmpty()) answer = answerList[0];
377
static long mFree = 0;
378
static long mUsedApplication = 0;
379
static long mUsedTotal = 0;
380
static long sUsed = 0;
381
static long sFree = 0;
385
s = i18n( " %1 processes ", answer.toInt() );
386
statusBar()->changeItem( s, 0 );
390
s = i18n( " CPU: %1% ", (int) (100 - answer.toFloat()) );
391
statusBar()->changeItem( s, 1 );
395
mFree = answer.toLong();
399
mUsedTotal = answer.toLong();
403
mUsedApplication = answer.toLong();
404
s = i18n( " Memory: %1 / %2 " ,
405
KGlobal::locale()->formatByteSize( mUsedApplication*1024),
406
KGlobal::locale()->formatByteSize( (mFree+mUsedTotal)*1024 ) );
407
statusBar()->changeItem( s, 2 );
411
sFree = answer.toLong();
415
sUsed = answer.toLong();
416
setSwapInfo( sUsed, sFree, unit );
420
KSGRD::SensorIntegerInfo info( answer );
421
unit = KSGRD::SensorMgr->translateUnit( info.unit() );
427
void TopLevel::setSwapInfo( long used, long free, const QString & )
430
if ( used == 0 && free == 0 ) // no swap available
431
msg = i18n( " No swap space available " );
433
msg = i18n( " Swap: %1 / %2 " ,
434
KGlobal::locale()->formatByteSize( used*1024 ),
435
KGlobal::locale()->formatByteSize( free*1024) );
438
statusBar()->changeItem( msg, 3 );
442
* Once upon a time...
444
extern "C" KDE_EXPORT int kdemain( int argc, char** argv )
446
// initpipe is used to keep the parent process around till the child
447
// has registered with dbus
448
#ifdef FORK_KSYSGUARD
452
/* This forking will put ksysguard in it's own session not having a
453
* controlling terminal attached to it. This prevents ssh from
454
* using this terminal for password requests. Thus, you
455
* need a ssh with ssh-askpass support to popup an X dialog to
456
* enter the password. */
457
#ifdef FORK_KSYSGUARD
459
if ( ( pid = fork() ) < 0 )
463
close( initpipe[ 1 ] );
465
// wait till init is complete
467
while( read( initpipe[ 0 ], &c, 1 ) < 0 );
470
close( initpipe[ 0 ] );
474
close( initpipe[ 0 ] );
478
KAboutData aboutData( "ksysguard", 0, ki18n( "System Monitor" ),
479
KSYSGUARD_VERSION, ki18n(Description), KAboutData::License_GPL,
480
ki18n( "(c) 1996-2006 The KDE System Monitor Developers" ) );
481
aboutData.addAuthor( ki18n("John Tapsell"), ki18n("Current Maintainer"), "john.tapsell@kde.org" );
482
aboutData.addAuthor( ki18n("Chris Schlaeger"), ki18n("Previous Maintainer"), "cs@kde.org" );
483
aboutData.addAuthor( ki18n("Greg Martyn"), KLocalizedString(), "greg.martyn@gmail.com" );
484
aboutData.addAuthor( ki18n("Tobias Koenig"), KLocalizedString(), "tokoe@kde.org" );
485
aboutData.addAuthor( ki18n("Nicolas Leclercq"), KLocalizedString(), "nicknet@planete.net" );
486
aboutData.addAuthor( ki18n("Alex Sanda"), KLocalizedString(), "alex@darkstart.ping.at" );
487
aboutData.addAuthor( ki18n("Bernd Johannes Wuebben"), KLocalizedString(), "wuebben@math.cornell.edu" );
488
aboutData.addAuthor( ki18n("Ralf Mueller"), KLocalizedString(), "rlaf@bj-ig.de" );
489
aboutData.addAuthor( ki18n("Hamish Rodda"), KLocalizedString(), "rodda@kde.org" );
490
aboutData.addAuthor( ki18n("Torsten Kasch"), ki18n( "Solaris Support\n"
491
"Parts derived (by permission) from the sunos5\n"
492
"module of William LeFebvre's \"top\" utility." ),
493
"tk@Genetik.Uni-Bielefeld.DE" );
495
KCmdLineArgs::init( argc, argv, &aboutData );
497
KCmdLineOptions options;
498
options.add("+[worksheet]", ki18n( "Optional worksheet files to load" ));
499
KCmdLineArgs::addCmdLineOptions( options );
500
// initialize KDE application
501
KApplication *app = new KApplication;
503
KSGRD::SensorMgr = new KSGRD::SensorManager();
504
KSGRD::Style = new KSGRD::StyleEngine();
507
#ifdef FORK_KSYSGUARD
509
write( initpipe[ 1 ], &c, 1 );
510
close( initpipe[ 1 ] );
512
topLevel = new TopLevel();
514
// create top-level widget
515
if ( app->isSessionRestored() )
516
topLevel->restore( 1 );
519
topLevel->readProperties( KConfigGroup( KGlobal::config(), "MainWindow" ) );
522
topLevel->initStatusBar();
524
KSGRD::SensorMgr->setBroadcaster( topLevel );
526
// run the application
527
int result = app->exec();
530
delete KSGRD::SensorMgr;
536
#include "ksysguard.moc"