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 <kstandarddirs.h>
50 #include <kstatusbar.h>
51 #include <kstandardaction.h>
52 #include <ktoggleaction.h>
55 #include <kwindowsystem.h>
58 #include "../version.h"
59 #include "SensorBrowser.h"
60 #include "Workspace.h"
61 #include "WorkSheet.h"
62 #include "StyleEngine.h"
63 #include "HostConnector.h"
64 #include "ProcessController.h"
66 #include "ksysguard.h"
70 //Comment out to stop ksysguard from forking. Good for debugging
71 //#define FORK_KSYSGUARD
73 static const char Description
[] = I18N_NOOP( "KDE System Monitor" );
77 This is the constructor for the main widget. It sets up the menu and the
81 : KXmlGuiWindow( NULL
)
83 QDBusConnection::sessionBus().registerObject("/", this, QDBusConnection::ExportScriptableSlots
);
86 mSplitter
= new QSplitter( this );
87 mSplitter
->setOrientation( Qt::Horizontal
);
88 mSplitter
->setOpaqueResize( KGlobalSettings::opaqueResize() );
89 setCentralWidget( mSplitter
);
93 mWorkSpace
= new Workspace( mSplitter
);
94 connect( mWorkSpace
, SIGNAL( setCaption( const QString
&) ),
95 SLOT( setCaption( const QString
&) ) );
96 connect( mWorkSpace
, SIGNAL( currentChanged( int ) ),
97 SLOT( currentTabChanged( int ) ) );
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 mNewWorksheetAction
= actionCollection()->addAction("new_worksheet");
120 mNewWorksheetAction
->setIcon(KIcon("tab-new"));
121 connect(mNewWorksheetAction
, SIGNAL(triggered(bool)), mWorkSpace
, SLOT( newWorkSheet() ));
122 mInsertWorksheetAction
= actionCollection()->addAction("import_worksheet");
123 mInsertWorksheetAction
->setIcon(KIcon("document-open") );
124 connect(mInsertWorksheetAction
, SIGNAL(triggered(bool)), mWorkSpace
, SLOT( importWorkSheet() ));
125 mTabRemoveAction
= actionCollection()->addAction( "remove_worksheet" );
126 mTabRemoveAction
->setIcon( KIcon("tab-close") );
127 connect(mTabRemoveAction
, SIGNAL(triggered(bool)), mWorkSpace
, SLOT( removeWorkSheet() ));
128 mTabExportAction
= actionCollection()->addAction( "export_worksheet" );
129 mTabExportAction
->setIcon( KIcon("document-save-as") );
130 connect(mTabExportAction
, SIGNAL(triggered(bool)), mWorkSpace
, SLOT( exportWorkSheet() ));
134 mMonitorRemoteAction
= actionCollection()->addAction( "connect_host" );
135 mMonitorRemoteAction
->setIcon( KIcon("network-connect") );
136 connect(mMonitorRemoteAction
, SIGNAL(triggered(bool)), SLOT( connectHost() ));
138 mConfigureSheetAction
= actionCollection()->addAction( "configure_sheet" );
139 mConfigureSheetAction
->setIcon( KIcon("configure") );
140 connect(mConfigureSheetAction
, SIGNAL(triggered(bool)), mWorkSpace
, SLOT( configure() ));
142 if (!initialGeometrySet())
143 resize( QSize(700, 480).expandedTo(minimumSizeHint()));
148 void TopLevel::retranslateUi()
150 setPlainCaption( i18n( "System Monitor" ) );
151 mNewWorksheetAction
->setText(i18n( "&New Worksheet..." ));
152 mInsertWorksheetAction
->setText(i18n( "Import Worksheet..." ));
153 mTabExportAction
->setText( i18n( "&Export Worksheet..." ) );
154 mTabRemoveAction
->setText( i18n( "&Remove Worksheet" ) );
155 mMonitorRemoteAction
->setText( i18n( "Monitor remote machine..." ) );
156 mConfigureSheetAction
->setText( i18n( "&Worksheet Properties" ) );
158 KAction
*tmpQuitAction
= KStandardAction::quit( NULL
, NULL
, NULL
);
159 mQuitAction
->setText(tmpQuitAction
->text());
160 mQuitAction
->setWhatsThis(tmpQuitAction
->whatsThis());
161 mQuitAction
->setToolTip(tmpQuitAction
->toolTip());
162 delete tmpQuitAction
;
164 mQuitAction
= KStandardAction::quit( this, SLOT( close() ), actionCollection() );
168 void TopLevel::currentTabChanged(int index
)
170 QWidget
*wdg
= mWorkSpace
->widget(index
);
171 WorkSheet
*sheet
= (WorkSheet
*)(wdg
);
173 bool locked
= !sheet
|| sheet
->isLocked();
174 mTabRemoveAction
->setVisible(!locked
);
175 mTabExportAction
->setVisible(!locked
);
176 mMonitorRemoteAction
->setVisible(!locked
);
178 if(!locked
&& !mSensorBrowser
) {
179 startSensorBrowserWidget();
182 if(mSensorBrowser
->isVisible() && locked
) //going from visible to not visible to save the state
183 mSplitterSize
= mSplitter
->sizes();
184 mSensorBrowser
->setVisible(!locked
);
188 void TopLevel::startSensorBrowserWidget()
190 if(mSensorBrowser
) return;
191 mSensorBrowser
= new SensorBrowserWidget( 0, KSGRD::SensorMgr
);
192 mSplitter
->insertWidget(2,mSensorBrowser
);
193 mSplitter
->setSizes( mSplitterSize
);
197 * DBUS Interface functions
200 void TopLevel::showOnCurrentDesktop()
202 KWindowSystem::setOnDesktop( winId(), KWindowSystem::currentDesktop() );
203 kapp
->updateUserTimestamp();
204 KWindowSystem::forceActiveWindow( winId() );
207 void TopLevel::importWorkSheet( const QString
&fileName
)
209 mWorkSpace
->importWorkSheet( KUrl( fileName
) );
212 void TopLevel::removeWorkSheet( const QString
&fileName
)
214 mWorkSpace
->removeWorkSheet( fileName
);
217 QStringList
TopLevel::listSensors( const QString
&hostName
)
219 if(!mSensorBrowser
) {
220 setUpdatesEnabled(false);
221 startSensorBrowserWidget();
222 mSensorBrowser
->setVisible(false);
223 setUpdatesEnabled(true);
225 return mSensorBrowser
->listSensors( hostName
);
228 QStringList
TopLevel::listHosts()
230 if(!mSensorBrowser
) {
231 setUpdatesEnabled(false);
232 startSensorBrowserWidget();
233 mSensorBrowser
->setVisible(false);
234 setUpdatesEnabled(true);
236 return mSensorBrowser
->listHosts();
239 void TopLevel::initStatusBar()
241 KSGRD::SensorMgr
->engage( "localhost", "", "ksysguardd" );
242 /* Request info about the swap space size and the units it is
243 * measured in. The requested info will be received by
244 * answerReceived(). */
245 KSGRD::SensorMgr
->sendRequest( "localhost", "mem/swap/used?",
246 (KSGRD::SensorClient
*)this, 7 );
248 KToggleAction
*sb
= dynamic_cast<KToggleAction
*>(action("options_show_statusbar"));
250 connect(sb
, SIGNAL(toggled(bool)), this, SLOT(updateStatusBar()));
251 setupGUI(ToolBar
| Keys
| StatusBar
| Save
| Create
);
254 void TopLevel::updateStatusBar()
256 if ( mTimerId
== -1 )
257 mTimerId
= startTimer( 2000 );
259 // call timerEvent to fill the status bar with real values
263 void TopLevel::connectHost()
265 HostConnector
hostConnector( this );
267 // hostConnector.setHostNames( mHostList );
268 // hostConnector.setCommands( mCommandList );
270 // hostConnector.setCurrentHostName( "" );
272 if ( !hostConnector
.exec() )
275 // mHostList = hostConnector.hostNames();
276 // mCommandList = hostConnector.commands();
279 QString command
= "";
282 /* Check which radio button is selected and set parameters
284 if ( hostConnector
.useSsh() )
286 else if ( hostConnector
.useRsh() )
288 else if ( hostConnector
.useDaemon() )
289 port
= hostConnector
.port();
291 command
= hostConnector
.currentCommand();
293 KSGRD::SensorMgr
->engage( hostConnector
.currentHostName(), shell
, command
, port
);
296 void TopLevel::disconnectHost()
299 mSensorBrowser
->disconnect();
302 void TopLevel::editToolbars()
304 saveMainWindowSettings( KConfigGroup( KGlobal::config(), "MainWindow" ) );
305 KEditToolBar
dlg( actionCollection() );
306 connect( &dlg
, SIGNAL( newToolBarConfig() ), this,
307 SLOT( slotNewToolbarConfig() ) );
312 void TopLevel::slotNewToolbarConfig()
315 applyMainWindowSettings( KConfigGroup( KGlobal::config(), "MainWindow" ) );
318 bool TopLevel::event( QEvent
*e
)
320 if ( e
->type() == QEvent::User
) {
321 /* Due to the asynchronous communication between ksysguard and its
322 * back-ends, we sometimes need to show message boxes that were
323 * triggered by objects that have died already. */
324 KMessageBox::error( this, static_cast<KSGRD::SensorManager::MessageEvent
*>(e
)->message() );
329 return KXmlGuiWindow::event( e
);
332 void TopLevel::timerEvent( QTimerEvent
* )
334 if ( statusBar()->isVisibleTo( this ) ) {
335 /* Request some info about the memory status. The requested
336 * information will be received by answerReceived(). */
337 KSGRD::SensorMgr
->sendRequest( "localhost", "pscount",
338 (KSGRD::SensorClient
*)this, 0 );
339 KSGRD::SensorMgr
->sendRequest( "localhost", "cpu/idle",
340 (KSGRD::SensorClient
*)this, 1 );
341 KSGRD::SensorMgr
->sendRequest( "localhost", "mem/physical/free",
342 (KSGRD::SensorClient
*)this, 2 );
343 KSGRD::SensorMgr
->sendRequest( "localhost", "mem/physical/used",
344 (KSGRD::SensorClient
*)this, 3 );
345 KSGRD::SensorMgr
->sendRequest( "localhost", "mem/physical/application",
346 (KSGRD::SensorClient
*)this, 4 );
347 KSGRD::SensorMgr
->sendRequest( "localhost", "mem/swap/free",
348 (KSGRD::SensorClient
*)this, 5 );
349 KSGRD::SensorMgr
->sendRequest( "localhost", "mem/swap/used",
350 (KSGRD::SensorClient
*)this, 6 );
354 void TopLevel::changeEvent( QEvent
* event
)
356 if (event
->type() == QEvent::LanguageChange
) {
357 KSGRD::SensorMgr
->retranslate();
358 setUpdatesEnabled(false);
359 setupGUI(ToolBar
| Keys
| StatusBar
| Create
);
361 setUpdatesEnabled(true);
363 KXmlGuiWindow::changeEvent(event
);
366 bool TopLevel::queryClose()
368 if ( !mWorkSpace
->saveOnQuit() )
371 KConfigGroup
cg( KGlobal::config(), "MainWindow" );
372 saveProperties( cg
);
373 KGlobal::config()->sync();
378 void TopLevel::readProperties( const KConfigGroup
& cfg
)
381 /* we can ignore 'isMaximized' because we can't set the window
382 maximized, so we save the coordinates instead */
383 // if ( cfg.readEntry( "isMinimized" , false) == true )
386 mSplitterSize
= cfg
.readEntry( "SplitterSizeList",QList
<int>() );
387 if ( mSplitterSize
.isEmpty() ) {
388 // start with a 30/70 ratio
389 mSplitterSize
.append( 10 );
390 mSplitterSize
.append( 90 );
393 KSGRD::SensorMgr
->readProperties( cfg
);
394 KSGRD::Style
->readProperties( cfg
);
396 mWorkSpace
->readProperties( cfg
);
398 QList
<WorkSheet
*> workSheets
= mWorkSpace
->getWorkSheets();
399 ProcessController
*processController
= NULL
;
400 foreach(WorkSheet
*sheet
, workSheets
) {
401 processController
= sheet
->getLocalProcessController();
402 if(processController
!= NULL
) {
403 for(int i
= 0; i
< processController
->actions().size(); i
++) {
404 actionCollection()->addAction("processAction" + QString::number(i
), processController
->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 long mFree
= 0;
436 static long mUsedApplication
= 0;
437 static long mUsedTotal
= 0;
438 static long sUsed
= 0;
439 static long sFree
= 0;
443 s
= i18n( " %1 processes ", answer
.toInt() );
444 sbProcessCount
->setText( s
);
449 s
= i18n( " CPU: %1% ", (int) (100 - answer
.toFloat()) );
450 sbCpuStat
->setText( s
);
454 mFree
= answer
.toLong();
458 mUsedTotal
= answer
.toLong();
462 mUsedApplication
= answer
.toLong();
463 s
= i18n( " Memory: %1 / %2 " ,
464 KGlobal::locale()->formatByteSize( mUsedApplication
*1024),
465 KGlobal::locale()->formatByteSize( (mFree
+mUsedTotal
)*1024 ) );
466 sbMemTotal
->setText( s
);
470 sFree
= answer
.toLong();
474 sUsed
= answer
.toLong();
475 setSwapInfo( sUsed
, sFree
, unit
);
479 KSGRD::SensorIntegerInfo
info( answer
);
480 unit
= KSGRD::SensorMgr
->translateUnit( info
.unit() );
486 void TopLevel::setSwapInfo( long used
, long free
, const QString
& )
489 if ( used
== 0 && free
== 0 ) // no swap available
490 msg
= i18n( " No swap space available " );
492 msg
= i18n( " Swap: %1 / %2 " ,
493 KGlobal::locale()->formatByteSize( used
*1024 ),
494 KGlobal::locale()->formatByteSize( (free
+used
)*1024) );
497 sbSwapTotal
->setText( msg
);
501 * Once upon a time...
503 extern "C" KDE_EXPORT
int kdemain( int argc
, char** argv
)
505 // initpipe is used to keep the parent process around till the child
506 // has registered with dbus
507 #ifdef FORK_KSYSGUARD
511 /* This forking will put ksysguard in it's own session not having a
512 * controlling terminal attached to it. This prevents ssh from
513 * using this terminal for password requests. Thus, you
514 * need a ssh with ssh-askpass support to popup an X dialog to
515 * enter the password. */
516 #ifdef FORK_KSYSGUARD
518 if ( ( pid
= fork() ) < 0 )
522 close( initpipe
[ 1 ] );
524 // wait till init is complete
526 while( read( initpipe
[ 0 ], &c
, 1 ) < 0 );
529 close( initpipe
[ 0 ] );
533 close( initpipe
[ 0 ] );
537 KAboutData
aboutData( "ksysguard", 0, ki18n( "System Monitor" ),
538 KSYSGUARD_VERSION
, ki18n(Description
), KAboutData::License_GPL
,
539 ki18n( "(c) 1996-2008 The KDE System Monitor Developers" ) );
540 aboutData
.addAuthor( ki18n("John Tapsell"), ki18n("Current Maintainer"), "john.tapsell@kde.org" );
541 aboutData
.addAuthor( ki18n("Chris Schlaeger"), ki18n("Previous Maintainer"), "cs@kde.org" );
542 aboutData
.addAuthor( ki18n("Greg Martyn"), KLocalizedString(), "greg.martyn@gmail.com" );
543 aboutData
.addAuthor( ki18n("Tobias Koenig"), KLocalizedString(), "tokoe@kde.org" );
544 aboutData
.addAuthor( ki18n("Nicolas Leclercq"), KLocalizedString(), "nicknet@planete.net" );
545 aboutData
.addAuthor( ki18n("Alex Sanda"), KLocalizedString(), "alex@darkstart.ping.at" );
546 aboutData
.addAuthor( ki18n("Bernd Johannes Wuebben"), KLocalizedString(), "wuebben@math.cornell.edu" );
547 aboutData
.addAuthor( ki18n("Ralf Mueller"), KLocalizedString(), "rlaf@bj-ig.de" );
548 aboutData
.addAuthor( ki18n("Hamish Rodda"), KLocalizedString(), "rodda@kde.org" );
549 aboutData
.addAuthor( ki18n("Torsten Kasch"), ki18n( "Solaris Support\n"
550 "Parts derived (by permission) from the sunos5\n"
551 "module of William LeFebvre's \"top\" utility." ),
552 "tk@Genetik.Uni-Bielefeld.DE" );
554 KCmdLineArgs::init( argc
, argv
, &aboutData
);
556 KCmdLineOptions options
;
557 options
.add("+[worksheet]", ki18n( "Optional worksheet files to load" ));
558 KCmdLineArgs::addCmdLineOptions( options
);
559 // initialize KDE application
560 KApplication
*app
= new KApplication
;
561 QApplication::setWindowIcon(KIcon("utilities-system-monitor"));
563 KSGRD::SensorMgr
= new KSGRD::SensorManager();
564 KSGRD::Style
= new KSGRD::StyleEngine();
566 #ifdef FORK_KSYSGUARD
568 write( initpipe
[ 1 ], &c
, 1 );
569 close( initpipe
[ 1 ] );
571 topLevel
= new TopLevel();
573 topLevel
->initStatusBar();
575 // create top-level widget
576 topLevel
->readProperties( KConfigGroup( KGlobal::config(), "MainWindow" ) );
578 if ( app
->isSessionRestored() )
579 topLevel
->restore( 1 );
582 KSGRD::SensorMgr
->setBroadcaster( topLevel
); // SensorMgr uses a QPointer for toplevel, so it is okay if topLevel is deleted first
584 // run the application
585 int result
= app
->exec();
588 delete KSGRD::SensorMgr
;
594 #include "ksysguard.moc"