bum-de-dum! plasma to kdelibs.

svn path=/trunk/KDE/kdelibs/; revision=879757
This commit is contained in:
Aaron J. Seigo 2008-11-03 23:03:26 +00:00
parent 8a4bc36925
commit fa5a2d87d9
538 changed files with 39096 additions and 1691 deletions

View File

@ -1,299 +1,14 @@
find_package(Nepomuk REQUIRED)
include (KDE4Defaults)
include(NepomukMacros)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${KDEBASE_WORKSPACE_SOURCE_DIR}/libs ${CMAKE_CURRENT_SOURCE_DIR}/.. ${KDE4_INCLUDES})
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
include_directories(${OPENGL_INCLUDE_DIR})
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
add_subdirectory(tests)
add_definitions(-DKDE_DEFAULT_DEBUG_AREA=1209)
########### next target ###############
set(plasmagik_SRCS
packagemetadata.cpp
packagestructure.cpp
package.cpp
)
set(plasma_LIB_SRCS
${plasmagik_SRCS}
abstractrunner.cpp
animationdriver.cpp
animator.cpp
applet.cpp
configloader.cpp
containment.cpp
context.cpp
corona.cpp
datacontainer.cpp
dataengine.cpp
dataenginemanager.cpp
delegate.cpp
dialog.cpp
extender.cpp
extenderitem.cpp
paintutils.cpp
framesvg.cpp
plasma.cpp
popupapplet.cpp
private/applethandle.cpp
private/datacontainer_p.cpp
private/desktoptoolbox.cpp
private/extenderapplet.cpp
private/nativetabbar.cpp
private/packages.cpp
private/paneltoolbox.cpp
private/style.cpp
private/toolbox.cpp
private/tooltip.cpp
private/windowpreview.cpp
querymatch.cpp
runnercontext.cpp
runnermanager.cpp
scripting/appletscript.cpp
scripting/dataenginescript.cpp
scripting/runnerscript.cpp
scripting/scriptengine.cpp
scripting/uiloader.cpp
service.cpp
servicejob.cpp
svg.cpp
theme.cpp
tooltipcontent.cpp
tooltipmanager.cpp
version.cpp
view.cpp
wallpaper.cpp
widgets/checkbox.cpp
widgets/combobox.cpp
widgets/flashinglabel.cpp
widgets/frame.cpp
widgets/groupbox.cpp
widgets/iconwidget.cpp
widgets/label.cpp
widgets/lineedit.cpp
widgets/meter.cpp
widgets/pushbutton.cpp
widgets/radiobutton.cpp
widgets/scrollbar.cpp
widgets/signalplotter.cpp
widgets/slider.cpp
widgets/busywidget.cpp
widgets/svgwidget.cpp
widgets/tabbar.cpp
widgets/treeview.cpp
widgets/textedit.cpp
widgets/webview.cpp
)
#NEPOMUK_GENERATE_FROM_ONTOLOGY(
# nwc.nrl
# ${metadata_test_BINARY_DIR}
# TEST_HEADERS
# TEST_SOURCES
# TEST_INCLUDES
#)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
MESSAGE(STATUS "Adding support for OpenGL applets to libplasma")
set(plasma_LIB_SRCS
${plasma_LIB_SRCS}
glapplet.cpp)
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
kde4_add_library(plasma SHARED ${plasma_LIB_SRCS})
target_link_libraries(plasma ${KDE4_KIO_LIBS} ${KDE4_KFILE_LIBS} ${KDE4_KNEWSTUFF2_LIBS}
${QT_QTUITOOLS_LIBRARY} ${QT_QTWEBKIT_LIBRARY}
${KDE4_THREADWEAVER_LIBRARIES} ${KDE4_SOLID_LIBS} ${X11_LIBRARIES})
if(DL_LIBRARY)
target_link_libraries(plasma ${DL_LIBRARY})
endif(DL_LIBRARY)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
target_link_libraries(plasma ${QT_QTOPENGL_LIBRARY} ${OPENGL_gl_LIBRARY})
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
set_target_properties(plasma PROPERTIES
VERSION 3.0.0
SOVERSION 3
${KDE4_DISABLE_PROPERTY_}LINK_INTERFACE_LIBRARIES "${KDE4_KDEUI_LIBS}"
)
install(TARGETS plasma ${INSTALL_TARGETS_DEFAULT_ARGS})
########### install files ###############
set(plasmagik_HEADERS
packagemetadata.h
packagestructure.h
package.h
)
install(FILES ${plasmagik_HEADERS} DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/ COMPONENT Devel)
set(plasma_LIB_INCLUDES
abstractrunner.h
animationdriver.h
animator.h
applet.h
configloader.h
containment.h
context.h
corona.h
datacontainer.h
dataengine.h
dataenginemanager.h
delegate.h
dialog.h
extender.h
extenderitem.h
paintutils.h
framesvg.h
plasma.h
plasma_export.h
popupapplet.h
querymatch.h
runnercontext.h
runnermanager.h
service.h
servicejob.h
svg.h
theme.h
tooltipcontent.h
tooltipmanager.h
tooltipmanager.h
version.h
view.h
wallpaper.h)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
set(plasma_LIB_INCLUDES
${plasma_LIB_INCLUDES}
glapplet.h)
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
install(FILES
${plasma_LIB_INCLUDES}
DESTINATION ${INCLUDE_INSTALL_DIR}/plasma COMPONENT Devel)
install(FILES
widgets/checkbox.h
widgets/combobox.h
widgets/flashinglabel.h
widgets/frame.h
widgets/groupbox.h
widgets/iconwidget.h
widgets/label.h
widgets/lineedit.h
widgets/meter.h
widgets/pushbutton.h
widgets/radiobutton.h
widgets/scrollbar.h
widgets/signalplotter.h
widgets/slider.h
widgets/busywidget.h
widgets/svgwidget.h
widgets/tabbar.h
widgets/treeview.h
widgets/textedit.h
widgets/webview.h
DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/widgets COMPONENT Devel)
install(FILES
scripting/appletscript.h
scripting/dataenginescript.h
scripting/runnerscript.h
scripting/scriptengine.h
scripting/uiloader.h
DESTINATION ${INCLUDE_INSTALL_DIR}/plasma/scripting COMPONENT Devel)
install(FILES
includes/AbstractRunner
includes/AnimationDriver
includes/Animator
includes/Applet
includes/AppletScript
includes/CheckBox
includes/ComboBox
includes/ConfigLoader
includes/Containment
includes/Context
includes/Corona
includes/DataContainer
includes/DataEngine
includes/DataEngineManager
includes/DataEngineScript
includes/Delegate
includes/Dialog
includes/Extender
includes/ExtenderItem
includes/FlashingLabel
includes/Frame
includes/FrameSvg
includes/GroupBox
includes/IconWidget
includes/Label
includes/LineEdit
includes/Meter
includes/Package
includes/PackageMetadata
includes/PackageStructure
includes/PaintUtils
includes/Plasma
includes/PopupApplet
includes/PushButton
includes/QueryMatch
includes/RadioButton
includes/RunnerContext
includes/RunnerManager
includes/RunnerScript
includes/ScriptEngine
includes/ScrollBar
includes/Service
includes/ServiceJob
includes/SignalPlotter
includes/Slider
includes/BusyWidget
includes/Svg
includes/SvgWidget
includes/TabBar
includes/TextEdit
includes/ToolTipManager
includes/Theme
includes/TreeView
includes/UiLoader
includes/View
includes/Version
includes/Wallpaper
includes/WebView
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Plasma COMPONENT Devel)
if(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
install(FILES
includes/GLApplet
DESTINATION ${INCLUDE_INSTALL_DIR}/KDE/Plasma COMPONENT Devel)
endif(QT_QTOPENGL_FOUND AND OPENGL_FOUND)
install(FILES
servicetypes/plasma-animator.desktop
servicetypes/plasma-applet.desktop
servicetypes/plasma-containment.desktop
servicetypes/plasma-dataengine.desktop
servicetypes/plasma-packagestructure.desktop
servicetypes/plasma-runner.desktop
servicetypes/plasma-scriptengine.desktop
servicetypes/plasma-wallpaper.desktop
DESTINATION ${SERVICETYPES_INSTALL_DIR})
install(FILES
servicetypes/plasma-applet-extenderapplet.desktop
DESTINATION ${SERVICES_INSTALL_DIR})
install(FILES scripting/plasmoids.knsrc DESTINATION ${CONFIG_INSTALL_DIR})
add_subdirectory(kdm)
add_subdirectory(solid)
add_subdirectory(plasma)
add_subdirectory(libplasmaclock)
if(NOT WIN32)
add_subdirectory(taskmanager)
add_subdirectory(ksysguard)
add_subdirectory(kworkspace)
endif(NOT WIN32)
if(Nepomuk_FOUND)
add_subdirectory(nepomukquery)
add_subdirectory(nepomukqueryclient)
endif(Nepomuk_FOUND)

View File

@ -1,100 +1,16 @@
/** @mainpage Plasma libraries
/** @mainpage KDE Workspace Libraries
libplasma is the core of the Plasma desktop. It provides a framework of graphical
widgets (Plasma::Applet) that can be organised into managed groupings
(Plasma::Containment), such as a desktop or panel. It also provides a data
abstraction layer (Plasma::DataEngine) and a corresponding service interaction
layer (Plasma::Service) to make implementing widgets easier and a
system of callouts (Plasma::AbstractRunner) that provide responses to queries,
from running an application to performing a quick calculation.
Libraries for the KDE workspace.
<p>
The following libraries are available:
- <a href="../plasma/html/index.html">libplasma</a>, a library for implementing
%Plasma applets and data engines
- <a href="../kworkspace/html/index.html">libkworkspace</a>, a library for
interacting with the workspace
The <a href="http://doc.trolltech.com/latest/graphicsview.html">Qt Graphics View
framework</a> and the <a href="http://api.kde.org/4.x-api/kdelibs-apidocs/">KDE
libraries</a> provide the underpinning for libplasma. As a result, it should
work anywhere that Qt and KDE do.
Although libplasma is developed for the use of Plasma, the new desktop shell in
KDE 4, it is general enough to be useful in other applications.
<a href="http://amarok.kde.org">Amarok</a> is already using it for its context
view, allowing for pluggable widgets to display and interact with the music
collection, such as "current track" and "tag cloud" widgets.
libplasma itself only provides a framework, and the widgets, containments,
data engines and runners are all implemented as plugins. However, the framework
is designed to make implementing these plugins as easy as possible, including
providing scripting support. Also, infrastructure such as a dialog to install
new widgets and even download them from the web (Plasma::AppletBrowser) is also
included.
Other important classes are:
- Plasma::Corona: the canvas that containments are placed on
- Plasma::View: a QWidget for displaying a containment
- Plasma::Theme: provides theming support
- Plasma::Animator: provides animations for things like elements appearing
and disappearing
- Plasma::Delegate: provides an item delegate for Qt's
<a href="http://doc.trolltech.com/latest/model-view-programming.html">Model /
View framework</a> for menu items.
- Plasma::ToolTipManager: allows widgets have (themed) tooltips displayed when the
mouse is hovered over them
- Plasma::Dialog: displays a themed application dialog
- Plasma::Extender: provides detachable sections to Plasma::Applet
- Plasma::GLApplet: provides an OpneGL-rendered Plasma::Applet
- Plasma::PackageStructure: provides descriptions of packages containing plugins
for libplasma
- Plasma::PopupApplet: provides a simple way of implementing a Plasma::Applet
consisting of an icon that shows a popup when clicked
- Plasma::Svg and Plasma::FrameSvg: provides themable, cached SVGs
- Plasma::Wallpaper: provides pluggable backgrounds for containments
- Plasma::AppletScript, Plasma::DataEngineScript, Plasma::RunnerScript and
Plasma::ScriptEngine: provide scripting interfaces for plugins
- Various themed QGraphicsWidgets for use in creating a Plasma::Applet
The
<a href="http://techbase.kde.org/Development/Tutorials/Plasma">Plasma tutorials</a>
on TechBase provide a good introduction to writing plugins, such as widgets and
data engines, for libplasma-based applications.
@authors
Aaron Seigo \<aseigo@kde.org\><br>
Alessandro Diaferia \<alediaferia@gmail.com\><br>
Alex Merry \<kde@randomguy3.me.uk\><br>
Alexander Wiedenbruch \<wirr01@gmail.com\><br>
Alexis Ménard \<darktears31@gmail.com\><br>
André Duffeck \<andre@duffeck.de\><br>
Andrew Lake \<jamboarder@yahoo.com\><br>
Bertjan Broeksema \<b.broeksema@kdemail.net\><br>
Chani Armitage \<chanika@gmail.com\><br>
Davide Bettio \<davide.bettio@kdemail.net\><br>
Dan Meltzer \<hydrogen@notyetimplemented.com\><br>
Fredrik Höglund \<fredrik@kde.org\><br>
Ivan Cukic \<ivan.cukic+kde@gmail.com\><br>
John Tapsell \<tapsell@kde.org\><br>
Jordi Polo \<mumismo@gmail.com\><br>
Kevin Ottens \<ervin@kde.org\><br>
Montel Laurent \<montel@kde.org\<br>
Marco Martin \<notmart@gmail.com\><br>
Matt Broadstone \<mbroadst@gmail.com\><br>
Petri Damsten \<damu@iki.fi\><br>
Rafael Fernández López \<ereslibre@kde.org\><br>
Riccardo Iaconelli \<riccardo@kde.org\><br>
Richard J. Moore \<rich@kde.org\><br>
Rob Scheepmaker \<r.scheepmaker@student.utwente.nl\><br>
Robert Knight \<robertknight@gmail.com\><br>
Sebastian Kuegler \<sebas@kde.org\><br>
Siraj Razick \<siraj@kde.net\><br>
Zack Rusin \<zack@kde.org\>
@maintainers
Aaron Seigo \<aseigo@kde.org\>
@licenses
@lgpl
In addition, two KDM plugins are provided.
*/
// DOXYGEN_SET_PROJECT_NAME = libplasma
// DOXYGEN_SET_RECURSIVE = YES
// DOXYGEN_SET_PROJECT_NAME = Libraries
// vim:ts=4:sw=4:expandtab:filetype=doxygen

47
kdm/CMakeLists.txt Normal file
View File

@ -0,0 +1,47 @@
include_directories( ${KDEBASE_WORKSPACE_SOURCE_DIR}/kdm/kfrontend )
########### next target ###############
set(kgreet_classic_PART_SRCS kgreet_classic.cpp )
kde4_add_plugin(kgreet_classic ${kgreet_classic_PART_SRCS})
target_link_libraries(kgreet_classic ${KDE4_KDEUI_LIBS})
install(TARGETS kgreet_classic DESTINATION ${PLUGIN_INSTALL_DIR} )
########### next target ###############
set(kgreet_winbind_PART_SRCS kgreet_winbind.cpp )
kde4_add_plugin(kgreet_winbind ${kgreet_winbind_PART_SRCS})
target_link_libraries(kgreet_winbind ${KDE4_KDEUI_LIBS})
install(TARGETS kgreet_winbind DESTINATION ${PLUGIN_INSTALL_DIR} )
########### next target ###############
set(kgreet_generic_PART_SRCS kgreet_generic.cpp )
kde4_add_plugin(kgreet_generic ${kgreet_generic_PART_SRCS})
target_link_libraries(kgreet_generic ${KDE4_KDEUI_LIBS})
install(TARGETS kgreet_generic DESTINATION ${PLUGIN_INSTALL_DIR} )
########### install files ###############
install( FILES kgreeterplugin.h DESTINATION ${INCLUDE_INSTALL_DIR} COMPONENT Devel )

4
kdm/Messages.sh Normal file
View File

@ -0,0 +1,4 @@
#! /usr/bin/env bash
$XGETTEXT kgreet_classic.cpp -o $podir/kgreet_classic.pot
$XGETTEXT kgreet_winbind.cpp -o $podir/kgreet_winbind.pot
$XGETTEXT kgreet_generic.cpp -o $podir/kgreet_generic.pot

484
kdm/kgreet_classic.cpp Normal file
View File

@ -0,0 +1,484 @@
/*
Conversation widget for kdm greeter
Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kgreet_classic.h"
#include "themer/kdmthemer.h"
#include "themer/kdmitem.h"
#include <kglobal.h>
#include <klocale.h>
#include <klineedit.h>
#include <kuser.h>
#include <QRegExp>
#include <QLayout>
#include <QLabel>
static int echoMode;
class KDMPasswordEdit : public KLineEdit {
public:
KDMPasswordEdit( QWidget *parent ) : KLineEdit( parent )
{
if (::echoMode == -1)
setPasswordMode(true);
else
setEchoMode( ::echoMode ? Password : NoEcho );
setContextMenuPolicy( Qt::NoContextMenu );
}
};
KClassicGreeter::KClassicGreeter( KGreeterPluginHandler *_handler,
QWidget *parent,
const QString &_fixedEntity,
Function _func, Context _ctx ) :
QObject(),
KGreeterPlugin( _handler ),
fixedUser( _fixedEntity ),
func( _func ),
ctx( _ctx ),
exp( -1 ),
pExp( -1 ),
running( false )
{
QGridLayout *grid = 0;
int line = 0;
if (!_handler->gplugHasNode( "user-entry" ) ||
!_handler->gplugHasNode( "pw-entry" ))
{
parent = new QWidget( parent );
parent->setObjectName( "talker" );
widgetList << parent;
grid = new QGridLayout( parent );
grid->setMargin( 0 );
}
loginLabel = passwdLabel = passwd1Label = passwd2Label = 0;
loginEdit = 0;
passwdEdit = passwd1Edit = passwd2Edit = 0;
if (ctx == ExUnlock || ctx == ExChangeTok)
fixedUser = KUser().loginName();
if (func != ChAuthTok) {
if (fixedUser.isEmpty()) {
loginEdit = new KLineEdit( parent );
loginEdit->setContextMenuPolicy( Qt::NoContextMenu );
connect( loginEdit, SIGNAL(editingFinished()), SLOT(slotLoginLostFocus()) );
connect( loginEdit, SIGNAL(editingFinished()), SLOT(slotChanged()) );
connect( loginEdit, SIGNAL(textChanged( const QString & )), SLOT(slotChanged()) );
connect( loginEdit, SIGNAL(selectionChanged()), SLOT(slotChanged()) );
if (!grid) {
loginEdit->setObjectName( "user-entry" );
widgetList << loginEdit;
} else {
loginLabel = new QLabel( i18n("&Username:"), parent );
loginLabel->setBuddy( loginEdit );
grid->addWidget( loginLabel, line, 0 );
grid->addWidget( loginEdit, line++, 1 );
}
} else if (ctx != Login && ctx != Shutdown && grid) {
loginLabel = new QLabel( i18n("Username:"), parent );
grid->addWidget( loginLabel, line, 0 );
grid->addWidget( new QLabel( fixedUser, parent ), line++, 1 );
}
passwdEdit = new KDMPasswordEdit( parent );
connect( passwdEdit, SIGNAL(textChanged( const QString & )),
SLOT(slotChanged()) );
connect( passwdEdit, SIGNAL(editingFinished()), SLOT(slotChanged()) );
if (!grid) {
passwdEdit->setObjectName( "pw-entry" );
widgetList << passwdEdit;
} else {
passwdLabel = new QLabel( func == Authenticate ?
i18n("&Password:") :
i18n("Current &password:"),
parent );
passwdLabel->setBuddy( passwdEdit );
grid->addWidget( passwdLabel, line, 0 );
grid->addWidget( passwdEdit, line++, 1 );
}
if (loginEdit)
loginEdit->setFocus();
else
passwdEdit->setFocus();
}
if (func != Authenticate) {
passwd1Edit = new KDMPasswordEdit( parent );
passwd1Label = new QLabel( i18n("&New password:"), parent );
passwd1Label->setBuddy( passwd1Edit );
passwd2Edit = new KDMPasswordEdit( parent );
passwd2Label = new QLabel( i18n("Con&firm password:"), parent );
passwd2Label->setBuddy( passwd2Edit );
if (grid) {
grid->addWidget( passwd1Label, line, 0 );
grid->addWidget( passwd1Edit, line++, 1 );
grid->addWidget( passwd2Label, line, 0 );
grid->addWidget( passwd2Edit, line, 1 );
}
if (!passwdEdit)
passwd1Edit->setFocus();
}
}
// virtual
KClassicGreeter::~KClassicGreeter()
{
abort();
qDeleteAll( widgetList );
}
void // virtual
KClassicGreeter::loadUsers( const QStringList &users )
{
KCompletion *userNamesCompletion = new KCompletion;
userNamesCompletion->setItems( users );
loginEdit->setCompletionObject( userNamesCompletion );
loginEdit->setAutoDeleteCompletionObject( true );
loginEdit->setCompletionMode( KGlobalSettings::CompletionAuto );
}
void // virtual
KClassicGreeter::presetEntity( const QString &entity, int field )
{
loginEdit->setText( entity );
if (field == 1)
passwdEdit->setFocus();
else {
loginEdit->setFocus();
loginEdit->selectAll();
if (field == -1) {
passwdEdit->setText( " " );
passwdEdit->setEnabled( false );
authTok = false;
}
}
curUser = entity;
}
QString // virtual
KClassicGreeter::getEntity() const
{
return fixedUser.isEmpty() ? loginEdit->text() : fixedUser;
}
void // virtual
KClassicGreeter::setUser( const QString &user )
{
// assert( fixedUser.isEmpty() );
curUser = user;
loginEdit->setText( user );
passwdEdit->setFocus();
passwdEdit->selectAll();
}
void // virtual
KClassicGreeter::setEnabled( bool enable )
{
// assert( !passwd1Label );
// assert( func == Authenticate && ctx == Shutdown );
// if (loginLabel)
// loginLabel->setEnabled( enable );
passwdLabel->setEnabled( enable );
setActive( enable );
if (enable)
passwdEdit->setFocus();
}
void // private
KClassicGreeter::returnData()
{
switch (exp) {
case 0:
handler->gplugReturnText( (loginEdit ? loginEdit->text() :
fixedUser).toLocal8Bit(),
KGreeterPluginHandler::IsUser );
break;
case 1:
Q_ASSERT(passwdEdit);
handler->gplugReturnText( passwdEdit->text().toLocal8Bit() ,
KGreeterPluginHandler::IsPassword |
KGreeterPluginHandler::IsSecret );
break;
case 2:
Q_ASSERT(passwd1Edit);
handler->gplugReturnText( passwd1Edit->text().toLocal8Bit(),
KGreeterPluginHandler::IsSecret );
break;
default: // case 3:
Q_ASSERT(passwd2Edit);
handler->gplugReturnText( passwd2Edit->text().toLocal8Bit(),
KGreeterPluginHandler::IsNewPassword |
KGreeterPluginHandler::IsSecret );
break;
}
}
bool // virtual
KClassicGreeter::textMessage( const char *text, bool err )
{
if (!err &&
QString( text ).indexOf( QRegExp( "^Changing password for [^ ]+$" ) ) >= 0)
return true;
return false;
}
void // virtual
KClassicGreeter::textPrompt( const char *prompt, bool echo, bool nonBlocking )
{
pExp = exp;
if (echo)
exp = 0;
else if (!authTok)
exp = 1;
else {
QString pr( prompt );
if (pr.indexOf( QRegExp( "\\bpassword\\b", Qt::CaseInsensitive ) ) >= 0) {
if (pr.indexOf( QRegExp( "\\b(re-?(enter|type)|again|confirm|repeat)\\b",
Qt::CaseInsensitive ) ) >= 0)
exp = 3;
else if (pr.indexOf( QRegExp( "\\bnew\\b", Qt::CaseInsensitive ) ) >= 0)
exp = 2;
else { // QRegExp( "\\b(old|current)\\b", Qt::CaseInsensitive ) is too strict
handler->gplugReturnText( "",
KGreeterPluginHandler::IsOldPassword |
KGreeterPluginHandler::IsSecret );
return;
}
} else {
handler->gplugMsgBox( QMessageBox::Critical,
i18n("Unrecognized prompt \"%1\"",
prompt ) );
handler->gplugReturnText( 0, 0 );
exp = -1;
return;
}
}
if (pExp >= 0 && pExp >= exp) {
revive();
has = -1;
}
if (has >= exp || nonBlocking)
returnData();
}
bool // virtual
KClassicGreeter::binaryPrompt( const char *, bool )
{
// this simply cannot happen ... :}
return true;
}
void // virtual
KClassicGreeter::start()
{
authTok = !(passwdEdit && passwdEdit->isEnabled());
exp = has = -1;
running = true;
}
void // virtual
KClassicGreeter::suspend()
{
}
void // virtual
KClassicGreeter::resume()
{
}
void // virtual
KClassicGreeter::next()
{
// assert( running );
if (loginEdit && loginEdit->hasFocus()) {
passwdEdit->setFocus(); // will cancel running login if necessary
has = 0;
} else if (passwdEdit && passwdEdit->hasFocus()) {
if (passwd1Edit)
passwd1Edit->setFocus();
has = 1;
} else if (passwd1Edit) {
if (passwd1Edit->hasFocus()) {
passwd2Edit->setFocus();
has = 1; // sic!
} else
has = 3;
} else
has = 1;
if (exp < 0)
handler->gplugStart();
else if (has >= exp)
returnData();
}
void // virtual
KClassicGreeter::abort()
{
running = false;
if (exp >= 0) {
exp = -1;
handler->gplugReturnText( 0, 0 );
}
}
void // virtual
KClassicGreeter::succeeded()
{
// assert( running || timed_login );
if (!authTok) {
setActive( false );
if (passwd1Edit) {
authTok = true;
return;
}
} else
setActive2( false );
exp = -1;
running = false;
}
void // virtual
KClassicGreeter::failed()
{
// assert( running || timed_login );
setActive( false );
setActive2( false );
exp = -1;
running = false;
}
void // virtual
KClassicGreeter::revive()
{
// assert( !running );
setActive2( true );
if (authTok) {
passwd1Edit->clear();
passwd2Edit->clear();
passwd1Edit->setFocus();
} else {
passwdEdit->clear();
if (loginEdit && loginEdit->isEnabled())
passwdEdit->setEnabled( true );
else {
setActive( true );
if (loginEdit && loginEdit->text().isEmpty())
loginEdit->setFocus();
else
passwdEdit->setFocus();
}
}
}
void // virtual
KClassicGreeter::clear()
{
// assert( !running && !passwd1Edit );
passwdEdit->clear();
if (loginEdit) {
loginEdit->clear();
loginEdit->setFocus();
curUser.clear();
} else
passwdEdit->setFocus();
}
// private
void
KClassicGreeter::setActive( bool enable )
{
if (loginEdit)
loginEdit->setEnabled( enable );
if (passwdEdit)
passwdEdit->setEnabled( enable );
}
void
KClassicGreeter::setActive2( bool enable )
{
if (passwd1Edit) {
passwd1Edit->setEnabled( enable );
passwd2Edit->setEnabled( enable );
}
}
void
KClassicGreeter::slotLoginLostFocus()
{
if (!running)
return;
if (exp > 0) {
if (curUser == loginEdit->text())
return;
exp = -1;
handler->gplugReturnText( 0, 0 );
}
curUser = loginEdit->text();
handler->gplugSetUser( curUser );
}
void
KClassicGreeter::slotChanged()
{
if (running)
handler->gplugChanged();
}
// factory
static bool init( const QString &,
QVariant (*getConf)( void *, const char *, const QVariant & ),
void *ctx )
{
echoMode = getConf( ctx, "EchoPasswd", QVariant( -1 ) ).toInt();
KGlobal::locale()->insertCatalog( "kgreet_classic" );
return true;
}
static void done( void )
{
KGlobal::locale()->removeCatalog( "kgreet_classic" );
}
static KGreeterPlugin *
create( KGreeterPluginHandler *handler,
QWidget *parent,
const QString &fixedEntity,
KGreeterPlugin::Function func,
KGreeterPlugin::Context ctx )
{
return new KClassicGreeter( handler, parent, fixedEntity, func, ctx );
}
KDE_EXPORT KGreeterPluginInfo kgreeterplugin_info = {
I18N_NOOP2("@item:inmenu authentication method", "Username + password (classic)"), "classic",
KGreeterPluginInfo::Local | KGreeterPluginInfo::Presettable,
init, done, create
};
#include "kgreet_classic.moc"

84
kdm/kgreet_classic.h Normal file
View File

@ -0,0 +1,84 @@
/*
Conversation widget for kdm greeter
Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KGREET_CLASSIC_H
#define KGREET_CLASSIC_H
#include "kgreeterplugin.h"
#include <QObject>
class KLineEdit;
class KSimpleConfig;
class QLabel;
class KClassicGreeter : public QObject, public KGreeterPlugin {
Q_OBJECT
public:
KClassicGreeter( KGreeterPluginHandler *handler,
QWidget *parent,
const QString &fixedEntitiy,
Function func, Context ctx );
~KClassicGreeter();
virtual void loadUsers( const QStringList &users );
virtual void presetEntity( const QString &entity, int field );
virtual QString getEntity() const;
virtual void setUser( const QString &user );
virtual void setEnabled( bool on );
virtual bool textMessage( const char *message, bool error );
virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking );
virtual bool binaryPrompt( const char *prompt, bool nonBlocking );
virtual void start();
virtual void suspend();
virtual void resume();
virtual void next();
virtual void abort();
virtual void succeeded();
virtual void failed();
virtual void revive();
virtual void clear();
public Q_SLOTS:
void slotLoginLostFocus();
void slotChanged();
private:
void setActive( bool enable );
void setActive2( bool enable );
void returnData();
QLabel *loginLabel, *passwdLabel, *passwd1Label, *passwd2Label;
KLineEdit *loginEdit;
KLineEdit *passwdEdit, *passwd1Edit, *passwd2Edit;
KSimpleConfig *stsFile;
QString fixedUser, curUser;
Function func;
Context ctx;
int exp, pExp, has;
bool running, authTok;
};
#endif /* KGREET_CLASSIC_H */

354
kdm/kgreet_generic.cpp Normal file
View File

@ -0,0 +1,354 @@
/*
Conversation widget for kdm greeter
Copyright (C) 2008 Dirk Mueller <mueller@kde.org>
Copyright (C) 2008 Oswald Buddenhagen <ossi@kde.org>
based on classic kdm greeter:
Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kgreet_generic.h"
#include <kglobal.h>
#include <klocale.h>
#include <klineedit.h>
#include <kuser.h>
#include <QLayout>
#include <QLabel>
#include <QTextDocument>
extern KDE_EXPORT KGreeterPluginInfo kgreeterplugin_info; // defined at bottom
static int echoMode;
class KDMPasswordEdit : public KLineEdit {
public:
KDMPasswordEdit( QWidget *parent = 0 ) : KLineEdit( parent )
{
if (::echoMode == -1)
setPasswordMode( true );
else
setEchoMode( ::echoMode ? Password : NoEcho );
setContextMenuPolicy( Qt::NoContextMenu );
}
};
KGenericGreeter::KGenericGreeter( KGreeterPluginHandler *_handler,
QWidget *parent,
const QString &_fixedEntity,
Function _func, Context _ctx ) :
QObject(),
KGreeterPlugin( _handler ),
m_lineEdit( 0 ),
fixedUser( _fixedEntity ),
func( _func ),
ctx( _ctx ),
exp( -1 ),
running( false )
{
m_parentWidget = new QWidget( parent );
m_parentWidget->setObjectName( "talker" );
// XXX set some minimal size
widgetList << m_parentWidget;
m_grid = new QGridLayout( m_parentWidget );
m_grid->setMargin( 0 );
if (ctx == ExUnlock || ctx == ExChangeTok)
fixedUser = KUser().loginName();
}
// virtual
KGenericGreeter::~KGenericGreeter()
{
abort();
delete m_parentWidget;
}
void // virtual
KGenericGreeter::loadUsers( const QStringList &users )
{
m_users = users;
}
void // virtual
KGenericGreeter::presetEntity( const QString &entity, int /* field */ )
{
// assert( !running );
curUser = entity;
}
QString // virtual
KGenericGreeter::getEntity() const
{
return fixedUser.isEmpty() ? curUser : fixedUser;
}
void // virtual
KGenericGreeter::setUser( const QString &user )
{
// assert( running );
// assert( fixedUser.isEmpty() );
if (!(kgreeterplugin_info.flags & KGreeterPluginInfo::Presettable))
return; // Not interested in PAM telling us who logged in
if (exp) {
abort();
start();
}
curUser = user;
if (m_lineEdit) { // could be null if plugin is misconfigured
m_lineEdit->setText( user );
m_lineEdit->selectAll();
m_lineEdit->setFocus();
}
}
void // virtual
KGenericGreeter::setEnabled( bool enable )
{
// assert( func == Authenticate && ctx == Shutdown );
// XXX this is likely to bear some bogosity
foreach (QWidget *w, m_children)
w->setEnabled( enable );
if (enable && m_lineEdit)
m_lineEdit->setFocus();
}
bool // virtual
KGenericGreeter::textMessage( const char *text, bool err )
{
if (err)
return false;
if (m_infoMsgs.isEmpty())
revive();
QString qtext = QString::fromUtf8( text );
m_infoMsgs.append( qtext );
QLabel *label = new QLabel( qtext, m_parentWidget );
m_grid->addWidget( label, m_line++, 0, 1, 2 );
m_children.append( label );
return true;
}
void // virtual
KGenericGreeter::textPrompt( const char *prompt, bool echo, bool /* nonBlocking */ )
{
exp =
exp >= 0 ||
func != Authenticate ||
!(kgreeterplugin_info.flags & KGreeterPluginInfo::Presettable);
if (!exp && !fixedUser.isEmpty()) {
handler->gplugReturnText( fixedUser.toLocal8Bit(),
KGreeterPluginHandler::IsUser );
return;
}
if (m_infoMsgs.isEmpty())
revive();
else
m_infoMsgs.clear();
QLabel *label = new QLabel( QString::fromUtf8( prompt ).trimmed() );
m_grid->addWidget( label, m_line, 0 );
m_children.append( label );
m_echo = echo;
if (echo) {
m_lineEdit = new KLineEdit;
m_lineEdit->setContextMenuPolicy( Qt::NoContextMenu );
if (!exp) {
if (!m_users.isEmpty()) {
KCompletion *userNamesCompletion = new KCompletion;
userNamesCompletion->setItems( m_users );
m_lineEdit->setCompletionObject( userNamesCompletion );
m_lineEdit->setAutoDeleteCompletionObject( true );
m_lineEdit->setCompletionMode( KGlobalSettings::CompletionAuto );
}
if (!curUser.isEmpty()) {
m_lineEdit->setText( curUser );
m_lineEdit->selectAll();
connect( m_lineEdit, SIGNAL(selectionChanged()), SLOT(slotChanged()) );
}
connect( m_lineEdit, SIGNAL(editingFinished()), SLOT(slotLoginLostFocus()) );
}
connect( m_lineEdit, SIGNAL(editingFinished()), SLOT(slotChanged()) );
connect( m_lineEdit, SIGNAL(textChanged( const QString & )), SLOT(slotChanged()) );
} else {
m_lineEdit = new KDMPasswordEdit;
}
m_lineEdit->setMinimumWidth(
m_lineEdit->fontMetrics().width( "This is a long password" ) );
m_grid->addWidget( m_lineEdit, m_line, 1 );
m_children.append( m_lineEdit );
m_lineEdit->show();
m_lineEdit->setFocus();
}
bool // virtual
KGenericGreeter::binaryPrompt( const char *, bool )
{
// FIXME
return true;
}
void // virtual
KGenericGreeter::start()
{
exp = -1;
running = true;
handler->gplugStart();
}
void // virtual
KGenericGreeter::suspend()
{
}
void // virtual
KGenericGreeter::resume()
{
}
void // virtual
KGenericGreeter::next()
{
if (m_lineEdit) {
m_lineEdit->setEnabled( false );
QString text = m_lineEdit->text();
m_lineEdit = 0;
handler->gplugReturnText( text.toLocal8Bit(),
!m_echo ?
KGreeterPluginHandler::IsSecret :
!exp ?
KGreeterPluginHandler::IsUser : 0 );
}
}
void // virtual
KGenericGreeter::abort()
{
running = false;
if (exp >= 0) {
exp = -1;
handler->gplugReturnText( 0, 0 );
}
}
void // virtual
KGenericGreeter::succeeded()
{
failed(); // redefining terms :-D
}
void // virtual
KGenericGreeter::failed()
{
// assert( running || timed_login );
if (!m_infoMsgs.isEmpty()) {
QString text = "<qt>";
foreach (const QString &msg, m_infoMsgs)
text += "<p>" + Qt::escape( msg ) + "</p>";
text += "</qt>";
revive();
handler->gplugMsgBox( QMessageBox::Information, text );
} else {
foreach (QWidget *w, m_children)
w->setEnabled( false );
}
exp = -1;
running = false;
}
void // virtual
KGenericGreeter::revive()
{
// assert( !running );
foreach (QWidget *w, m_children)
w->deleteLater();
m_children.clear();
m_infoMsgs.clear();
m_lineEdit = 0;
m_line = 0;
}
void // virtual
KGenericGreeter::clear()
{
// assert( !running && !passwd1Edit );
revive();
curUser = QString::null;
}
// private
void
KGenericGreeter::slotLoginLostFocus()
{
if (curUser != m_lineEdit->text()) {
curUser = m_lineEdit->text();
handler->gplugSetUser( curUser );
}
}
void
KGenericGreeter::slotChanged()
{
handler->gplugChanged();
}
// factory
static bool init( const QString &,
QVariant (*getConf)( void *, const char *, const QVariant & ),
void *ctx )
{
echoMode = getConf( ctx, "EchoMode", QVariant( -1 ) ).toInt();
// Fielded entities are not supported per se.
// This implies that the first field is the presettable entity, if any.
if (getConf( ctx, "generic.Presettable", QVariant( false ) ).toBool())
kgreeterplugin_info.flags |= KGreeterPluginInfo::Presettable;
KGlobal::locale()->insertCatalog( "kgreet_generic" );
return true;
}
static void done( void )
{
KGlobal::locale()->removeCatalog( "kgreet_generic" );
}
static KGreeterPlugin *
create( KGreeterPluginHandler *handler,
QWidget *parent,
const QString &fixedEntity,
KGreeterPlugin::Function func,
KGreeterPlugin::Context ctx )
{
return new KGenericGreeter( handler, parent, fixedEntity, func, ctx );
}
KDE_EXPORT KGreeterPluginInfo kgreeterplugin_info = {
I18N_NOOP2("@item:inmenu authentication method", "Generic"), "generic",
KGreeterPluginInfo::Local,
init, done, create
};
#include "kgreet_generic.moc"

84
kdm/kgreet_generic.h Normal file
View File

@ -0,0 +1,84 @@
/*
Conversation widget for kdm greeter
Copyright (C) 2008 Dirk Mueller <mueller@kde.org>
Copyright (C) 2008 Oswald Buddenhagen <ossi@kde.org>
based on classic kdm greeter:
Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KGREET_GENERIC_H
#define KGREET_GENERIC_H
#include "kgreeterplugin.h"
#include <QObject>
class QGridLayout;
class QWidget;
class KLineEdit;
class KGenericGreeter : public QObject, public KGreeterPlugin {
Q_OBJECT
public:
KGenericGreeter( KGreeterPluginHandler *handler,
QWidget *parent, const QString &fixedEntitiy,
Function func, Context ctx );
~KGenericGreeter();
virtual void loadUsers( const QStringList &users );
virtual void presetEntity( const QString &entity, int field );
virtual QString getEntity() const;
virtual void setUser( const QString &user );
virtual void setEnabled( bool on );
virtual bool textMessage( const char *message, bool error );
virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking );
virtual bool binaryPrompt( const char *prompt, bool nonBlocking );
virtual void start();
virtual void suspend();
virtual void resume();
virtual void next();
virtual void abort();
virtual void succeeded();
virtual void failed();
virtual void revive();
virtual void clear();
public slots:
void slotLoginLostFocus();
void slotChanged();
private:
QGridLayout *m_grid;
QList<QWidget *> m_children;
KLineEdit *m_lineEdit;
QWidget *m_parentWidget;
QList<QString> m_infoMsgs;
QString fixedUser, curUser;
QStringList m_users;
Function func;
Context ctx;
int exp, m_line;
bool running, m_echo;
};
#endif /* KGREET_GENERIC_H */

627
kdm/kgreet_winbind.cpp Normal file
View File

@ -0,0 +1,627 @@
/*
Conversation widget for kdm greeter
Copyright (C) 1997, 1998, 2000 Steffen Hansen <hansen@kde.org>
Copyright (C) 2000-2004 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "kgreet_winbind.h"
#include "themer/kdmthemer.h"
#include "themer/kdmitem.h"
#include <klocale.h>
#include <kglobal.h>
#include <kdebug.h>
#include <kcombobox.h>
#include <klineedit.h>
#include <kuser.h>
#include <kprocess.h>
#include <QRegExp>
#include <QLayout>
#include <QLabel>
#include <QContextMenuEvent>
#include <QGridLayout>
#include <QTextStream>
#include <stdlib.h>
#include <stdio.h>
static int echoMode;
class KDMPasswordEdit : public KLineEdit {
public:
KDMPasswordEdit( QWidget *parent ) : KLineEdit( parent )
{
if (::echoMode == -1)
setPasswordMode(true);
else
setEchoMode( ::echoMode ? Password : NoEcho );
setContextMenuPolicy( Qt::NoContextMenu );
}
};
static char separator;
static QStringList staticDomains;
static QString defaultDomain;
static void
splitEntity( const QString &ent, QString &dom, QString &usr )
{
int pos = ent.indexOf( separator );
if (pos < 0)
dom = "<local>", usr = ent;
else
dom = ent.left( pos ), usr = ent.mid( pos + 1 );
}
KWinbindGreeter::KWinbindGreeter( KGreeterPluginHandler *_handler,
QWidget *parent,
const QString &_fixedEntity,
Function _func, Context _ctx ) :
QObject(),
KGreeterPlugin( _handler ),
func( _func ),
ctx( _ctx ),
exp( -1 ),
pExp( -1 ),
running( false )
{
QGridLayout *grid = 0;
int line = 0;
if (!_handler->gplugHasNode( "domain-entry" ) ||
!_handler->gplugHasNode( "user-entry" ) ||
!_handler->gplugHasNode( "pw-entry" ))
{
parent = new QWidget( parent );
parent->setObjectName( "talker" );
widgetList << parent;
grid = new QGridLayout( parent );
grid->setMargin( 0 );
}
domainLabel = loginLabel = passwdLabel = passwd1Label = passwd2Label = 0;
domainCombo = 0;
loginEdit = 0;
passwdEdit = passwd1Edit = passwd2Edit = 0;
if (ctx == ExUnlock || ctx == ExChangeTok)
splitEntity( KUser().loginName(), fixedDomain, fixedUser );
else
splitEntity( _fixedEntity, fixedDomain, fixedUser );
if (func != ChAuthTok) {
if (fixedUser.isEmpty()) {
domainCombo = new KComboBox( parent );
connect( domainCombo, SIGNAL(activated( const QString & )),
SLOT(slotChangedDomain( const QString & )) );
connect( domainCombo, SIGNAL(activated( const QString & )),
SLOT(slotLoginLostFocus()) );
connect( domainCombo, SIGNAL(activated( const QString & )),
SLOT(slotChanged()) );
// should handle loss of focus
loginEdit = new KLineEdit( parent );
loginEdit->setContextMenuPolicy( Qt::NoContextMenu );
if (!grid) {
loginEdit->setObjectName( "user-entry" );
domainCombo->setObjectName( "domain-entry" );
widgetList << domainCombo << loginEdit;
} else {
domainLabel = new QLabel( i18n("&Domain:"), parent );
domainLabel->setBuddy( domainCombo );
loginLabel = new QLabel( i18n("&Username:"), parent );
loginLabel->setBuddy( loginEdit );
grid->addWidget( domainLabel, line, 0 );
grid->addWidget( domainCombo, line++, 1 );
grid->addWidget( loginLabel, line, 0 );
grid->addWidget( loginEdit, line++, 1 );
}
connect( loginEdit, SIGNAL(editingFinished()), SLOT(slotLoginLostFocus()) );
connect( loginEdit, SIGNAL(editingFinished()), SLOT(slotChanged()) );
connect( loginEdit, SIGNAL(textChanged( const QString & )), SLOT(slotChanged()) );
connect( loginEdit, SIGNAL(selectionChanged()), SLOT(slotChanged()) );
domainCombo->addItems( staticDomains );
QTimer::singleShot( 0, this, SLOT(slotStartDomainList()) );
} else if (ctx != Login && ctx != Shutdown && grid) {
domainLabel = new QLabel( i18n("Domain:"), parent );
grid->addWidget( domainLabel, line, 0 );
grid->addWidget( new QLabel( fixedDomain, parent ), line++, 1 );
loginLabel = new QLabel( i18n("Username:"), parent );
grid->addWidget( loginLabel, line, 0 );
grid->addWidget( new QLabel( fixedUser, parent ), line++, 1 );
}
passwdEdit = new KDMPasswordEdit( parent );
connect( passwdEdit, SIGNAL(textChanged( const QString & )),
SLOT(slotChanged()) );
connect( passwdEdit, SIGNAL(editingFinished()), SLOT(slotChanged()) );
if (!grid) {
passwdEdit->setObjectName( "pw-entry" );
widgetList << passwdEdit;
} else {
passwdLabel = new QLabel( func == Authenticate ?
i18n("&Password:") :
i18n("Current &password:"),
parent );
passwdLabel->setBuddy( passwdEdit );
grid->addWidget( passwdLabel, line, 0 );
grid->addWidget( passwdEdit, line++, 1 );
}
if (loginEdit)
loginEdit->setFocus();
else
passwdEdit->setFocus();
}
if (func != Authenticate) {
passwd1Edit = new KDMPasswordEdit( parent );
passwd1Label = new QLabel( i18n("&New password:"), parent );
passwd1Label->setBuddy( passwd1Edit );
passwd2Edit = new KDMPasswordEdit( parent );
passwd2Label = new QLabel( i18n("Con&firm password:"), parent );
passwd2Label->setBuddy( passwd2Edit );
if (grid) {
grid->addWidget( passwd1Label, line, 0 );
grid->addWidget( passwd1Edit, line++, 1 );
grid->addWidget( passwd2Label, line, 0 );
grid->addWidget( passwd2Edit, line, 1 );
}
if (!passwdEdit)
passwd1Edit->setFocus();
}
}
// virtual
KWinbindGreeter::~KWinbindGreeter()
{
abort();
qDeleteAll( widgetList );
}
void
KWinbindGreeter::slotChangedDomain( const QString &dom )
{
if (!loginEdit->completionObject())
return;
QStringList users;
if (dom == "<local>") {
for (QStringList::ConstIterator it = allUsers.begin(); it != allUsers.end(); ++it)
if ((*it).indexOf( separator ) < 0)
users << *it;
} else {
QString st( dom + separator );
for (QStringList::ConstIterator it = allUsers.begin(); it != allUsers.end(); ++it)
if ((*it).startsWith( st ))
users << (*it).mid( st.length() );
}
loginEdit->completionObject()->setItems( users );
}
void // virtual
KWinbindGreeter::loadUsers( const QStringList &users )
{
allUsers = users;
KCompletion *userNamesCompletion = new KCompletion;
loginEdit->setCompletionObject( userNamesCompletion );
loginEdit->setAutoDeleteCompletionObject( true );
loginEdit->setCompletionMode( KGlobalSettings::CompletionAuto );
slotChangedDomain( defaultDomain );
}
void // virtual
KWinbindGreeter::presetEntity( const QString &entity, int field )
{
QString dom, usr;
splitEntity( entity, dom, usr );
domainCombo->setCurrentItem( dom, true );
slotChangedDomain( dom );
loginEdit->setText( usr );
if (field > 1)
passwdEdit->setFocus();
else if (field == 1 || field == -1) {
if (field == -1) {
passwdEdit->setText( " " );
passwdEdit->setEnabled( false );
authTok = false;
}
loginEdit->setFocus();
loginEdit->selectAll();
}
curUser = entity;
}
QString // virtual
KWinbindGreeter::getEntity() const
{
QString dom, usr;
if (fixedUser.isEmpty())
dom = domainCombo->currentText(), usr = loginEdit->text();
else
dom = fixedDomain, usr = fixedUser;
return dom == "<local>" ? usr : dom + separator + usr;
}
void // virtual
KWinbindGreeter::setUser( const QString &user )
{
// assert (fixedUser.isEmpty());
curUser = user;
QString dom, usr;
splitEntity( user, dom, usr );
domainCombo->setCurrentItem( dom, true );
slotChangedDomain( dom );
loginEdit->setText( usr );
passwdEdit->setFocus();
passwdEdit->selectAll();
}
void // virtual
KWinbindGreeter::setEnabled( bool enable )
{
// assert( !passwd1Label );
// assert( func == Authenticate && ctx == Shutdown );
// if (domainCombo)
// domainCombo->setEnabled( enable );
// if (loginLabel)
// loginLabel->setEnabled( enable );
passwdLabel->setEnabled( enable );
setActive( enable );
if (enable)
passwdEdit->setFocus();
}
void // private
KWinbindGreeter::returnData()
{
switch (exp) {
case 0:
handler->gplugReturnText( getEntity().toLocal8Bit(),
KGreeterPluginHandler::IsUser );
break;
case 1:
handler->gplugReturnText( passwdEdit->text().toLocal8Bit(),
KGreeterPluginHandler::IsPassword |
KGreeterPluginHandler::IsSecret );
break;
case 2:
handler->gplugReturnText( passwd1Edit->text().toLocal8Bit(),
KGreeterPluginHandler::IsSecret );
break;
default: // case 3:
handler->gplugReturnText( passwd2Edit->text().toLocal8Bit(),
KGreeterPluginHandler::IsNewPassword |
KGreeterPluginHandler::IsSecret );
break;
}
}
bool // virtual
KWinbindGreeter::textMessage( const char *text, bool err )
{
if (!err &&
QString( text ).indexOf( QRegExp( "^Changing password for [^ ]+$" ) ) >= 0)
return true;
return false;
}
void // virtual
KWinbindGreeter::textPrompt( const char *prompt, bool echo, bool nonBlocking )
{
pExp = exp;
if (echo)
exp = 0;
else if (!authTok)
exp = 1;
else {
QString pr( prompt );
if (pr.indexOf( QRegExp( "\\b(old|current)\\b", Qt::CaseInsensitive ) ) >= 0) {
handler->gplugReturnText( "",
KGreeterPluginHandler::IsOldPassword |
KGreeterPluginHandler::IsSecret );
return;
} else if (pr.indexOf( QRegExp( "\\b(re-?(enter|type)|again|confirm|repeat)\\b",
Qt::CaseInsensitive ) ) >= 0)
exp = 3;
else if (pr.indexOf( QRegExp( "\\bnew\\b", Qt::CaseInsensitive ) ) >= 0)
exp = 2;
else {
handler->gplugMsgBox( QMessageBox::Critical,
i18n("Unrecognized prompt \"%1\"",
prompt ) );
handler->gplugReturnText( 0, 0 );
exp = -1;
return;
}
}
if (pExp >= 0 && pExp >= exp) {
revive();
has = -1;
}
if (has >= exp || nonBlocking)
returnData();
}
bool // virtual
KWinbindGreeter::binaryPrompt( const char *, bool )
{
// this simply cannot happen ... :}
return true;
}
void // virtual
KWinbindGreeter::start()
{
authTok = !(passwdEdit && passwdEdit->isEnabled());
exp = has = -1;
running = true;
}
void // virtual
KWinbindGreeter::suspend()
{
}
void // virtual
KWinbindGreeter::resume()
{
}
void // virtual
KWinbindGreeter::next()
{
// assert( running );
if (domainCombo && domainCombo->hasFocus())
loginEdit->setFocus();
else if (loginEdit && loginEdit->hasFocus()) {
passwdEdit->setFocus(); // will cancel running login if necessary
has = 0;
} else if (passwdEdit && passwdEdit->hasFocus()) {
if (passwd1Edit)
passwd1Edit->setFocus();
has = 1;
} else if (passwd1Edit) {
if (passwd1Edit->hasFocus()) {
passwd2Edit->setFocus();
has = 1; // sic!
} else
has = 3;
} else
has = 1;
if (exp < 0)
handler->gplugStart();
else if (has >= exp)
returnData();
}
void // virtual
KWinbindGreeter::abort()
{
running = false;
if (exp >= 0) {
exp = -1;
handler->gplugReturnText( 0, 0 );
}
}
void // virtual
KWinbindGreeter::succeeded()
{
// assert( running || timed_login );
if (!authTok) {
setActive( false );
if (passwd1Edit) {
authTok = true;
return;
}
} else
setActive2( false );
exp = -1;
running = false;
}
void // virtual
KWinbindGreeter::failed()
{
// assert( running || timed_login );
setActive( false );
setActive2( false );
exp = -1;
running = false;
}
void // virtual
KWinbindGreeter::revive()
{
// assert( !running );
setActive2( true );
if (authTok) {
passwd1Edit->clear();
passwd2Edit->clear();
passwd1Edit->setFocus();
} else {
passwdEdit->clear();
if (loginEdit && loginEdit->isEnabled())
passwdEdit->setEnabled( true );
else {
setActive( true );
if (loginEdit && loginEdit->text().isEmpty())
loginEdit->setFocus();
else
passwdEdit->setFocus();
}
}
}
void // virtual
KWinbindGreeter::clear()
{
// assert( !running && !passwd1Edit );
passwdEdit->clear();
if (loginEdit) {
domainCombo->setCurrentItem( defaultDomain );
slotChangedDomain( defaultDomain );
loginEdit->clear();
loginEdit->setFocus();
curUser.clear();
} else
passwdEdit->setFocus();
}
// private
void
KWinbindGreeter::setActive( bool enable )
{
if (domainCombo)
domainCombo->setEnabled( enable );
if (loginEdit)
loginEdit->setEnabled( enable );
if (passwdEdit)
passwdEdit->setEnabled( enable );
}
void
KWinbindGreeter::setActive2( bool enable )
{
if (passwd1Edit) {
passwd1Edit->setEnabled( enable );
passwd2Edit->setEnabled( enable );
}
}
void
KWinbindGreeter::slotLoginLostFocus()
{
if (!running)
return;
QString ent( getEntity() );
if (exp > 0) {
if (curUser == ent)
return;
exp = -1;
handler->gplugReturnText( 0, 0 );
}
curUser = ent;
handler->gplugSetUser( curUser );
}
void
KWinbindGreeter::slotChanged()
{
if (running)
handler->gplugChanged();
}
void
KWinbindGreeter::slotStartDomainList()
{
m_domainLister = new KProcess( this );
(*m_domainLister) << "wbinfo" << "--own-domain" << "--trusted-domains";
m_domainLister->setOutputChannelMode( KProcess::OnlyStdoutChannel );
connect( m_domainLister, SIGNAL(finished( int, QProcess::ExitStatus )),
SLOT(slotEndDomainList()) );
m_domainLister->start();
}
void
KWinbindGreeter::slotEndDomainList()
{
QStringList domainList;
while (!m_domainLister->atEnd()) {
QString dom = m_domainLister->readLine();
dom.chop( 1 );
if (!staticDomains.contains( dom ))
domainList.append( dom );
}
delete m_domainLister;
for (int i = domainCombo->count(), min = staticDomains.count(); --i >= min; ) {
int dli = domainList.indexOf( domainCombo->itemText( i ) );
if (dli < 0) {
if (i == domainCombo->currentIndex())
domainCombo->setCurrentItem( defaultDomain );
domainCombo->removeItem( i );
} else
domainList.removeAt( dli );
}
domainCombo->addItems( domainList );
QTimer::singleShot( 5 * 1000, this, SLOT(slotStartDomainList()) );
}
// factory
static bool init( const QString &,
QVariant (*getConf)( void *, const char *, const QVariant & ),
void *ctx )
{
echoMode = getConf( ctx, "EchoPasswd", QVariant( -1 ) ).toInt();
staticDomains = getConf( ctx, "winbind.Domains", QVariant( "" ) ).toString().split( ':', QString::SkipEmptyParts );
if (!staticDomains.size())
staticDomains << "<local>";
defaultDomain = getConf( ctx, "winbind.DefaultDomain", QVariant( staticDomains.first() ) ).toString();
QString sepstr = getConf( ctx, "winbind.Separator", QVariant( QString() ) ).toString();
if (sepstr.isNull()) {
FILE *sepfile = popen( "wbinfo --separator 2>/dev/null", "r" );
if (sepfile) {
QTextStream( sepfile ) >> sepstr;
if (pclose( sepfile ))
sepstr = "\\";
} else
sepstr = "\\";
}
separator = sepstr[0].toLatin1();
KGlobal::locale()->insertCatalog( "kgreet_winbind" );
return true;
}
static void done( void )
{
KGlobal::locale()->removeCatalog( "kgreet_winbind" );
// avoid static deletion problems ... hopefully
staticDomains.clear();
defaultDomain.clear();
}
static KGreeterPlugin *
create( KGreeterPluginHandler *handler,
QWidget *parent,
const QString &fixedEntity,
KGreeterPlugin::Function func,
KGreeterPlugin::Context ctx )
{
return new KWinbindGreeter( handler, parent, fixedEntity, func, ctx );
}
KDE_EXPORT KGreeterPluginInfo kgreeterplugin_info = {
I18N_NOOP2("@item:inmenu authentication method", "Winbind / Samba"), "classic",
KGreeterPluginInfo::Local | KGreeterPluginInfo::Fielded | KGreeterPluginInfo::Presettable,
init, done, create
};
#include "kgreet_winbind.moc"

94
kdm/kgreet_winbind.h Normal file
View File

@ -0,0 +1,94 @@
/*
Conversation widget for kdm greeter
Copyright (C) 1997, 1998 Steffen Hansen <hansen@kde.org>
Copyright (C) 2000-2003 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef KGREET_WINBIND_H
#define KGREET_WINBIND_H
#include "kgreeterplugin.h"
#include <QObject>
#include <QtCore/QTimer>
class KComboBox;
class KLineEdit;
class KSimpleConfig;
class QLabel;
class KProcess;
class KWinbindGreeter : public QObject, public KGreeterPlugin {
Q_OBJECT
public:
KWinbindGreeter( KGreeterPluginHandler *handler,
QWidget *parent,
const QString &fixedEntitiy,
Function func, Context ctx );
~KWinbindGreeter();
virtual void loadUsers( const QStringList &users );
virtual void presetEntity( const QString &entity, int field );
virtual QString getEntity() const;
virtual void setUser( const QString &user );
virtual void setEnabled( bool on );
virtual bool textMessage( const char *message, bool error );
virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking );
virtual bool binaryPrompt( const char *prompt, bool nonBlocking );
virtual void start();
virtual void suspend();
virtual void resume();
virtual void next();
virtual void abort();
virtual void succeeded();
virtual void failed();
virtual void revive();
virtual void clear();
public Q_SLOTS:
void slotLoginLostFocus();
void slotChangedDomain( const QString &dom );
void slotChanged();
void slotStartDomainList();
void slotEndDomainList();
private:
void setActive( bool enable );
void setActive2( bool enable );
void returnData();
QLabel *domainLabel, *loginLabel, *passwdLabel, *passwd1Label, *passwd2Label;
KComboBox *domainCombo;
KLineEdit *loginEdit;
KLineEdit *passwdEdit, *passwd1Edit, *passwd2Edit;
KSimpleConfig *stsFile;
QString fixedDomain, fixedUser, curUser;
QStringList allUsers;
KProcess* m_domainLister;
Function func;
Context ctx;
int exp, pExp, has;
bool running, authTok;
};
#endif /* KGREET_WINBIND_H */

410
kdm/kgreeterplugin.h Normal file
View File

@ -0,0 +1,410 @@
/*
Authentication method specific conversation plugin for KDE's greeter widgets
Copyright (C) 2003 Oswald Buddenhagen <ossi@kde.org>
Copyright (C) 2003 Fabian Kaiser <xfk@softpro.de>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KGREETERPLUGIN_H
#define KGREETERPLUGIN_H
#include <QtCore/QVariant>
#include <QtGui/QMessageBox>
#include <kdemacros.h>
class QWidget;
class KGreeterPluginHandler {
public:
virtual ~KGreeterPluginHandler() {}
/* keep in sync with V_IS_* */
enum { IsSecret = 1, IsUser = 2, IsPassword = 4, IsOldPassword = 8,
IsNewPassword = 16 };
/**
* Reply to textPrompt().
* @param text text to return to core; null to abort auth cycle
* @param tag zero or one of Is*
*/
virtual void gplugReturnText( const char *text, int tag ) = 0;
/**
* Reply to binaryPrompt().
* @param data data in pam_client format to return to the core;
* null to abort auth cycle
*/
virtual void gplugReturnBinary( const char *data ) = 0;
/**
* Tell the greeter who is logging in.
* Call this preferably before gplugStart, as otherwise the .dmrc
* load will be delayed. Don't call at all if your plugin doesn't
* have the Local flag set. Call only for internally generated
* user changes.
* @param user the user logging in
*/
virtual void gplugSetUser( const QString &user ) = 0;
/**
* Start processing.
*/
virtual void gplugStart() = 0;
/**
* This should be called each time the talker changes in any way from the
* pristine state after an authentication cycle starts, so the greeter
* knows it must reset the fields after some time of inactivity.
*/
virtual void gplugChanged() = 0;
/**
* Plugins that expect user input from a different device than the mouse or
* keyboard must call this when user activity is detected to prevent the
* greeter from resetting/going away. Events should be compressed to no
* more than ten per second; one every five seconds is actually enough.
* Events should be actual changes to the input fields, not random motion.
*/
virtual void gplugActivity() = 0;
/**
* Show a message box on behalf of the talker.
* @param type message severity
* @param text message text
*/
virtual void gplugMsgBox( QMessageBox::Icon type, const QString &text ) = 0;
/**
* Determine if the named widget is welcomed.
* @param id the widget name
*/
virtual bool gplugHasNode( const QString &id ) = 0;
};
/**
* Abstract base class for conversation plugins ("talkers") to be used with
* KDM, kdesktop_lock, etc.
* The authentication method used by a particular instance of a plugin
* may be configurable, but the instance must handle exactly one method,
* i.e., info->method must be determined at the latest at init() time.
*/
class KGreeterPlugin {
public:
KGreeterPlugin( KGreeterPluginHandler *h ) : handler( h ) {}
virtual ~KGreeterPlugin() {}
/**
* Variations of the talker:
* - Authenticate: authentication
* - AuthChAuthTok: authentication and password change
* - ChAuthTok: password change
*/
enum Function { Authenticate, AuthChAuthTok, ChAuthTok };
/**
* Contexts the talker can be used in:
* - Login: kdm login dialog
* - Shutdown: kdm shutdown dialog
* - Unlock: kdm unlock dialog (TODO)
* - ChangeTok: kdm password change dialog (TODO)
* - ExUnlock: kdesktop_lock unlock dialog
* - ExChangeTok: kdepasswd password change dialog (TODO)
*
* The Ex* contexts exist within a running session; the talker must know
* how to obtain the currently logged in user (+ domain/realm, etc.)
* itself (i.e., fixedEntity will be null). The non-Ex variants will have
* a fixedEntity passed in.
*/
enum Context { Login, Shutdown, Unlock, ChangeTok,
ExUnlock, ExChangeTok };
/**
* Provide the talker with a list of selectable users. This can be used
* for autocompletion, etc.
* Will be called only when not running.
* @param users the users to load.
*/
virtual void loadUsers( const QStringList &users ) = 0;
/**
* Preload the talker with an (opaque to the greeter) entity.
* Will be called only when not running.
* @param entity the entity to preload the talker with. That
* will usually be something like "user" or "user@domain".
* @param field the sub-widget (probably line edit) to put the cursor into.
* If -1, preselect the user for timed login. This means pre-filling
* the password field with anything, disabling it, and placing the
* cursor in the user name field.
*/
virtual void presetEntity( const QString &entity, int field ) = 0;
/**
* Obtain the actually logged in entity.
* Will be called only after succeeded() was called.
*/
virtual QString getEntity() const = 0;
/**
* "Push" a user into the talker. That can be a click into the user list
* or successful authentication without the talker calling gplugSetUser.
* Will be called only when running.
* @param user the user to set. Note that this is a UNIX login, not a
* canonical entity
*/
virtual void setUser( const QString &user ) = 0;
/**
* En-/disable any widgets contained in the talker.
* Will be called only when not running.
* @param on the state to set
*/
virtual void setEnabled( bool on ) = 0;
/**
* Called when a message from the authentication backend arrives.
* @param message the message received from the backend
* @param error if true, @p message is an error message, otherwise it's
* an informational message
* @return true means that the talker already handled the message, false
* that the greeter should display it in a message box
*
* FIXME: Filtering a message usually means that the backend issued a
* prompt and obtains the authentication data itself. However, in that
* state the backend is unresponsive, e.g., no shutdown is possible.
* The frontend could send the backend a signal, but the "escape path"
* within the backend is unclear (PAM won't like simply longjmp()ing
* out of it).
*/
virtual bool textMessage( const char *message, bool error ) = 0;
/**
* Prompt the user for data. Reply by calling handler->gplugReturnText().
* @param propmt the prompt to display. It may be null, in which case
* "Username"/"Password" should be shown and the replies should be tagged
* with the respective Is* flag.
* @param echo if true, a normal input widget can be used, otherwise one that
* visually obscures the user's input.
* @param nonBlocking if true, report whatever is already available,
* otherwise wait for user input.
*/
virtual void textPrompt( const char *prompt, bool echo, bool nonBlocking ) = 0;
/**
* Request binary authentication data from the talker. Reply by calling
* handler->gplugReturnBinary().
* @param prompt prompt in pam_client format
* @param nonBlocking if true, report whatever is already available,
* otherwise wait for user input.
* @return always true for now
*
* TODO:
* @return if true, the prompt was handled by the talker, otherwise the
* handler has to use libpam_client to obtain the authentication data.
* In that state the talker still can abort the data fetch by
* gplugReturn()ing a null array. When the data was obtained, another
* binaryPrompt with a null prompt will be issued.
*/
virtual bool binaryPrompt( const char *prompt, bool nonBlocking ) = 0;
/**
* This can either
* - Start a processing cycle. Will be called only when not running.
* - Restart authTok cycle - will be called while running and implies
* revive(). PAM is a bit too clever, so we need this.
* In any case the talker is running afterwards.
*/
virtual void start() = 0;
/**
* Request to suspend the auth. Make sure that a second talker of any
* type will be able to operate while this one is suspended (no busy
* device nodes, etc.).
* Will be called only if running within Login context. (Actually it
* won't be called at all, but be prepared.)
*/
virtual void suspend() = 0;
/**
* Request to resume the auth from the point it was suspended at.
* Will be called only when suspended.
*/
virtual void resume() = 0;
/**
* The "login" button was pressed in the greeter.
* This might call gplugReturn* or gplugStart.
* Will be called only when running.
*/
virtual void next() = 0;
/**
* Abort auth cycle. Note that this should _not_ clear out already
* entered auth tokens if they are still on the screen.
* Will be called only when running and stops it.
*/
virtual void abort() = 0;
/**
* Indicate successful end of the current phase.
* This is more or less a request to disable editable widgets
* responsible for the that phase.
* There will be no further attempt to enter that phase until the
* widget is destroyed.
* Will be called only when running and stops it.
*/
virtual void succeeded() = 0;
/**
* Indicate unsuccessful end of the current phase.
* This is mostly a request to disable all editable widgets.
* The widget will be treated as dead until revive() is called.
* Will be called only when running and stops it.
*/
virtual void failed() = 0;
/**
* Prepare retrying the previously failed phase.
* This is mostly a request to re-enable all editable widgets failed()
* disabled previously, clear the probably incorrect authentication tokens
* and to set the input focus appropriately.
* Will be called only after failed() (possibly with clear() in between),
* or after presetEntity() with field -1.
*/
virtual void revive() = 0;
/**
* Clear any edit widgets, particularly anything set by setUser.
* Will be called only when not running.
*/
virtual void clear() = 0;
typedef QList<QWidget *> WidgetList;
/**
* Obtain the QWidget to actually handle the conversation.
*/
const WidgetList &getWidgets() const { return widgetList; }
protected:
KGreeterPluginHandler *handler;
WidgetList widgetList;
};
struct KDE_EXPORT KGreeterPluginInfo {
/**
* Human readable name of this plugin (should be a little more
* informative than just the libary name). Must be I18N_NOOP()ed.
*/
const char *name;
/**
* The authentication method to use - the meaning is up to the backend,
* but will usually be related to the PAM service.
*/
const char *method;
/**
* Capabilities.
*/
enum {
/**
* All users exist on the local system permanently (will be listed
* by getpwent()); an entity corresponds to a UNIX user.
*/
Local = 1,
/**
* The entities consist of multiple fields.
* PluginOptions/<plugin>.FocusField is used instead of FocusPasswd.
*/
Fielded = 2,
/**
* An entity can be preset, the talker has a widget where a user can
* be selected explicitly. If the method is "classic", timed login
* is possible, too.
* This also means that setUser/gplugSetUser can be used and a
* userlist can be shown at all - provided Local is set as well.
*/
Presettable = 4
};
/*
* Capability flags.
*/
int flags;
/**
* Call after loading the plugin.
*
* @param method if non-empty and the plugin is unable to handle that
* method, return false. If the plugin has a constant method defined
* above, it can ignore this parameter.
* @param getConf can be used to obtain configuration items from the
* greeter; you have to pass it the @p ctx pointer.
* The only predefined key (in KDM) is "EchoMode", which is an int
* (in fact, QLineEdit::EchoModes).
* Other keys are obtained from the PluginOptions option; see kdmrc
* for details.
* If the key is unknown, dflt is returned.
* @param ctx context pointer for @p getConf
* @return if false, unload the plugin again (don't call done() first)
*/
bool (*init)( const QString &method,
QVariant (*getConf)( void *ctx, const char *key,
const QVariant &dflt ),
void *ctx );
/**
* Call before unloading the plugin.
* This pointer can be null.
*/
void (*done)( void );
/**
* Factory method to create an instance of the plugin.
* Note that multiple instances can exist at one time, but only
* one of them is active at any moment (the others would be suspended
* or not running at all).
* @param handler the object offering the necessary callbacks
* @param parent parent widget
* @param predecessor the focus widget before the conversation widget
* @param fixedEntity see below
* @param func see below
* @param ctx see below
* @return an instance of this conversation plugin
*
* Valid combinations of Function and Context:
* - Authenticate:Login - init
* - Authenticate:Shutdown - init, for now "root" is passed as fixedEntitiy
* and it is not supposed to be displayed. Plugins with Local not set
* might have to conjure something up to make getEntity() return a
* canonical entitiy. FIXME: don't restrict shutdown to root.
* - AuthChAuthTok:Login, AuthChAuthTok:Shutdown - cont/cont,
* only relevant for classic method (as it is relevant only for password-
* less logins, which always use classic). The login should not be shown -
* it is known to the user already; the backend won't ask for it, either.
* - ChAuthTok:Login & ChAuthTok:Shutdown - cont
* - Authenticate:Unlock & Authenticate:ExUnlock - init,
* AuthChAuthTok:ChangeTok & AuthChAuthTok:ExChangeTok - init/cont,
* display fixedEntity as labels. The backend does not ask for the UNIX
* login, as it already knows it - but it will ask for all components of
* the entity if it is no UNIX login.
*
* "init" means that the plugin is supposed to call gplugStart, "cont"
* that the backend is already in a cycle of the method the plugin was
* initialized with (it does not hurt if gplugStart is still called).
*/
KGreeterPlugin *(*create)( KGreeterPluginHandler *handler,
QWidget *parent,
const QString &fixedEntity,
KGreeterPlugin::Function func,
KGreeterPlugin::Context ctx );
};
#endif

13
ksysguard/CMakeLists.txt Normal file
View File

@ -0,0 +1,13 @@
########### next target ###############
add_subdirectory( lsofui )
add_subdirectory( processcore )
add_subdirectory( processui )
add_subdirectory( tests )
check_include_files(sys/ptrace.h HAVE_SYS_PTRACE_H)
configure_file(config-ksysguard.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-ksysguard.h )

View File

@ -0,0 +1,8 @@
/* Define to 1 if you have the <sys/ptrace.h> header file. */
#cmakedefine HAVE_SYS_PTRACE_H 1
/* Define to 1 if you have the <sys/endian.h> header file. */
#cmakedefine HAVE_SYS_ENDIAN_H 1
/* Define to 1 if you have the <byteswap.h> header file. */
#cmakedefine HAVE_BYTESWAP_H 1

View File

@ -0,0 +1,33 @@
########### next target ###############
set(lsofui_LIB_SRCS
lsof.cpp
)
kde4_add_ui_files( lsofui_LIB_SRCS
LsofSearchWidget.ui
)
kde4_add_library(lsofui SHARED ${lsofui_LIB_SRCS})
target_link_libraries(lsofui ${KDE4_KIO_LIBS} )
set_target_properties(lsofui PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
install(TARGETS lsofui ${INSTALL_TARGETS_DEFAULT_ARGS} )
########### install files ###############
install( FILES lsof.h DESTINATION ${INCLUDE_INSTALL_DIR}/ksysguard)
set(ksysguardlsofwidgets_PART_SRCS
${CMAKE_CURRENT_BINARY_DIR}/ksysguardlsofwidgets.cpp
)
KDE4_ADD_WIDGET_FILES(${ksysguardlsofwidgets_PART_SRCS} ksysguardlsof.widgets)
kde4_add_plugin(ksysguardlsofwidgets ${ksysguardlsofwidgets_PART_SRCS})
target_link_libraries(ksysguardlsofwidgets ${KDE4_KDECORE_LIBS} ${QT_QTGUI_LIBRARY} lsofui)
install(TARGETS ksysguardlsofwidgets DESTINATION ${PLUGIN_INSTALL_DIR}/plugins/designer )

View File

@ -0,0 +1,46 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 1999 Chris Schlaeger <cs@kde.org>
Copyright (c) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <klocale.h>
#include <kdebug.h>
#include "LsofSearchWidget.moc"
#include "ui_LsofSearchWidget.h"
LsofSearchWidget::LsofSearchWidget(QWidget* parent, int pid )
: KDialog( parent )
{
setObjectName( "Renice Dialog" );
setModal( true );
setCaption( i18n("Renice Process") );
setButtons( Close );
showButtonSeparator( false );
QWidget *widget = new QWidget(this);
setMainWidget(widget);
ui = new Ui_LsofSearchWidget();
ui->setupUi(widget);
ui->klsofwidget->setPid(pid);
ktreewidgetsearchline
}

View File

@ -0,0 +1,45 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 2008 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _LsofSearchWidget_h_
#define _LsofSearchWidget_h_
#include <kdialog.h>
class Ui_LsofSearchWidget;
/**
* This class creates and handles a simple dialog to change the scheduling
* priority of a process.
*/
class LsofSearchWidget : public KDialog
{
Q_OBJECT
public:
LsofSearchWidget(QWidget *parent);
private:
Ui_LsofSearchWidget *ui;
};
#endif

View File

@ -0,0 +1,54 @@
<ui version="4.0" >
<class>KLsofSearchWidget</class>
<widget class="QWidget" name="KLsofSearchWidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>956</width>
<height>686</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<widget class="KTreeWidgetSearchLine" name="ktreewidgetsearchline" />
</item>
<item>
<widget class="KLsofWidget" name="klsofwidget" >
<property name="rootIsDecorated" >
<bool>false</bool>
</property>
<column>
<property name="text" >
<string>Stream</string>
</property>
</column>
<column>
<property name="text" >
<string>Type</string>
</property>
</column>
<column>
<property name="text" >
<string>Filename</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KTreeWidgetSearchLine</class>
<extends>KLineEdit</extends>
<header>ktreewidgetsearchline.h</header>
</customwidget>
<customwidget>
<class>KLsofWidget</class>
<extends>QTreeWidget</extends>
<header>lsof.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

2
ksysguard/lsofui/Messages.sh Executable file
View File

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp -o $podir/ksysguardlsofwidgets.pot

View File

@ -0,0 +1,8 @@
[Global]
PluginName=KSysGuardLsofWidgets
[KLsofWidget]
Group=KSysGuard (KDE)
ConstructorArgs=(parent)
IncludeFile=lsof.h

86
ksysguard/lsofui/lsof.cpp Normal file
View File

@ -0,0 +1,86 @@
#include <QString>
#include <QProcess>
#include <klocale.h>
#include "lsof.h"
struct KLsofWidgetPrivate {
qlonglong pid;
QProcess *process;
};
KLsofWidget::KLsofWidget(QWidget *parent) : QTreeWidget(parent), d(new KLsofWidgetPrivate)
{
d->pid = -1;
setColumnCount(3);
setUniformRowHeights(true);
setRootIsDecorated(false);
setItemsExpandable(false);
setSortingEnabled(true);
setAllColumnsShowFocus(true);
setHeaderLabels(QStringList() << i18nc("Short for File Descriptor", "FD") << i18n("Type") << i18n("Object"));
d->process = new QProcess(this);
connect(d->process, SIGNAL(finished ( int, QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus)));
}
KLsofWidget::~KLsofWidget()
{
delete d;
}
qlonglong KLsofWidget::pid() const
{
return d->pid;
}
void KLsofWidget::setPid(qlonglong pid) {
d->pid = pid;
update();
}
bool KLsofWidget::update()
{
clear();
QStringList args;
d->process->waitForFinished();
args << "-Fftn";
if(d->pid > 0)
args << ("-p" + QString::number(d->pid));
d->process->start("lsof", args);
return true;
}
void KLsofWidget::finished ( int exitCode, QProcess::ExitStatus exitStatus )
{
char buf[1024];
QTreeWidgetItem *process = NULL;
while(true) {
qint64 lineLength = d->process->readLine(buf, sizeof(buf));
if(lineLength <= 0)
break;
if(buf[lineLength-1] == '\n')
lineLength--;
switch(buf[0]) {
/* Process related stuff */
case 'f':
process = new QTreeWidgetItem(this);
process->setText(0,QString::fromUtf8(buf+1, lineLength - 1));
break;
case 't':
if(process)
process->setText(1,QString::fromUtf8(buf+1, lineLength - 1));
break;
case 'n':
if(process)
process->setText(2,QString::fromUtf8(buf+1, lineLength - 1));
break;
default:
break;
}
}
}
#include "lsof.moc"

83
ksysguard/lsofui/lsof.h Normal file
View File

@ -0,0 +1,83 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef LSOFWIDGET_H_
#define LSOFWIDGET_H_
#include <QtCore/QObject>
#include <QtCore/QProcess>
#include <QtGui/QTreeWidget>
#include <kapplication.h>
struct KLsofWidgetPrivate;
class KDE_EXPORT KLsofWidget : public QTreeWidget {
Q_OBJECT
Q_PROPERTY( qlonglong pid READ pid WRITE setPid )
public:
KLsofWidget(QWidget *parent = NULL);
~KLsofWidget();
bool update();
private Q_SLOTS:
/* For QProcess *process */
//void error ( QProcess::ProcessError error );
void finished ( int exitCode, QProcess::ExitStatus exitStatus );
//void readyReadStandardError ();
//void readyReadStandardOutput ();
//void started ();
qlonglong pid() const;
void setPid(qlonglong pid);
private:
KLsofWidgetPrivate* const d;
};
/* class LsofProcessInfo {
public:
pid_t tpid;
int pidst;
pid_t pid;
pid_t ppid;
pid_t pgrp;
int uid;
QString cmd;
QString login;
};
class LsofFileInfo {
QString file_descriptor;
char access;
int file_struct_share_count;
char device_character_code;
long major_minor;
long file_struct_address;
long file_flags;
long inode;
long link_count;
char lock;
long file_struct_node_id;
long file_offset;
QString protocol_name;
QString stream_module;
QString file_type;
QString tcp_info;
};
*/
#endif

View File

@ -0,0 +1,28 @@
########### next target ###############
set(ksysguard_LIB_SRCS
processes.cpp
process.cpp
processes_local_p.cpp
processes_remote_p.cpp
processes_base_p.cpp
)
kde4_add_library(processcore SHARED ${ksysguard_LIB_SRCS})
target_link_libraries(processcore ${KDE4_KDECORE_LIBS} )
if( ${CMAKE_SYSTEM_NAME} MATCHES "NetBSD" )
message(STATUS "Adding kvm library on NetBSD")
target_link_libraries(processcore kvm)
endif( ${CMAKE_SYSTEM_NAME} MATCHES "NetBSD" )
set_target_properties(processcore PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
install(TARGETS processcore ${INSTALL_TARGETS_DEFAULT_ARGS} )
########### install files ###############
install( FILES processes.h process.h DESTINATION ${INCLUDE_INSTALL_DIR}/ksysguard COMPONENT Devel)

View File

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp -o $podir/processcore.pot

View File

@ -0,0 +1,246 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "process.h"
KSysGuard::Process::Process() {
clear();
}
KSysGuard::Process::Process(long long _pid, long long _ppid, Process *_parent) {
clear();
pid = _pid;
parent_pid = _ppid;
parent = _parent;
}
QString KSysGuard::Process::niceLevelAsString() const {
// Just some rough heuristic to map a number to how nice it is
if( niceLevel == 0) return i18nc("Process Niceness", "Normal");
if( niceLevel >= 10) return i18nc("Process Niceness", "Very low priority");
if( niceLevel > 0) return i18nc("Process Niceness", "Low priority");
if( niceLevel <= -10) return i18nc("Process Niceness", "Very high priority");
if( niceLevel < 0) return i18nc("Process Niceness", "High priority");
return QString(); //impossible;
}
QString KSysGuard::Process::ioniceLevelAsString() const {
// Just some rough heuristic to map a number to how nice it is
if( ioniceLevel == 4) return i18nc("Process Niceness", "Normal");
if( ioniceLevel >= 6) return i18nc("Process Niceness", "Very low priority");
if( ioniceLevel > 4) return i18nc("Process Niceness", "Low priority");
if( ioniceLevel <= 2) return i18nc("Process Niceness", "Very high priority");
if( ioniceLevel < 4) return i18nc("Process Niceness", "High priority");
return QString(); //impossible;
}
QString KSysGuard::Process::ioPriorityClassAsString() const {
switch( ioPriorityClass ) {
case None: return i18nc("Priority Class", "None");
case RealTime: return i18nc("Priority Class", "Real Time");
case BestEffort: return i18nc("Priority Class", "Best Effort");
case Idle: return i18nc("Priority Class", "Idle");
default: return i18nc("Priority Class", "Unknown");
}
}
QString KSysGuard::Process::translatedStatus() const {
switch( status ) {
case Running: return i18nc("process status", "running");
case Sleeping: return i18nc("process status", "sleeping");
case DiskSleep: return i18nc("process status", "disk sleep");
case Zombie: return i18nc("process status", "zombie");
case Stopped: return i18nc("process status", "stopped");
case Paging: return i18nc("process status", "paging");
default: return i18nc("process status", "unknown");
}
}
QString KSysGuard::Process::schedulerAsString() const {
switch( scheduler ) {
case Fifo: return i18nc("Scheduler", "FIFO");
case RoundRobin: return i18nc("Scheduler", "Round Robin");
case Batch: return i18nc("Scheduler", "Batch");
default: return QString();
}
}
void KSysGuard::Process::clear() {
pid = 0;
parent_pid = 0;
uid = 0;
gid = -1;
suid = euid = fsuid = -1;
sgid = egid = fsgid = -1;
tracerpid = 0;
userTime = -1;
sysTime = -1;
userUsage=0;
sysUsage=0;
totalUserUsage=0;
totalSysUsage=0;
numChildren=0;
niceLevel=0;
vmSize=0;
vmRSS = 0;
vmURSS = 0;
status=OtherStatus;
parent = NULL;
ioPriorityClass = None;
ioniceLevel = -1;
scheduler = Other;
changes = Process::Nothing;
}
void KSysGuard::Process::setLogin(QString _login) {
if(login == _login) return;
login = _login;
changes |= Process::Login;
}
void KSysGuard::Process::setUid(long long _uid) {
if(uid == _uid) return;
uid = _uid;
changes |= Process::Uids;
}
void KSysGuard::Process::setEuid(long long _euid) {
if(euid == _euid) return;
euid = _euid;
changes |= Process::Uids;
}
void KSysGuard::Process::setSuid(long long _suid) {
if(suid == _suid) return;
suid = _suid;
changes |= Process::Uids;
}
void KSysGuard::Process::setFsuid(long long _fsuid) {
if(fsuid == _fsuid) return;
fsuid = _fsuid;
changes |= Process::Uids;
}
void KSysGuard::Process::setGid(long long _gid) {
if(gid == _gid) return;
gid = _gid;
changes |= Process::Gids;
}
void KSysGuard::Process::setEgid(long long _egid) {
if(egid == _egid) return;
egid = _egid;
changes |= Process::Gids;
}
void KSysGuard::Process::setSgid(long long _sgid) {
if(sgid == _sgid) return;
sgid = _sgid;
changes |= Process::Gids;
}
void KSysGuard::Process::setFsgid(long long _fsgid) {
if(fsgid == _fsgid) return;
fsgid = _fsgid;
changes |= Process::Gids;
}
void KSysGuard::Process::setTracerpid(long long _tracerpid) {
if(tracerpid == _tracerpid) return;
tracerpid = _tracerpid;
changes |= Process::Tracerpid;
}
void KSysGuard::Process::setTty(QByteArray _tty) {
if(tty == _tty) return;
tty = _tty;
changes |= Process::Tty;
}
void KSysGuard::Process::setUserTime(long long _userTime) {
userTime = _userTime;
}
void KSysGuard::Process::setSysTime(long long _sysTime) {
sysTime = _sysTime;
}
void KSysGuard::Process::setUserUsage(int _userUsage) {
if(userUsage == _userUsage) return;
userUsage = _userUsage;
changes |= Process::Usage;
}
void KSysGuard::Process::setSysUsage(int _sysUsage) {
if(sysUsage == _sysUsage) return;
sysUsage = _sysUsage;
changes |= Process::Usage;
}
void KSysGuard::Process::setTotalUserUsage(int _totalUserUsage) {
if(totalUserUsage == _totalUserUsage) return;
totalUserUsage = _totalUserUsage;
changes |= Process::TotalUsage;
}
void KSysGuard::Process::setTotalSysUsage(int _totalSysUsage) {
if(totalSysUsage == _totalSysUsage) return;
totalSysUsage = _totalSysUsage;
changes |= Process::TotalUsage;
}
void KSysGuard::Process::setNiceLevel(int _niceLevel) {
if(niceLevel == _niceLevel) return;
niceLevel = _niceLevel;
changes |= Process::NiceLevels;
}
void KSysGuard::Process::setscheduler(Scheduler _scheduler) {
if(scheduler == _scheduler) return;
scheduler = _scheduler;
changes |= Process::NiceLevels;
}
void KSysGuard::Process::setIoPriorityClass(IoPriorityClass _ioPriorityClass) {
if(ioPriorityClass == _ioPriorityClass) return;
ioPriorityClass = _ioPriorityClass;
changes |= Process::NiceLevels;
}
void KSysGuard::Process::setIoniceLevel(int _ioniceLevel) {
if(ioniceLevel == _ioniceLevel) return;
ioniceLevel = _ioniceLevel;
changes |= Process::NiceLevels;
}
void KSysGuard::Process::setVmSize(long _vmSize) {
if(vmSize == _vmSize) return;
vmSize = _vmSize;
changes |= Process::VmSize;
}
void KSysGuard::Process::setVmRSS(long _vmRSS) {
if(vmRSS == _vmRSS) return;
vmRSS = _vmRSS;
changes |= Process::VmRSS;
}
void KSysGuard::Process::setVmURSS(long _vmURSS) {
if(vmURSS == _vmURSS) return;
vmURSS = _vmURSS;
changes |= Process::VmURSS;
}
void KSysGuard::Process::setName(QString _name) {
if(name == _name) return;
name = _name;
changes |= Process::Name;
}
void KSysGuard::Process::setCommand(QString _command) {
if(command == _command) return;
command = _command;
changes |= Process::Command;
}
void KSysGuard::Process::setStatus(ProcessStatus _status) {
if(status == _status) return;
status = _status;
changes |= Process::Status;
}

View File

@ -0,0 +1,160 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESS_H_
#define PROCESS_H_
#include <kdemacros.h>
#include <QtCore/QList>
#include <QtCore/QTime>
#include <QtCore/QFlags>
#include <klocale.h>
namespace KSysGuard
{
class KDE_EXPORT Process {
public:
enum ProcessStatus { Running, Sleeping, DiskSleep, Zombie, Stopped, Paging, OtherStatus };
enum IoPriorityClass { None, RealTime, BestEffort, Idle };
enum Scheduler { Other = 0, Fifo, RoundRobin, Batch };
Process();
Process(long long _pid, long long _ppid, Process *_parent);
long pid; ///The system's ID for this process. 1 for init. 0 for our virtual 'parent of init' process used just for convience.
long parent_pid; ///The system's ID for the parent of this process. 0 for init.
/** A guaranteed NON-NULL pointer for all real processes to the parent process except for the fake process with pid 0.
* The Parent's pid is the same value as the parent_pid. The parent process will be also pointed
* to by ProcessModel::mPidToProcess to there is no need to worry about mem management in using parent.
* For init process, parent will point to a (fake) process with pid 0 to simplify things.
* For the fake process, this will point to NULL
*/
Process *parent;
void setLogin(QString _login); ///The user login name. Only used for processes on remote machines. Otherwise use uid to get the name
void setUid(long long _uid); ///The user id that the process is running as
void setEuid(long long _euid); ///The effective user id that the process is running as
void setSuid(long long _suid); ///The set user id that the process is running as
void setFsuid(long long _fsuid); ///The file system user id that the process is running as.
void setGid(long long _gid); ///The process group id that the process is running as
void setEgid(long long _egid); ///The effective group id that the process is running as
void setSgid(long long _sgid); ///The set group id that the process is running as
void setFsgid(long long _fsgid); ///The file system group id that the process is running as
void setTracerpid(long long _tracerpid); ///If this is being debugged, this is the process that is debugging it
void setTty(QByteArray _tty); /// The name of the tty the process owns
void setUserTime(long long _userTime); ///The time, in 100ths of a second, spent in total on user calls. -1 if not known
void setSysTime(long long _sysTime); ///The time, in 100ths of a second, spent in total on system calls. -1 if not known
void setUserUsage(int _userUsage); ///Percentage (0 to 100). It might be more than 100% on multiple cpu core systems
void setSysUsage(int _sysUsage); ///Percentage (0 to 100). It might be more than 100% on multiple cpu core systems
void setTotalUserUsage(int _totalUserUsage); ///Percentage (0 to 100) from the sum of itself and all its children recursively. If there's no children, it's equal to userUsage. It might be more than 100% on multiple cpu core systems
void setTotalSysUsage(int _totalSysUsage); ///Percentage (0 to 100) from the sum of itself and all its children recursively. If there's no children, it's equal to sysUsage. It might be more than 100% on multiple cpu core systems
void setNiceLevel(int _niceLevel); ///If Scheduler = Other, niceLevel is the niceness (-20 to 20) of this process. A lower number means a higher priority. Otherwise sched priority (1 to 99)
void setscheduler(Scheduler _scheduler); ///The scheduler this process is running in. See man sched_getscheduler for more info
void setIoPriorityClass(IoPriorityClass _ioPriorityClass); /// The IO priority class. See man ionice for detailed information.
void setIoniceLevel(int _ioniceLevel); ///IO Niceness (0 to 7) of this process. A lower number means a higher io priority. -1 if not known or not applicable because ioPriorityClass is Idle or None
void setVmSize(long _vmSize); ///Virtual memory size in KiloBytes, including memory used, mmap'ed files, graphics memory etc,
void setVmRSS(long _vmRSS); ///Physical memory used by the process and its shared libraries. If the process and libraries are swapped to disk, this could be as low as 0
void setVmURSS(long _vmURSS); ///Physical memory used only by the process, and not counting the code for shared libraries. Set to -1 if unknown
void setName(QString _name); ///The name (e.g. "ksysguard", "konversation", "init")
void setCommand(QString _command); ///The command the process was launched with
void setStatus( ProcessStatus _status); ///Whether the process is running/sleeping/etc
QString login;
long long uid;
long long euid;
long long suid;
long long fsuid;
long long gid;
long long egid;
long long sgid;
long long fsgid;
long long tracerpid;
QByteArray tty;
long long userTime;
long long sysTime;
int userUsage;
int sysUsage;
int totalUserUsage;
int totalSysUsage;
unsigned long numChildren;
int niceLevel;
Scheduler scheduler;
IoPriorityClass ioPriorityClass;
int ioniceLevel;
long vmSize;
long vmRSS;
long vmURSS;
QString name;
QString command;
ProcessStatus status;
QList<Process *> children; ///A list of all the direct children that the process has. Children of children are not listed here, so note that children_pids <= numChildren
QTime timeKillWasSent; /// This is usually a NULL time. When trying to kill a process, this is the time that the kill signal was sent to the process.
QString translatedStatus() const; /// Returns a translated string of the status. e.g. "Running" etc
QString niceLevelAsString() const; /// Returns a simple translated string of the nice priority. e.g. "Normal", "High", etc
QString ioniceLevelAsString() const; /// Returns a simple translated string of the io nice priority. e.g. "Normal", "High", etc
QString ioPriorityClassAsString() const; /// Returns a translated string of the io nice class. i.e. "None", "Real Time", "Best Effort", "Idle"
QString schedulerAsString() const; /// Returns a translated string of the scheduler class. e.g. "FIFO", "Round Robin", "Batch"
int index; /// Each process has a parent process. Each sibling has a unique number to identify it under that parent. This is that number.
/** An enum to keep track of what changed since the last update. Note that we
* the maximum we can use is 0x4000, so some of the enums represent multiple variables
*/
enum Change {
Nothing = 0x0,
Uids = 0x1,
Gids = 0x2,
Tracerpid = 0x4,
Tty = 0x8,
Usage = 0x10,
TotalUsage = 0x20,
NiceLevels = 0x40,
VmSize = 0x80,
VmRSS = 0x100,
VmURSS = 0x200,
Name = 0x400,
Command = 0x800,
Status = 0x1000,
Login = 0x2000
};
Q_DECLARE_FLAGS(Changes, Change)
Changes changes; /** A QFlags representing what has changed */
private:
void clear();
};
Q_DECLARE_OPERATORS_FOR_FLAGS(Process::Changes)
}
#endif

View File

@ -0,0 +1,398 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes.h"
#include "processes_base_p.h"
#include "processes_local_p.h"
#include "processes_remote_p.h"
#include "process.h"
#include <klocale.h>
#include <kglobal.h>
#include <kdebug.h>
#include <QHash>
#include <QSet>
#include <QMutableSetIterator>
#include <QByteArray>
//for sysconf
#include <unistd.h>
/* if porting to an OS without signal.h please #define SIGTERM to something */
#include <signal.h>
namespace KSysGuard
{
Processes::StaticPrivate *Processes::d2 = 0;
class Processes::Private
{
public:
Private() { mAbstractProcesses = 0; mProcesses.insert(0, &mFakeProcess); mElapsedTimeCentiSeconds = -1; ref=1; }
~Private();
QSet<long> mToBeProcessed;
QSet<long> mProcessedLastTime;
QHash<long, Process *> mProcesses; //This must include mFakeProcess at pid 0
QList<Process *> mListProcesses; //A list of the processes. Does not include mFakeProcesses
Process mFakeProcess; //A fake process with pid 0 just so that even init points to a parent
AbstractProcesses *mAbstractProcesses; //The OS specific code to get the process information
QTime mLastUpdated; //This is the time we last updated. Used to calculate cpu usage.
long mElapsedTimeCentiSeconds; //The number of centiseconds (100ths of a second) that passed since the last update
int ref; //Reference counter. When it reaches 0, delete.
};
class Processes::StaticPrivate
{
public:
StaticPrivate() { processesLocal = 0; ref =1; }
Processes *processesLocal;
QHash<QString, Processes*> processesRemote;
int ref; //Reference counter. When it reaches 0, delete.
};
Processes::Private::~Private() {
foreach(Process *process, mProcesses) {
if(process != &mFakeProcess)
delete process;
}
mProcesses.clear();
mListProcesses.clear();
delete mAbstractProcesses;
mAbstractProcesses = NULL;
}
Processes *Processes::getInstance(const QString &host) { //static
if(!d2) {
d2 = new StaticPrivate();
} else {
d2->ref++;
}
if(host.isEmpty()) {
//Localhost processes
if(!d2->processesLocal) {
KGlobal::locale()->insertCatalog("processcore"); //Make sure we include the translation stuff. This needs to be run before any i18n call here
d2->processesLocal = new Processes(new ProcessesLocal());
} else {
d2->processesLocal->d->ref++;
}
return d2->processesLocal;
} else {
Processes *processes = d2->processesRemote.value(host, NULL);
if( !processes ) {
//connect to it
KGlobal::locale()->insertCatalog("processcore"); //Make sure we include the translation stuff. This needs to be run before any i18n call here
ProcessesRemote *remote = new ProcessesRemote(host);
processes = new Processes( remote );
d2->processesRemote.insert(host, processes);
connect(remote, SIGNAL(runCommand(const QString &, int )), processes, SIGNAL(runCommand(const QString&, int)));
} else {
processes->d->ref++;
}
return processes;
}
}
void Processes::returnInstance(const QString &host) { //static
if(!d2) {
kDebug() << "Internal error - static class does not exist";
return;
}
if(host.isEmpty()) {
//Localhost processes
if(!d2->processesLocal) {
//Serious error. Returning instance we don't have.
kDebug() << "Internal error - returning instance we do not have";
return;
} else {
if(--(d2->processesLocal->d->ref) == 0) {
delete d2->processesLocal;
d2->processesLocal = NULL;
}
}
} else {
Processes *processes = d2->processesRemote.value(host, NULL);
if( !processes ) {
kDebug() << "Internal error - returning instance we do not have";
return;
} else {
if(--(processes->d->ref) == 0) {
delete processes;
d2->processesRemote.remove(host);
}
}
}
if(--(d2->ref) == 0) {
delete d2;
d2 = NULL;
}
}
Processes::Processes(AbstractProcesses *abstractProcesses) : d(new Private())
{
d->mAbstractProcesses = abstractProcesses;
connect( abstractProcesses, SIGNAL( processesUpdated() ), SLOT( processesUpdated() ));
}
Processes::~Processes()
{
delete d;
}
Process *Processes::getProcess(long pid) const
{
return d->mProcesses.value(pid);
}
QList<Process *> Processes::getAllProcesses() const
{
return d->mListProcesses;
}
bool Processes::updateProcess( Process *ps, long ppid, bool onlyReparent)
{
Process *parent = d->mProcesses.value(ppid);
Q_ASSERT(parent); //even init has a non-null parent - the mFakeProcess
if(ps->parent != parent) {
emit beginMoveProcess(ps, parent/*new parent*/);
//Processes has been reparented
Process *p = ps;
do {
p = p->parent;
p->numChildren--;
} while (p->pid!= 0);
ps->parent->children.removeAll(ps);
ps->parent = parent; //the parent has changed
parent->children.append(ps);
p = ps;
do {
p = p->parent;
p->numChildren++;
} while (p->pid!= 0);
emit endMoveProcess();
}
if(onlyReparent)
return true;
ps->parent = parent;
ps->parent_pid = ppid;
//Now we can actually get the process info
long oldUserTime = ps->userTime;
long oldSysTime = ps->sysTime;
ps->changes = Process::Nothing;
bool success = d->mAbstractProcesses->updateProcessInfo(ps->pid, ps);
//Now we have the process info. Calculate the cpu usage and total cpu usage for itself and all its parents
if(oldUserTime != -1 && d->mElapsedTimeCentiSeconds!= 0) { //Update the user usage and sys usage
#ifndef Q_OS_NETBSD
ps->setUserUsage((int)(((ps->userTime - oldUserTime)*100.0 + 0.5) / d->mElapsedTimeCentiSeconds));
ps->setSysUsage((int)(((ps->sysTime - oldSysTime)*100.0 + 0.5) / d->mElapsedTimeCentiSeconds));
#endif
ps->setTotalUserUsage(ps->userUsage);
ps->setTotalSysUsage(ps->sysUsage);
if(ps->userUsage != 0 || ps->sysUsage != 0) {
Process *p = ps->parent;
while(p->pid != 0) {
p->totalUserUsage += ps->userUsage;
p->totalSysUsage += ps->sysUsage;
emit processChanged(p, true);
p= p->parent;
}
}
}
emit processChanged(ps, false);
return success;
}
bool Processes::addProcess(long pid, long ppid)
{
Process *parent = d->mProcesses.value(ppid);
if(!parent) return false; //How can this be?
//it's a new process - we need to set it up
Process *ps = new Process(pid, ppid, parent);
emit beginAddProcess(ps);
d->mProcesses.insert(pid, ps);
ps->index = d->mListProcesses.count();
d->mListProcesses.append(ps);
ps->parent->children.append(ps);
Process *p = ps;
do {
p = p->parent;
p->numChildren++;
} while (p->pid!= 0);
ps->parent_pid = ppid;
//Now we can actually get the process info
bool success = d->mAbstractProcesses->updateProcessInfo(pid, ps);
emit endAddProcess();
return success;
}
bool Processes::updateOrAddProcess( long pid)
{
long ppid = d->mAbstractProcesses->getParentPid(pid);
if(d->mToBeProcessed.contains(ppid)) {
//Make sure that we update the parent before we update this one. Just makes things a bit easier.
d->mToBeProcessed.remove(ppid);
d->mProcessedLastTime.remove(ppid); //It may or may not be here - remove it if it is there
updateOrAddProcess(ppid);
}
Process *ps = d->mProcesses.value(pid, 0);
if(!ps)
return addProcess(pid, ppid);
else
return updateProcess(ps, ppid);
}
void Processes::updateAllProcesses( long updateDurationMS )
{
if(d->mElapsedTimeCentiSeconds == -1) {
//First time update has been called
d->mLastUpdated.start();
d->mElapsedTimeCentiSeconds = 0;
} else {
if(d->mLastUpdated.elapsed() < updateDurationMS) //don't update more often than the time given
return;
d->mElapsedTimeCentiSeconds = d->mLastUpdated.restart() / 10;
}
d->mAbstractProcesses->updateAllProcesses();
}
void Processes::processesUpdated() {
d->mToBeProcessed = d->mAbstractProcesses->getAllPids();
QSet<long> beingProcessed(d->mToBeProcessed); //keep a copy so that we can replace mProcessedLastTime with this at the end of this function
long pid;
{
QMutableSetIterator<long> i(d->mToBeProcessed);
while( i.hasNext()) {
pid = i.next();
i.remove();
d->mProcessedLastTime.remove(pid); //It may or may not be here - remove it if it is there
updateOrAddProcess(pid); //This adds the process or changes an extisting one
i.toFront(); //we can remove entries from this set elsewhere, so our iterator might be invalid. reset it back to the start of the set
}
}
{
QMutableSetIterator<long> i(d->mProcessedLastTime);
while( i.hasNext()) {
//We saw these pids last time, but not this time. That means we have to delete them now
pid = i.next();
i.remove();
deleteProcess(pid);
i.toFront();
}
}
d->mProcessedLastTime = beingProcessed; //update the set for next time this function is called
return;
}
void Processes::deleteProcess(long pid)
{
Q_ASSERT(pid > 0);
Process *process = d->mProcesses.value(pid);
foreach( Process *it, process->children) {
d->mProcessedLastTime.remove(it->pid);
deleteProcess(it->pid);
}
emit beginRemoveProcess(process);
d->mProcesses.remove(pid);
d->mListProcesses.removeAll(process);
process->parent->children.removeAll(process);
Process *p = process;
do {
p = p->parent;
p->numChildren--;
} while (p->pid!= 0);
int i=0;
foreach( Process *it, d->mListProcesses ) {
if(it->index > process->index)
it->index--;
Q_ASSERT(it->index == i++);
}
delete process;
emit endRemoveProcess();
}
bool Processes::killProcess(long pid) {
return sendSignal(pid, SIGTERM);
}
bool Processes::sendSignal(long pid, int sig) {
return d->mAbstractProcesses->sendSignal(pid, sig);
}
bool Processes::setNiceness(long pid, int priority) {
return d->mAbstractProcesses->setNiceness(pid, priority);
}
bool Processes::setScheduler(long pid, KSysGuard::Process::Scheduler priorityClass, int priority) {
return d->mAbstractProcesses->setScheduler(pid, priorityClass, priority);
}
bool Processes::setIoNiceness(long pid, KSysGuard::Process::IoPriorityClass priorityClass, int priority) {
return d->mAbstractProcesses->setIoNiceness(pid, priorityClass, priority);
}
bool Processes::supportsIoNiceness() {
return d->mAbstractProcesses->supportsIoNiceness();
}
long long Processes::totalPhysicalMemory() {
return d->mAbstractProcesses->totalPhysicalMemory();
}
long Processes::numberProcessorCores() {
return d->mAbstractProcesses->numberProcessorCores();
}
void Processes::answerReceived( int id, const QList<QByteArray>& answer ) {
KSysGuard::ProcessesRemote *processes = dynamic_cast<KSysGuard::ProcessesRemote *>(d->mAbstractProcesses);
if(processes)
processes->answerReceived(id, answer);
}
}
#include "processes.moc"

View File

@ -0,0 +1,224 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESSES_H_
#define PROCESSES_H_
#include <kdemacros.h>
#include "process.h"
#include <QtCore/QHash>
namespace KSysGuard
{
class AbstractProcesses;
/**
* This class retrieves the processes currently running in an OS independent way.
*
* To use, do something like:
*
* \code
* #include <ksysguard/processes.h>
* #include <ksysguard/process.h>
*
* KSysGuard::Processes *processes = KSysGuard::Processes::getInstance();
* QHash<long, Process *> processlist = processes->getProcesses();
* foreach( Process * process, processlist) {
* kDebug() << "Process with pid " << process->pid << " is called " << process->name;
* }
* KSysGuard::Processes::returnInstance(processes);
* processes = NULL;
* \endcode
*
* @author John Tapsell <tapsell@kde.org>
*/
class KDE_EXPORT Processes : public QObject
{
Q_OBJECT
public:
/**
* Singleton pattern to return the instance associated with @p host.
* Leave as the default for the current machine
*/
static Processes *getInstance(const QString &host = QString());
/**
* Call when you are finished with the Processes pointer from getInstance.
* The pointer from getInstance may not be valid after calling this.
* This is reference counted - once all the instances are returned, the object is deleted
*/
static void returnInstance(const QString &host = QString());
/**
* Update all the process information. After calling this, /proc or equivalent is scanned and
* the signals processChanged, etc are emitted.
*
* Set updateDuration to whatever time period that you update, in milliseconds.
* For example, if you update every 2000ms, set this to 2000. That way it won't update
* more often than needed
*/
void updateAllProcesses(long updateDurationMS = 0);
/**
* Return information for one specific process. call getProcess(0) to get the
* fake process used as the top most parent for all processes.
* This doesn't fetch any new information and so returns almost instantly.
* Call updateAllProcesses() to actually fetch the process information.
*/
Process *getProcess(long pid) const;
/**
* Kill the specified process. You may not have the privillage to kill the process.
* The process may also chose to ignore the command. Send the SIGKILL signal to kill
* the process immediately. You may lose any unsaved data.
*
* @returns Successful or not in killing the process
*/
bool killProcess(long pid);
/**
* Send the specified named POSIX signal to the process given.
*
* For example, to indicate for process 324 to STOP do:
* \code
* #include <signals.h>
* ...
*
* KSysGuard::Processes::sendSignal(23, SIGSTOP);
* \endcode
*
*/
bool sendSignal(long pid, int sig);
/**
* Set the priority for a process. This is from 19 (very nice, lowest priority) to
* -20 (highest priority). The default value for a process is 0.
*
* @return false if you do not have permission to set the priority
*/
bool setNiceness(long pid, int priority);
/**
* Set the scheduler for a process. This is defined according to POSIX.1-2001
* See "man sched_setscheduler" for more information.
*
* @p priorityClass One of SCHED_FIFO, SCHED_RR, SCHED_OTHER, and SCHED_BATCH
* @p priority Set to 0 for SCHED_OTHER and SCHED_BATCH. Between 1 and 99 for SCHED_FIFO and SCHED_RR
* @return false if you do not have permission to set the priority
*/
bool setScheduler(long pid, KSysGuard::Process::Scheduler priorityClass, int priority);
/**
* Set the io priority for a process. This is from 7 (very nice, lowest io priority) to
* 0 (highest priority). The default value is determined as: io_nice = (cpu_nice + 20) / 5.
*
* @return false if you do not have permission to set the priority
*/
bool setIoNiceness(long pid, KSysGuard::Process::IoPriorityClass priorityClass, int priority);
/**
* Returns true if ionice is supported on this system
*/
bool supportsIoNiceness();
/**
* Return the internal pointer of all the processes. The order of the processes
* is guaranteed to never change. Call updateAllProcesses first to actually
* update the information.
*/
QList< Process *> getAllProcesses() const;
/**
* Return the total amount of physical memory in KB. This is fast (just a system call)
* Returns 0 on error
*/
long long totalPhysicalMemory();
/**
* Return the number of processor cores enabled.
* (A system can disable procesors. Disabled processors are not counted here).
* This is fast (just a system call) */
long numberProcessorCores();
public Q_SLOTS:
/** The abstract processes has updated its list of processes */
void processesUpdated();
Q_SIGNALS:
/** The data for a process has changed.
* if @p onlyTotalCpu is set, only the total cpu usage has been updated.
* process->changes contains a bit field indicating what has changed since the last time this was emitted
* for this process
*/
void processChanged( KSysGuard::Process *process, bool onlyTotalCpu);
/**
* This indicates we are about to add a process in the model.
* The process already has the pid, ppid and tree_parent set up.
*/
void beginAddProcess( KSysGuard::Process *process);
/**
* We have finished inserting a process
*/
void endAddProcess();
/**
* This indicates we are about to remove a process in the model. Emit the appropriate signals
*/
void beginRemoveProcess( KSysGuard::Process *process);
/**
* We have finished removing a process
*/
void endRemoveProcess();
/**
* This indicates we are about move a process from one parent to another.
*/
void beginMoveProcess(KSysGuard::Process *process, KSysGuard::Process *new_parent);
/**
* We have finished moving the process
*/
void endMoveProcess();
protected:
Processes(AbstractProcesses *abstractProcesses);
~Processes();
class Private;
Private *d;
class StaticPrivate;
static StaticPrivate *d2;
private:
bool updateOrAddProcess( long pid);
inline void deleteProcess(long pid);
bool updateProcess( Process *process, long ppid, bool onlyReparent = false);
bool addProcess(long pid, long ppid);
Q_SIGNALS:
/** For a remote machine, we rely on being able to communicate with ksysguardd.
* This must be dealt with by the program including this widget. It must listen to our
* 'runCommand' signal, and run the given command, with the given id. */
void runCommand(const QString &command, int id);
public:
/** For a remote machine, we rely on being able to communicate with ksysguardd.
* The programming using this must call this slot when an answer is received from ksysguardd,
* in response to a runCommand request. The id identifies the answer */
void answerReceived( int id, const QList<QByteArray>& answer );
};
}
#endif

View File

@ -0,0 +1,22 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes_base_p.moc"

View File

@ -0,0 +1,139 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESSES_BASE_P_H
#define PROCESSES_BASE_P_H
#include <QSet>
#include <QObject>
namespace KSysGuard
{
class Process;
/**
* This class contains the specific code to get the processes from the given host.
*
* To port this to other operating systems you need to make a processes_(osname).cpp file
* which implements all of the function below. If you need private functions/variables etc put them in
* the Private class.
*
* @author John Tapsell <tapsell@kde.org>
*/
class AbstractProcesses : public QObject
{
Q_OBJECT
public:
AbstractProcesses() {}
virtual ~AbstractProcesses() {}
/**
* To get information about processes, this will be the first function called.
*/
virtual QSet<long> getAllPids() = 0;
/**
* For each of the pids that getAllPids() returns, getParentPid will be called. This is used to setup the tree structure.
* For a particular pid, this is guaranteed to be called before updateProcessInfo for that pid.
* However this may be called several times in a row before the updateProcessInfo is called, so be careful
* if you want to try to preserve state in Private.
*/
virtual long getParentPid(long pid) = 0;
/**
* This will be called for every pid, after getParentPid() has been called for the same parameter.
*
* The process->pid process->ppid and process->parent are all guaranteed to be filled in correctly and process->parent
* will be non null.
*/
virtual bool updateProcessInfo(long pid, Process *process) = 0;
/**
* Send the specified named POSIX signal to the process given.
*
* For example, to indicate for process 324 to STOP do:
* \code
* #include <signals.h>
* ...
*
* KSysGuard::Processes::sendSignal(23, SIGSTOP);
* \endcode
*
*/
virtual bool sendSignal(long pid, int sig) = 0;
/**
* Set the priority for a process. For the normal scheduler, this is usually from 19
* (very nice, lowest priority) to -20 (highest priority). The default value for a process is 0.
*
* This has no effect if the scheduler is not the normal one (SCHED_OTHER)
*
* @return false if you do not have permission to set the priority
*/
virtual bool setNiceness(long pid, int priority) = 0;
/**
* Set the scheduler for a process. This is defined according to POSIX.1-2001
* See "man sched_setscheduler" for more information.
*
* @p priorityClass One of SCHED_FIFO, SCHED_RR, SCHED_OTHER, and SCHED_BATCH
* @p priority Set to 0 for SCHED_OTHER and SCHED_BATCH. Between 1 and 99 for SCHED_FIFO and SCHED_RR
* @return false if you do not have permission to set the priority
*/
virtual bool setScheduler(long pid, int priorityClass, int priority) = 0;
/**
* Return the total amount of physical memory in KB. This is fast (just a system call)
* Returns 0 on error
*/
virtual long long totalPhysicalMemory() = 0;
/**
* Set the io priority for a process. This is from 7 (very nice, lowest io priority) to
* 0 (highest priority). The default value is determined as: io_nice = (cpu_nice + 20) / 5.
*
* @return false if you do not have permission to set the priority
*/
virtual bool setIoNiceness(long pid, int priorityClass, int priority) = 0;
/**
* Returns true if ionice is supported on this system
*/
virtual bool supportsIoNiceness() = 0;
/**
* Return the number of processor cores enabled.
* (A system can disable procesors. Disabled processors are not counted here).
* This is fast (just a system call) */
virtual long numberProcessorCores() = 0;
/**
* Get all the current process information from the machine. When done, emit updateAllProcesses().
*/
virtual void updateAllProcesses() = 0;
Q_SIGNALS:
/**
* This is emitted when the processes have been updated, and the view should be refreshed
*/
void processesUpdated();
};
}
#endif // PROCESSES_BASE_P_H

View File

@ -0,0 +1,307 @@
/* This file is part of the KDE project
Copyright (C) 2007 Manolo Valdes <nolis71cu@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes_local_p.h"
#include "process.h"
#include <klocale.h>
#include <QSet>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/resource.h>
#if defined(__DragonFly__)
#include <sys/resourcevar.h>
#include <err.h>
#endif
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
namespace KSysGuard
{
class ProcessesLocal::Private
{
public:
Private() {;}
~Private() {;}
inline bool readProc(long pid, struct kinfo_proc *p);
inline void readProcStatus(struct kinfo_proc *p, Process *process);
inline void readProcStat(struct kinfo_proc *p, Process *process);
inline void readProcStatm(struct kinfo_proc *p, Process *process);
inline bool readProcCmdline(long pid, Process *process);
};
bool ProcessesLocal::Private::readProc(long pid, struct kinfo_proc *p)
{
int mib[4];
size_t len;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
len = sizeof (struct kinfo_proc);
if (sysctl(mib, 4, p, &len, NULL, 0) == -1 || !len)
return false;
return true;
}
void ProcessesLocal::Private::readProcStatus(struct kinfo_proc *p, Process *process)
{
process->setUid(0);
process->setGid(0);
process->setTracerpid(0);
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
process->setUid(p->ki_uid);
process->setGid(p->ki_pgid);
process->setName(QString(p->ki_comm ? p->ki_comm : "????"));
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
process->setUid(p->kp_uid);
process->setGid(p->kp_pgid);
process->setName(QString(p->kp_comm ? p->kp_comm : "????"));
#else
process->setUid(p->kp_eproc.e_ucred.cr_uid);
process->setGid(p->kp_eproc.e_pgid);
#endif
}
void ProcessesLocal::Private::readProcStat(struct kinfo_proc *p, Process *ps)
{
int status;
struct rusage pru;
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
ps->setUserTime(p->ki_runtime / 10000);
ps->setNiceLevel(p->ki_nice);
ps->setVmSize(p->ki_size);
ps->setVmRSS(p->ki_rssize * getpagesize());
status = p->ki_stat;
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
if (!getrusage(p->kp_pid, &pru)) {
errx(1, "failed to get rusage info");
}
ps->setUserTime(pru.ru_utime.tv_usec / 1000); /*p_runtime / 1000*/
ps->setNiceLevel(p->kp_nice);
ps->setVmSize(p->kp_vm_map_size);
ps->setVmRSS(p->kp_vm_rssize * getpagesize());
status = p->kp_stat;
#else
ps->setUserTime(p->kp_proc.p_rtime.tv_sec*100+p->kp_proc.p_rtime.tv_usec/100);
ps->setNiceLevel(p->kp_proc.p_nice);
ps->setVmSize(p->kp_eproc.e_vm.vm_map.size);
ps->setVmRSS(p->kp_eproc.e_vm.vm_rssize * getpagesize());
status = p->kp_proc.p_stat;
#endif
ps->setSysTime(0);
// "idle","run","sleep","stop","zombie"
switch( status ) {
case '0':
ps->setStatus(Process::DiskSleep);
break;
case '1':
ps->setStatus(Process::Running);
break;
case '2':
ps->setStatus(Process::Sleeping);
break;
case '3':
ps->setStatus(Process::Stopped);
break;
case '4':
ps->setStatus(Process::Zombie);
break;
default:
ps->setStatus(Process::OtherStatus);
break;
}
}
void ProcessesLocal::Private::readProcStatm(struct kinfo_proc *p, Process *process)
{
// TODO
// unsigned long shared;
// process->setVmURSS(process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024));
}
bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
{
int mib[4];
struct kinfo_proc p;
size_t buflen = 256;
char buf[256];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ARGS;
mib[3] = pid;
if (sysctl(mib, 4, buf, &buflen, NULL, 0) == -1 || !buflen)
return false;
QString command = QString(buf);
//cmdline seperates parameters with the NULL character
command = command.replace('\0', ' ');
process->setCommand(command.trimmed());
return true;
}
ProcessesLocal::ProcessesLocal() : d(new Private())
{
}
long ProcessesLocal::getParentPid(long pid) {
long long ppid = 0;
struct kinfo_proc p;
if(d->readProc(pid, &p))
{
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
ppid = p.ki_ppid;
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
ppid = p.kp_ppid;
#else
ppid = p.kp_eproc.e_ppid;
#endif
}
return ppid;
}
bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
{
struct kinfo_proc p;
if(!d->readProc(pid, &p)) return false;
d->readProcStat(&p, process);
d->readProcStatus(&p, process);
d->readProcStatm(&p, process);
if(!d->readProcCmdline(pid, process)) return false;
return true;
}
QSet<long> ProcessesLocal::getAllPids( )
{
QSet<long> pids;
int mib[3];
size_t len;
size_t num;
struct kinfo_proc *p;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ALL;
sysctl(mib, 3, NULL, &len, NULL, 0);
p = (kinfo_proc *) malloc(len);
sysctl(mib, 3, p, &len, NULL, 0);
for (num = 0; num < len / sizeof(struct kinfo_proc); num++)
{
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
long pid = p[num].ki_pid;
long long ppid = p[num].ki_ppid;
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
long pid = p[num].kp_pid;
long long ppid = p[num].kp_ppid;
#else
long pid = p[num].kp_proc.p_pid;
long long ppid = p[num].kp_eproc.e_ppid;
#endif
//skip all process with parent id = 0 but init
if(ppid == 0 && pid != 1)
continue;
pids.insert(pid);
}
free(p);
return pids;
}
bool ProcessesLocal::sendSignal(long pid, int sig) {
if ( kill( (pid_t)pid, sig ) ) {
//Kill failed
return false;
}
return true;
}
bool ProcessesLocal::setNiceness(long pid, int priority) {
if ( setpriority( PRIO_PROCESS, pid, priority ) ) {
//set niceness failed
return false;
}
return true;
}
bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
{
if(priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
priority = 0;
if(pid <= 0) return false; // check the parameters
struct sched_param params;
params.sched_priority = priority;
switch(priorityClass) {
case (KSysGuard::Process::Other):
return (sched_setscheduler( pid, SCHED_OTHER, &params) == 0);
case (KSysGuard::Process::RoundRobin):
return (sched_setscheduler( pid, SCHED_RR, &params) == 0);
case (KSysGuard::Process::Fifo):
return (sched_setscheduler( pid, SCHED_FIFO, &params) == 0);
#ifdef SCHED_BATCH
case (KSysGuard::Process::Batch):
return (sched_setscheduler( pid, SCHED_BATCH, &params) == 0);
#endif
default:
return false;
}
}
bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
return false; //Not yet supported
}
bool ProcessesLocal::supportsIoNiceness() {
return false;
}
long long ProcessesLocal::totalPhysicalMemory() {
size_t Total;
size_t len;
len = sizeof (Total);
sysctlbyname("hw.physmem", &Total, &len, NULL, 0);
return Total /= 1024;
}
ProcessesLocal::~ProcessesLocal()
{
delete d;
}
}

View File

@ -0,0 +1,517 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes_local_p.h"
#include "process.h"
#include <klocale.h>
#include <QFile>
#include <QHash>
#include <QSet>
#include <QByteArray>
#include <QTextStream>
//for sysconf
#include <unistd.h>
//for kill and setNice
#include <sys/types.h>
#include <signal.h>
#include <sys/resource.h>
#include <dirent.h>
#include <stdlib.h>
//for ionice
#include <sys/ptrace.h>
#include <asm/unistd.h>
//for getsched
#include <sched.h>
#define PROCESS_BUFFER_SIZE 1000
/* For ionice */
extern int sys_ioprio_set(int, int, int);
extern int sys_ioprio_get(int, int);
#define HAVE_IONICE
/* Check if this system has ionice */
#if !defined(SYS_ioprio_get) || !defined(SYS_ioprio_set)
/* All new kernels have SYS_ioprio_get and _set defined, but for the few that do not, here are the definitions */
#if defined(__i386__)
#define __NR_ioprio_set 289
#define __NR_ioprio_get 290
#elif defined(__ppc__) || defined(__powerpc__)
#define __NR_ioprio_set 273
#define __NR_ioprio_get 274
#elif defined(__x86_64__)
#define __NR_ioprio_set 251
#define __NR_ioprio_get 252
#elif defined(__ia64__)
#define __NR_ioprio_set 1274
#define __NR_ioprio_get 1275
#else
#ifdef __GNUC__
#warning "This architecture does not support IONICE. Disabling ionice feature."
#endif
#undef HAVE_IONICE
#endif
/* Map these to SYS_ioprio_get */
#define SYS_ioprio_get __NR_ioprio_get
#define SYS_ioprio_set __NR_ioprio_set
#endif /* !SYS_ioprio_get */
/* Set up ionice functions */
#ifdef HAVE_IONICE
#define IOPRIO_WHO_PROCESS 1
#define IOPRIO_CLASS_SHIFT 13
/* Expose the kernel calls to usespace via syscall
* See man ioprio_set and man ioprio_get for information on these functions */
static int ioprio_set(int which, int who, int ioprio)
{
return syscall(SYS_ioprio_set, which, who, ioprio);
}
static int ioprio_get(int which, int who)
{
return syscall(SYS_ioprio_get, which, who);
}
#endif
namespace KSysGuard
{
class ProcessesLocal::Private
{
public:
Private() { mProcDir = opendir( "/proc" );}
~Private();
inline bool readProcStatus(long pid, Process *process);
inline bool readProcStat(long pid, Process *process);
inline bool readProcStatm(long pid, Process *process);
inline bool readProcCmdline(long pid, Process *process);
inline bool getNiceness(long pid, Process *process);
QFile mFile;
char mBuffer[PROCESS_BUFFER_SIZE+1]; //used as a buffer to read data into
DIR* mProcDir;
};
ProcessesLocal::Private::~Private()
{
closedir(mProcDir);
}
ProcessesLocal::ProcessesLocal() : d(new Private())
{
}
bool ProcessesLocal::Private::readProcStatus(long pid, Process *process)
{
mFile.setFileName(QString("/proc/%1/status").arg(pid));
if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
return false; /* process has terminated in the meantime */
process->uid = 0;
process->gid = 0;
process->tracerpid = 0;
int size;
int found = 0; //count how many fields we found
while( (size = mFile.readLine( mBuffer, sizeof(mBuffer))) > 0) { //-1 indicates an error
switch( mBuffer[0]) {
case 'N':
if((unsigned int)size > sizeof("Name:") && qstrncmp(mBuffer, "Name:", sizeof("Name:")-1) == 0) {
process->name = QString::fromLocal8Bit(mBuffer + sizeof("Name:")-1, size-sizeof("Name:")+1).trimmed();
if(++found == 4) goto finish;
}
break;
case 'U':
if((unsigned int)size > sizeof("Uid:") && qstrncmp(mBuffer, "Uid:", sizeof("Uid:")-1) == 0) {
sscanf(mBuffer + sizeof("Uid:") -1, "%Ld %Ld %Ld %Ld", &process->uid, &process->euid, &process->suid, &process->fsuid );
if(++found == 4) goto finish;
}
break;
case 'G':
if((unsigned int)size > sizeof("Gid:") && qstrncmp(mBuffer, "Gid:", sizeof("Gid:")-1) == 0) {
sscanf(mBuffer + sizeof("Gid:")-1, "%Ld %Ld %Ld %Ld", &process->gid, &process->egid, &process->sgid, &process->fsgid );
if(++found == 4) goto finish;
}
break;
case 'T':
if((unsigned int)size > sizeof("TracerPid:") && qstrncmp(mBuffer, "TracerPid:", sizeof("TracerPid:")-1) == 0) {
process->tracerpid = atol(mBuffer + sizeof("TracerPid:")-1);
if(++found == 4) goto finish;
}
break;
default:
break;
}
}
finish:
mFile.close();
return true;
}
long ProcessesLocal::getParentPid(long pid) {
Q_ASSERT(pid != 0);
d->mFile.setFileName(QString("/proc/%1/stat").arg(pid));
if(!d->mFile.open(QIODevice::ReadOnly | QIODevice::Text))
return 0; /* process has terminated in the meantime */
int size; //amount of data read in
if( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) <= 0) { //-1 indicates nothing read
d->mFile.close();
return 0;
}
d->mFile.close();
int current_word = 0;
char *word = d->mBuffer;
while(true) {
if(word[0] == ' ' ) {
if(++current_word == 3)
break;
} else if(word[0] == 0) {
return 0; //end of data - serious problem
}
word++;
}
return atol(++word);
}
bool ProcessesLocal::Private::readProcStat(long pid, Process *ps)
{
QString filename = QString("/proc/%1/stat").arg(pid);
// As an optomization, if the last file read in was stat, then we already have this info in memory
if(mFile.fileName() != filename) {
mFile.setFileName(filename);
if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
return false; /* process has terminated in the meantime */
if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
mFile.close();
return false;
}
mFile.close();
}
int current_word = 0; //count from 0
char *word = mBuffer;
char status='\0';
long vmSize = 0;
long vmRSS = 0;
while(current_word < 23) {
if(word[0] == ' ' ) {
++current_word;
switch(current_word) {
case 2: //status
status=word[1]; // Look at the first letter of the status.
// We analyze this after the while loop
break;
case 6: //ttyNo
{
int ttyNo = atoi(word+1);
int major = ttyNo >> 8;
int minor = ttyNo & 0xff;
switch(major) {
case 136:
ps->setTty(QByteArray("pts/") + QByteArray::number(minor));
break;
case 5:
ps->setTty(QByteArray("tty"));
case 4:
if(minor < 64)
ps->setTty(QByteArray("tty") + QByteArray::number(minor));
else
ps->setTty(QByteArray("ttyS") + QByteArray::number(minor-64));
break;
default:
ps->setTty(QByteArray());
}
}
break;
case 13: //userTime
ps->setUserTime(atoll(word+1));
break;
case 14: //sysTime
ps->setSysTime(atoll(word+1));
break;
case 18: //niceLevel
ps->setNiceLevel(atoi(word+1)); /*Or should we use getPriority instead? */
break;
case 22: //vmSize
vmSize = atol(word+1);
break;
case 23: //vmRSS
vmRSS = atol(word+1);
break;
default:
break;
}
} else if(word[0] == 0) {
return false; //end of data - serious problem
}
word++;
}
/* There was a "(ps->vmRss+3) * sysconf(_SC_PAGESIZE)" here in the original ksysguard code. I have no idea why! After comparing it to
* meminfo and other tools, this means we report the RSS by 12 bytes differently compared to them. So I'm removing the +3
* to be consistent. NEXT TIME COMMENT STRANGE THINGS LIKE THAT! :-) */
ps->setVmRSS(vmRSS * sysconf(_SC_PAGESIZE) / 1024); /*convert to KiB*/
ps->setVmSize(vmSize /= 1024); /* convert to KiB */
switch( status) {
case 'R':
ps->setStatus(Process::Running);
break;
case 'S':
ps->setStatus(Process::Sleeping);
break;
case 'D':
ps->setStatus(Process::DiskSleep);
break;
case 'Z':
ps->setStatus(Process::Zombie);
break;
case 'T':
ps->setStatus(Process::Stopped);
break;
case 'W':
ps->setStatus(Process::Paging);
break;
default:
ps->setStatus(Process::OtherStatus);
break;
}
return true;
}
bool ProcessesLocal::Private::readProcStatm(long pid, Process *process)
{
mFile.setFileName(QString("/proc/%1/statm").arg(pid));
if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
return false; /* process has terminated in the meantime */
if( mFile.readLine( mBuffer, sizeof(mBuffer)) <= 0) { //-1 indicates nothing read
mFile.close();
return 0;
}
mFile.close();
int current_word = 0;
char *word = mBuffer;
while(true) {
if(word[0] == ' ' ) {
if(++current_word == 2) //number of pages that are shared
break;
} else if(word[0] == 0) {
return false; //end of data - serious problem
}
word++;
}
long shared = atol(word+1);
/* we use the rss - shared to find the amount of memory just this app uses */
process->vmURSS = process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024);
return true;
}
bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
{
if(!process->command.isNull()) return true; //only parse the cmdline once
mFile.setFileName(QString("/proc/%1/cmdline").arg(pid));
if(!mFile.open(QIODevice::ReadOnly | QIODevice::Text))
return false; /* process has terminated in the meantime */
QTextStream in(&mFile);
process->command = in.readAll();
//cmdline seperates parameters with the NULL character
process->command.replace('\0', ' ');
process->command = process->command.trimmed();
mFile.close();
return true;
}
bool ProcessesLocal::Private::getNiceness(long pid, Process *process) {
int sched = sched_getscheduler(pid);
switch(sched) {
case (SCHED_OTHER):
process->scheduler = KSysGuard::Process::Other;
break;
case (SCHED_RR):
process->scheduler = KSysGuard::Process::RoundRobin;
break;
case (SCHED_FIFO):
process->scheduler = KSysGuard::Process::Fifo;
break;
#ifdef SCHED_BATCH
case (SCHED_BATCH):
process->scheduler = KSysGuard::Process::Batch;
break;
#endif
default:
process->scheduler = KSysGuard::Process::Other;
}
if(sched == SCHED_FIFO || sched == SCHED_RR) {
struct sched_param param;
if(sched_getparam(pid, &param) == 0)
process->niceLevel = param.sched_priority;
else
process->niceLevel = 0; //Error getting scheduler parameters.
}
#ifdef HAVE_IONICE
int ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); /* Returns from 0 to 7 for the iopriority, and -1 if there's an error */
if(ioprio == -1) {
process->ioniceLevel = -1;
process->ioPriorityClass = KSysGuard::Process::None;
return false; /* Error. Just give up. */
}
process->ioniceLevel = ioprio & 0xff; /* Bottom few bits are the priority */
process->ioPriorityClass = (KSysGuard::Process::IoPriorityClass)(ioprio >> IOPRIO_CLASS_SHIFT); /* Top few bits are the class */
#else
return false; /* Do nothing, if we do not support this architecture */
#endif
return true;
}
bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
{
if(!d->readProcStat(pid, process)) return false;
if(!d->readProcStatus(pid, process)) return false;
if(!d->readProcStatm(pid, process)) return false;
if(!d->readProcCmdline(pid, process)) return false;
if(!d->getNiceness(pid, process)) return false;
return true;
}
QSet<long> ProcessesLocal::getAllPids( )
{
QSet<long> pids;
if(d->mProcDir==NULL) return pids; //There's not much we can do without /proc
struct dirent* entry;
rewinddir(d->mProcDir);
while ( ( entry = readdir( d->mProcDir ) ) )
if ( entry->d_name[ 0 ] >= '0' && entry->d_name[ 0 ] <= '9' )
pids.insert(atol( entry->d_name ));
return pids;
}
bool ProcessesLocal::sendSignal(long pid, int sig) {
if ( kill( (pid_t)pid, sig ) ) {
//Kill failed
return false;
}
return true;
}
bool ProcessesLocal::setNiceness(long pid, int priority) {
if(pid <= 0) return false; // check the parameters
if ( setpriority( PRIO_PROCESS, pid, priority ) ) {
//set niceness failed
return false;
}
return true;
}
bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority) {
if(priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
priority = 0;
if(pid <= 0) return false; // check the parameters
struct sched_param params;
params.sched_priority = priority;
switch(priorityClass) {
case (KSysGuard::Process::Other):
return (sched_setscheduler( pid, SCHED_OTHER, &params) == 0);
case (KSysGuard::Process::RoundRobin):
return (sched_setscheduler( pid, SCHED_RR, &params) == 0);
case (KSysGuard::Process::Fifo):
return (sched_setscheduler( pid, SCHED_FIFO, &params) == 0);
#ifdef SCHED_BATCH
case (KSysGuard::Process::Batch):
return (sched_setscheduler( pid, SCHED_BATCH, &params) == 0);
#endif
default:
return false;
}
}
bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
#ifdef HAVE_IONICE
if (ioprio_set(IOPRIO_WHO_PROCESS, pid, priority | priorityClass << IOPRIO_CLASS_SHIFT) == -1) {
//set io niceness failed
return false;
}
return true;
#else
return false;
#endif
}
bool ProcessesLocal::supportsIoNiceness() {
#ifdef HAVE_IONICE
return true;
#else
return false;
#endif
}
long long ProcessesLocal::totalPhysicalMemory() {
//Try to get the memory via sysconf. Note the cast to long long to try to avoid a long overflow
//Should we use sysconf(_SC_PAGESIZE) or getpagesize() ?
long long memory = ((long long)sysconf(_SC_PHYS_PAGES)) * (sysconf(_SC_PAGESIZE)/1024);
if(memory > 0) return memory;
//This is backup code incase the above failed. It should never fail on a linux system.
d->mFile.setFileName("/proc/meminfo");
if(!d->mFile.open(QIODevice::ReadOnly | QIODevice::Text))
return 0;
int size;
while( (size = d->mFile.readLine( d->mBuffer, sizeof(d->mBuffer))) > 0) { //-1 indicates an error
switch( d->mBuffer[0]) {
case 'M':
if((unsigned int)size > sizeof("MemTotal:") && qstrncmp(d->mBuffer, "MemTotal:", sizeof("MemTotal:")-1) == 0) {
d->mFile.close();
return atoll(d->mBuffer + sizeof("MemTotal:")-1);
}
}
}
return 0; // Not found. Probably will never happen
}
ProcessesLocal::~ProcessesLocal()
{
delete d;
}
}

View File

@ -0,0 +1,37 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#if defined __linux__
#include "processes_linux_p.cpp"
#elif defined __FreeBSD__ || defined __DragonFly__
#include "processes_freebsd_p.cpp"
#elif defined __OpenBSD__
#include "processes_openbsd_p.cpp"
#elif defined __NetBSD__
#include "processes_netbsd_p.cpp"
#else
// Use Qt's OS detection
#include <qglobal.h>
#ifdef Q_OS_SOLARIS
#include "processes_solaris_p.cpp"
#endif
#endif

View File

@ -0,0 +1,70 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESSES_LOCAL_H_
#define PROCESSES_LOCAL_H_
#include "processes_base_p.h"
#include <unistd.h> //For sysconf
#include <QSet>
namespace KSysGuard
{
class Process;
/**
* This is the OS specific code to get process information for the local host.
*/
class ProcessesLocal : public AbstractProcesses {
public:
ProcessesLocal();
virtual ~ProcessesLocal();
virtual QSet<long> getAllPids();
virtual long getParentPid(long pid);
virtual bool updateProcessInfo(long pid, Process *process);
virtual bool sendSignal(long pid, int sig);
virtual bool setNiceness(long pid, int priority);
virtual bool setScheduler(long pid, int priorityClass, int priority);
virtual long long totalPhysicalMemory();
virtual bool setIoNiceness(long pid, int priorityClass, int priority);
virtual bool supportsIoNiceness();
virtual long numberProcessorCores()
#ifdef _SC_NPROCESSORS_ONLN
{ return sysconf(_SC_NPROCESSORS_ONLN); } // Should work on any recent posix system
#else
;
#endif
virtual void updateAllProcesses() { emit processesUpdated(); } //For local machine, there is no delay
private:
/**
* You can use this for whatever data you want. Be careful about preserving state in between getParentPid and updateProcessInfo calls
* if you chose to do that. getParentPid may be called several times for different pids before the relevant updateProcessInfo calls are made.
* This is because the tree structure has to be sorted out first.
*/
class Private;
Private *d;
};
}
#endif

View File

@ -0,0 +1,292 @@
/* This file is part of the KDE project
Copyright (C) 2007 Manolo Valdes <nolis71cu@gmail.com>
Copyright (C) 2007 Mark Davies <mark@mcs.vuw.ac.nz>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes_local_p.h"
#include "process.h"
#include <klocale.h>
#include <QSet>
#include <kvm.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
namespace KSysGuard
{
class ProcessesLocal::Private
{
public:
Private() { kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open");}
~Private() { kvm_close(kd);}
inline bool readProc(long pid, struct kinfo_proc2 **p, int *num);
inline void readProcStatus(struct kinfo_proc2 *p, Process *process);
inline void readProcStat(struct kinfo_proc2 *p, Process *process);
inline void readProcStatm(struct kinfo_proc2 *p, Process *process);
inline bool readProcCmdline(struct kinfo_proc2 *p, Process *process);
kvm_t *kd;
};
#ifndef _SC_NPROCESSORS_ONLN
long int KSysGuard::ProcessesLocal::numberProcessorCores()
{
int mib[2];
int ncpu;
size_t len;
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
len = sizeof(ncpu);
if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1 || !len)
return 1;
return len;
}
#endif
bool ProcessesLocal::Private::readProc(long pid, struct kinfo_proc2 **p, int *num)
{
int len;
int op, arg;
if (pid == 0) {
op = KERN_PROC_ALL;
arg = 0;
} else {
op = KERN_PROC_PID;
arg = pid;
}
*p = kvm_getproc2(kd, op, arg, sizeof(struct kinfo_proc2), &len);
if (len < 1)
return false;
if (num != NULL)
*num = len;
return true;
}
void ProcessesLocal::Private::readProcStatus(struct kinfo_proc2 *p, Process *process)
{
process->setUid(p->p_ruid);
process->setEuid(p->p_uid);
process->setGid(p->p_rgid);
process->setEgid(p->p_gid);
process->setTracerpid(0);
process->setName(QString(p->p_comm ? p->p_comm : "????"));
}
void ProcessesLocal::Private::readProcStat(struct kinfo_proc2 *p, Process *ps)
{
const char *ttname;
dev_t dev;
ps->setUserTime(p->p_uutime_sec*100+p->p_uutime_usec/10000);
ps->setSysTime(p->p_ustime_sec*100+p->p_ustime_usec/10000);
ps->setUserUsage(100.0 * ((double)(p->p_pctcpu) / FSCALE));
ps->setSysUsage(0);
ps->setNiceLevel(p->p_nice - NZERO);
ps->setVmSize((p->p_vm_tsize + p->p_vm_dsize + p->p_vm_ssize)
* getpagesize());
ps->setVmRSS(p->p_vm_rssize * getpagesize());
// "idle","run","sleep","stop","zombie"
switch( p->p_stat ) {
case LSRUN:
ps->setStatus(Process::Running);
break;
case LSSLEEP:
ps->setStatus(Process::Sleeping);
break;
case LSSTOP:
ps->setStatus(Process::Stopped);
break;
case LSZOMB:
ps->setStatus(Process::Zombie);
break;
case LSONPROC:
ps->setStatus(Process::Running);
break;
default:
ps->setStatus(Process::OtherStatus);
break;
}
dev = p->p_tdev;
if (dev == NODEV || (ttname = devname(dev, S_IFCHR)) == NULL) {
ps->setTty(QByteArray());
} else {
ps->setTty(QByteArray(ttname));
}
}
void ProcessesLocal::Private::readProcStatm(struct kinfo_proc2 *p, Process *process)
{
// TODO
// unsigned long shared;
// process->vmURSS = process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024);
process->setVmURSS(-1);
}
bool ProcessesLocal::Private::readProcCmdline(struct kinfo_proc2 *p, Process *process)
{
char **argv;
if ((argv = kvm_getargv2(kd, p, 256)) == NULL)
return false;
QString command = QString("");
while (*argv) {
command += *argv;
command += " ";
argv++;
}
process->setCommand(command.trimmed());
return true;
}
ProcessesLocal::ProcessesLocal() : d(new Private())
{
}
long ProcessesLocal::getParentPid(long pid) {
long long ppid = 0;
struct kinfo_proc2 *p;
if(d->readProc(pid, &p, 0))
{
ppid = p->p_ppid;
}
return ppid;
}
bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
{
struct kinfo_proc2 *p;
if(!d->readProc(pid, &p, NULL)) return false;
d->readProcStat(p, process);
d->readProcStatus(p, process);
d->readProcStatm(p, process);
if(!d->readProcCmdline(p, process)) return false;
return true;
}
QSet<long> ProcessesLocal::getAllPids( )
{
QSet<long> pids;
int len;
int num;
struct kinfo_proc2 *p;
d->readProc(0, &p, &len);
for (num = 0; num < len; num++)
{
long pid = p[num].p_pid;
long long ppid = p[num].p_ppid;
//skip all process with parent id = 0 but init
if(ppid == 0 && pid != 1)
continue;
pids.insert(pid);
}
return pids;
}
bool ProcessesLocal::sendSignal(long pid, int sig) {
if ( kill( (pid_t)pid, sig ) ) {
//Kill failed
return false;
}
return true;
}
bool ProcessesLocal::setNiceness(long pid, int priority) {
if ( setpriority( PRIO_PROCESS, pid, priority ) ) {
//set niceness failed
return false;
}
return true;
}
bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
{
if(priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
priority = 0;
if(pid <= 0) return false; // check the parameters
struct sched_param params;
params.sched_priority = priority;
switch(priorityClass) {
case (KSysGuard::Process::Other):
return (sched_setscheduler( pid, SCHED_OTHER, &params) == 0);
case (KSysGuard::Process::RoundRobin):
return (sched_setscheduler( pid, SCHED_RR, &params) == 0);
case (KSysGuard::Process::Fifo):
return (sched_setscheduler( pid, SCHED_FIFO, &params) == 0);
#ifdef SCHED_BATCH
case (KSysGuard::Process::Batch):
return (sched_setscheduler( pid, SCHED_BATCH, &params) == 0);
#endif
default:
return false;
}
}
bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
return false; //Not yet supported
}
bool ProcessesLocal::supportsIoNiceness() {
return false;
}
long long ProcessesLocal::totalPhysicalMemory() {
size_t Total;
size_t len;
len = sizeof (Total);
sysctlbyname("hw.physmem", &Total, &len, NULL, 0);
return Total /= 1024;
}
ProcessesLocal::~ProcessesLocal()
{
delete d;
}
}

View File

@ -0,0 +1,306 @@
/* This file is part of the KDE project
Copyright (C) 2007 Manolo Valdes <nolis71cu@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes_local_p.h"
#include "process.h"
#include <klocale.h>
#include <QSet>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/resource.h>
#if defined(__DragonFly__)
#include <sys/resourcevar.h>
#include <err.h>
#endif
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
namespace KSysGuard
{
class ProcessesLocal::Private
{
public:
Private() {;}
~Private() {;}
inline bool readProc(long pid, struct kinfo_proc *p);
inline void readProcStatus(struct kinfo_proc *p, Process *process);
inline void readProcStat(struct kinfo_proc *p, Process *process);
inline void readProcStatm(struct kinfo_proc *p, Process *process);
inline bool readProcCmdline(long pid, Process *process);
};
bool ProcessesLocal::Private::readProc(long pid, struct kinfo_proc *p)
{
int mib[4];
size_t len;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = pid;
len = sizeof (struct kinfo_proc);
if (sysctl(mib, 4, p, &len, NULL, 0) == -1 || !len)
return false;
return true;
}
void ProcessesLocal::Private::readProcStatus(struct kinfo_proc *p, Process *process)
{
process->setUid(0);
process->setGid(0);
process->setTracerpid(0);
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
process->setUid(p->ki_uid);
process->setGid(p->ki_pgid);
process->setName(QString(p->ki_comm ? p->ki_comm : "????"));
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
process->setUid(p->kp_uid);
process->setGid(p->kp_pgid);
process->setName(QString(p->kp_comm ? p->kp_comm : "????"));
#else
process->setUid(p->kp_eproc.e_ucred.cr_uid);
process->setGid(p->kp_eproc.e_pgid);
#endif
}
void ProcessesLocal::Private::readProcStat(struct kinfo_proc *p, Process *ps)
{
int status;
struct rusage pru;
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
ps->setUserTime(p->ki_runtime / 10000);
ps->setNiceLevel(p->ki_nice);
ps->setVmSize(p->ki_size);
ps->setVmRSS(p->ki_rssize * getpagesize());
status = p->ki_stat;
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
if (!getrusage(p->kp_pid, &pru)) {
errx(1, "failed to get rusage info");
}
ps->setUserTime(pru.ru_utime.tv_usec / 1000); /*p_runtime / 1000*/
ps->setNiceLevel(p->kp_nice);
ps->setVmSize(p->kp_vm_map_size);
ps->setVmRSS(p->kp_vm_rssize * getpagesize());
status = p->kp_stat;
#else
ps->setUserTime(p->kp_proc.p_rtime.tv_sec*100+p->kp_proc.p_rtime.tv_usec/100);
ps->setNiceLevel(p->kp_proc.p_nice);
ps->setVmSize(p->kp_eproc.e_vm.vm_map.size);
ps->setVmRSS(p->kp_eproc.e_vm.vm_rssize * getpagesize());
status = p->kp_proc.p_stat;
#endif
ps->setSysTime(0);
// "idle","run","sleep","stop","zombie"
switch( status ) {
case '0':
ps->setStatus(Process::DiskSleep);
break;
case '1':
ps->setStatus(Process::Running);
break;
case '2':
ps->setStatus(Process::Sleeping);
break;
case '3':
ps->setStatus(Process::Stopped);
break;
case '4':
ps->setStatus(Process::Zombie);
break;
default:
ps->setStatus(Process::OtherStatus);
break;
}
}
void ProcessesLocal::Private::readProcStatm(struct kinfo_proc *p, Process *process)
{
// TODO
// unsigned long shared;
// process->setVmURSS(process->vmRSS - (shared * sysconf(_SC_PAGESIZE) / 1024));
}
bool ProcessesLocal::Private::readProcCmdline(long pid, Process *process)
{
int mib[4];
struct kinfo_proc p;
size_t buflen = 256;
char buf[256];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ARGS;
mib[3] = pid;
if (sysctl(mib, 4, buf, &buflen, NULL, 0) == -1 || !buflen)
return false;
QString command = QString(buf);
//cmdline seperates parameters with the NULL character
command.replace('\0', ' ');
process->setCommand(command.trimmed());
return true;
}
ProcessesLocal::ProcessesLocal() : d(new Private())
{
}
long ProcessesLocal::getParentPid(long pid) {
Q_ASSERT(pid != 0);
long long ppid = 0;
struct kinfo_proc p;
if(d->readProc(pid, &p))
{
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
ppid = p.ki_ppid;
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
ppid = p.kp_ppid;
#else
ppid = p.kp_eproc.e_ppid;
#endif
}
return ppid;
}
bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
{
struct kinfo_proc p;
if(!d->readProc(pid, &p)) return false;
d->readProcStat(&p, process);
d->readProcStatus(&p, process);
d->readProcStatm(&p, process);
if(!d->readProcCmdline(pid, process)) return false;
return true;
}
QSet<long> ProcessesLocal::getAllPids( )
{
QSet<long> pids;
int mib[3];
size_t len;
size_t num;
struct kinfo_proc *p;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_ALL;
sysctl(mib, 3, NULL, &len, NULL, 0);
p = (kinfo_proc *) malloc(len);
sysctl(mib, 3, p, &len, NULL, 0);
for (num = 0; num < len / sizeof(struct kinfo_proc); num++)
#if defined(__FreeBSD__) && __FreeBSD_version >= 500015
pids.insert(p[num].ki_pid);
#elif defined(__DragonFly__) && __DragonFly_version >= 190000
pids.insert(p[num].kp_pid);
#else
pids.insert(p[num].kp_proc.p_pid);
#endif
free(p);
return pids;
}
bool ProcessesLocal::sendSignal(long pid, int sig) {
if ( kill( (pid_t)pid, sig ) ) {
//Kill failed
return false;
}
return true;
}
bool ProcessesLocal::setNiceness(long pid, int priority) {
if ( setpriority( PRIO_PROCESS, pid, priority ) ) {
//set niceness failed
return false;
}
return true;
}
bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
{
if(priorityClass == KSysGuard::Process::Other || priorityClass == KSysGuard::Process::Batch)
priority = 0;
if(pid <= 0) return false; // check the parameters
return false;
}
bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
return false; //Not yet supported
}
bool ProcessesLocal::supportsIoNiceness() {
return false;
}
long long ProcessesLocal::totalPhysicalMemory() {
static int physmem_mib[] = { CTL_HW, HW_PHYSMEM };
/* get the page size with "getpagesize" and calculate pageshift from
* it */
int pagesize = ::getpagesize();
int pageshift = 0;
while (pagesize > 1) {
pageshift++;
pagesize >>= 1;
}
size_t Total = 0;
size_t size = sizeof(Total);
sysctl(physmem_mib, 2, &Total, &size, NULL, 0);
return Total /= 1024;
}
long int KSysGuard::ProcessesLocal::numberProcessorCores()
{
int mib[2];
int ncpu;
size_t len;
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
len = sizeof(ncpu);
if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1 || !len)
return 1;
return len;
}
ProcessesLocal::~ProcessesLocal()
{
delete d;
}
}

View File

@ -0,0 +1,267 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes_remote_p.h"
#include "process.h"
#include <QString>
#include <QSet>
#include <QTimer>
#include <klocale.h>
#include <kdebug.h>
namespace KSysGuard
{
class ProcessesRemote::Private
{
public:
Private() {havePsInfo = false; pidColumn = 1;
ppidColumn = nameColumn = uidColumn = gidColumn =
statusColumn = userColumn = systemColumn = niceColumn =
vmSizeColumn = vmRSSColumn = loginColumn = commandColumn =
tracerPidColumn = ttyColumn = ioprioClassColumn = ioprioColumn =
vmURSSColumn = -1;
usedMemory = freeMemory;}
~Private() {;}
QString host;
QList<QByteArray> lastAnswer;
QSet<long> pids;
QHash<long, QList<QByteArray> > processByPid;
bool havePsInfo;
int pidColumn;
int ppidColumn;
int tracerPidColumn;
int nameColumn;
int uidColumn;
int gidColumn;
int statusColumn;
int userColumn;
int systemColumn;
int niceColumn;
int vmSizeColumn;
int vmRSSColumn;
int vmURSSColumn;
int loginColumn;
int commandColumn;
int ioprioClassColumn;
int ioprioColumn;
int ttyColumn;
int numColumns;
long freeMemory;
long usedMemory;
};
ProcessesRemote::ProcessesRemote(const QString &hostname) : d(new Private())
{
d->host = hostname;
QTimer::singleShot(0, this, SLOT(setup()));
}
void ProcessesRemote::setup() {
emit runCommand("mem/physical/used", (int)UsedMemory);
emit runCommand("mem/physical/free", (int)FreeMemory);
emit runCommand("ps?", (int)PsInfo);
emit runCommand("ps", (int)Ps);
}
long ProcessesRemote::getParentPid(long pid) {
if(!d->processByPid.contains(pid)) {
kDebug() << "Parent pid requested for pid that we do not have info on " << pid;
return 0;
}
if(d->ppidColumn == -1) {
kDebug() << "ppid column not known ";
return 0;
}
return d->processByPid[pid].at(d->ppidColumn).toLong();
}
bool ProcessesRemote::updateProcessInfo( long pid, Process *process)
{
Q_CHECK_PTR(process);
if(!d->processByPid.contains(pid)) {
kDebug() << "update request for pid that we do not have info on " << pid;
return false;
}
QList<QByteArray> p = d->processByPid[pid];
if(d->nameColumn!= -1) process->setName(p.at(d->nameColumn));
if(d->uidColumn!= -1) process->setUid(p.at(d->uidColumn).toLong());
if(d->gidColumn!= -1) process->setGid(p.at(d->gidColumn).toLong());
if(d->statusColumn!= -1) {
switch( p.at(d->statusColumn)[0] ) {
case 's':
process->setStatus(Process::Sleeping);
break;
case 'r':
process->setStatus(Process::Running);
break;
}
}
if(d->userColumn!= -1) process->setUserTime(p.at(d->userColumn).toLong());
if(d->systemColumn!= -1) process->setSysTime(p.at(d->systemColumn).toLong());
if(d->niceColumn!= -1) process->setNiceLevel(p.at(d->niceColumn).toLong());
if(d->vmSizeColumn!= -1) process->setVmSize(p.at(d->vmSizeColumn).toLong());
if(d->vmRSSColumn!= -1) process->setVmRSS(p.at(d->vmRSSColumn).toLong());
if(d->vmURSSColumn!= -1) process->setVmURSS(p.at(d->vmURSSColumn).toLong());
if(d->loginColumn!= -1) process->setLogin(QString::fromUtf8(p.at(d->loginColumn).data()));
if(d->commandColumn!= -1) process->setCommand(QString::fromUtf8(p.at(d->commandColumn).data()));
if(d->tracerPidColumn!= -1) process->setTracerpid(p.at(d->tracerPidColumn).toLong());
if(d->vmURSSColumn!= -1) process->setVmURSS(p.at(d->vmURSSColumn).toLong());
if(d->ttyColumn!= -1) process->setTty(p.at(d->ttyColumn));
if(d->ioprioColumn!= -1) process->setIoniceLevel(p.at(d->ioprioColumn).toInt());
if(d->ioprioClassColumn!= -1) process->setIoPriorityClass((KSysGuard::Process::IoPriorityClass)(p.at(d->ioprioClassColumn).toInt()));
return true;
}
void ProcessesRemote::updateAllProcesses()
{
if(!d->havePsInfo)
emit runCommand("ps?", (int)PsInfo);
emit runCommand("ps", (int)Ps);
}
QSet<long> ProcessesRemote::getAllPids( )
{
d->pids.clear();
d->processByPid.clear();
foreach(const QByteArray &process, d->lastAnswer) {
QList<QByteArray> info = process.split('\t');
if(info.size() == d->numColumns) {
int pid = info.at(d->pidColumn).toLong();
Q_ASSERT(! d->pids.contains(pid));
d->pids << pid;
d->processByPid[pid] = info;
}
}
return d->pids;
}
bool ProcessesRemote::sendSignal(long pid, int sig) {
//TODO run the proper command for all these functions below
emit runCommand("kill " + QString::number(pid) + " " + QString::number(sig), (int)Kill);
return true;
}
bool ProcessesRemote::setNiceness(long pid, int priority) {
emit runCommand("setpriority " + QString::number(pid) + " " + QString::number(priority), (int)Renice);
return true;
}
bool ProcessesRemote::setIoNiceness(long pid, int priorityClass, int priority) {
emit runCommand("ionice " + QString::number(pid) + " " + QString::number(priorityClass) + " " + QString::number(priority), (int)Ionice);
return true;
}
bool ProcessesRemote::setScheduler(long pid, int priorityClass, int priority) {
return false;
}
bool ProcessesRemote::supportsIoNiceness() {
return true;
}
long long ProcessesRemote::totalPhysicalMemory() {
return d->usedMemory + d->freeMemory;
}
long ProcessesRemote::numberProcessorCores() {
return 0;
}
void ProcessesRemote::answerReceived( int id, const QList<QByteArray>& answer ) {
switch (id) {
case PsInfo: {
if(answer.isEmpty()) return; //Invalid data
QList<QByteArray> info = answer.at(0).split('\t');
d->numColumns = info.size();
for(int i =0; i < d->numColumns; i++) {
if(info[i] == "Name")
d->nameColumn = i;
else if(info[i] == "PID")
d->pidColumn = i;
else if(info[i] == "PPID")
d->ppidColumn = i;
else if(info[i] == "UID")
d->uidColumn = i;
else if(info[i] == "GID")
d->gidColumn = i;
else if(info[i] == "TracerPID")
d->tracerPidColumn = i;
else if(info[i] == "Status")
d->statusColumn = i;
else if(info[i] == "User Time")
d->userColumn = i;
else if(info[i] == "System Time")
d->systemColumn = i;
else if(info[i] == "Nice")
d->niceColumn = i;
else if(info[i] == "VmSize")
d->vmSizeColumn = i;
else if(info[i] == "VmRss")
d->vmRSSColumn = i;
else if(info[i] == "VmURss")
d->vmURSSColumn = i;
else if(info[i] == "Login")
d->loginColumn = i;
else if(info[i] == "TTY")
d->ttyColumn = i;
else if(info[i] == "Command")
d->commandColumn = i;
else if(info[i] == "IO Priority Class")
d->ioprioClassColumn = i;
else if(info[i] == "IO Priority")
d->ioprioColumn = i;
}
d->havePsInfo = true;
break;
}
case Ps:
d->lastAnswer = answer;
if(!d->havePsInfo) return; //Not setup yet. Should never happen
emit processesUpdated();
case FreeMemory:
if(answer.isEmpty()) return; //Invalid data
d->freeMemory = answer[0].toLong();
break;
case UsedMemory:
if(answer.isEmpty()) return; //Invalid data
d->usedMemory = answer[0].toLong();
break;
}
}
ProcessesRemote::~ProcessesRemote()
{
delete d;
}
}
#include "processes_remote_p.moc"

View File

@ -0,0 +1,78 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESSES_REMOTE_P_H_
#define PROCESSES_REMOTE_P_H_
#include "processes_base_p.h"
#include <QSet>
class Process;
namespace KSysGuard
{
/**
* This is used to connect to a remote host
*/
class ProcessesRemote : public AbstractProcesses {
Q_OBJECT
public:
ProcessesRemote(const QString &hostname);
virtual ~ProcessesRemote();
virtual QSet<long> getAllPids();
virtual long getParentPid(long pid);
virtual bool updateProcessInfo(long pid, Process *process);
virtual bool sendSignal(long pid, int sig);
virtual bool setNiceness(long pid, int priority);
virtual bool setScheduler(long pid, int priorityClass, int priority);
virtual long long totalPhysicalMemory();
virtual bool setIoNiceness(long pid, int priorityClass, int priority);
virtual bool supportsIoNiceness();
virtual long numberProcessorCores();
virtual void updateAllProcesses();
Q_SIGNALS:
/** For a remote machine, we rely on being able to communicate with ksysguardd.
* This must be dealt with by the program including this widget. It must listen to our
* 'runCommand' signal, and run the given command, with the given id. */
void runCommand(const QString &command, int id);
public Q_SLOTS:
/** For a remote machine, we rely on being able to communicate with ksysguardd.
* The programming using this must call this slot when an answer is received from ksysguardd,
* in response to a runCommand request. The id identifies the answer */
void answerReceived( int id, const QList<QByteArray>& answer );
/** Called soon after */
void setup();
protected:
enum { PsInfo, Ps, UsedMemory, FreeMemory, Kill, Renice, Ionice };
private:
/**
* You can use this for whatever data you want. Be careful about preserving state in between getParentPid and updateProcessInfo calls
* if you chose to do that. getParentPid may be called several times for different pids before the relevant updateProcessInfo calls are made.
* This is because the tree structure has to be sorted out first.
*/
class Private;
Private *d;
};
}
#endif

View File

@ -0,0 +1,100 @@
/* This file is part of the KDE project
Copyright (C) 2007 Adriaan de Groot <groot@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "processes_local_p.h"
#include "process.h"
#include <klocale.h>
#include <QSet>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/resource.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <sched.h>
#ifdef __GNUC__
#warning Totally bogus ProcessesLocal implementation
#endif
namespace KSysGuard
{
class ProcessesLocal::Private
{
public:
Private() { };
~Private() { };
} ;
ProcessesLocal::ProcessesLocal() : d(0)
{
}
long ProcessesLocal::getParentPid(long pid) {
long long ppid = 0;
return ppid;
}
bool ProcessesLocal::updateProcessInfo( long pid, Process *process)
{
return false;
}
QSet<long> ProcessesLocal::getAllPids( )
{
QSet<long> pids;
return pids;
}
bool ProcessesLocal::sendSignal(long pid, int sig) {
return false;
}
bool ProcessesLocal::setNiceness(long pid, int priority) {
return false;
}
bool ProcessesLocal::setScheduler(long pid, int priorityClass, int priority)
{
return false;
}
bool ProcessesLocal::setIoNiceness(long pid, int priorityClass, int priority) {
return false; //Not yet supported
}
bool ProcessesLocal::supportsIoNiceness() {
return false;
}
long long ProcessesLocal::totalPhysicalMemory() {
return 0;
}
ProcessesLocal::~ProcessesLocal()
{
delete d;
}
}

View File

@ -0,0 +1,91 @@
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../ ../processcore/)
check_include_files(sys/ptrace.h HAVE_SYS_PTRACE_H)
check_include_files(sys/endian.h HAVE_SYS_ENDIAN_H)
check_include_files(byteswap.h HAVE_BYTESWAP_H)
if (HAVE_SYS_PTRACE_H)
set(_SUPPORTED_REGS_STRUCT_CHECK "
#include <sys/user.h>
#include <sys/syscall.h>
#if defined(__i386__)
# define _regs_struct user_regs_struct
# define REG_ORIG_ACCUM orig_eax
#elif defined( __amd64__)
# define _regs_struct user_regs_struct
# define REG_ORIG_ACCUM orig_rax
#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__PPC__) || defined(powerpc)
# define _regs_struct pt_regs;
# define REG_ORIG_ACCUM gpr[0]
#endif
int main() {
struct _regs_struct* regs;
return regs->REG_ORIG_ACCUM == SYS_fork;
}")
include(CheckCSourceCompiles)
check_c_source_compiles("${_SUPPORTED_REGS_STRUCT_CHECK}" HAVE_KSYSGUARD_SUPPORTED_REGS_STRUCT)
else (HAVE_SYS_PTRACE_H)
# Even if we don't have ptrace, we need KMonitorProcessIO.cpp
# because the widget is referenced from the .widgets file and
# from the .ui files.
set(HAVE_KSYSGUARD_SUPPORTED_REGS_STRUCT "NO")
endif (HAVE_SYS_PTRACE_H)
if (HAVE_KSYSGUARD_SUPPORTED_REGS_STRUCT)
add_definitions(-DWITH_MONITOR_PROCESS_IO)
set(processui_ptrace_SRCS KMonitorProcessIO.cpp DisplayProcessDlg.cpp)
set(ksysguard_WIDGETS "${CMAKE_CURRENT_BINARY_DIR}/ksysguard.generated_widgets")
# Merge both widget files
file(READ ksysguard.widgets _ksysguard_buffer)
file(WRITE ${ksysguard_WIDGETS} "${_ksysguard_buffer}")
file(READ KMonitorProcessIO.widgets _ksysguard_buffer)
file(APPEND ${ksysguard_WIDGETS} "${_ksysguard_buffer}")
else (HAVE_KSYSGUARD_SUPPORTED_REGS_STRUCT)
set(processui_ptrace_SRCS )
set(ksysguard_WIDGETS "ksysguard.widgets")
endif (HAVE_KSYSGUARD_SUPPORTED_REGS_STRUCT)
set(processui_LIB_SRCS
ksysguardprocesslist.cpp
ProcessFilter.cc
ProcessModel.cc
ReniceDlg.cc
KTextEditVT.cpp
${processui_ptrace_SRCS}
)
kde4_add_ui_files( processui_LIB_SRCS
ReniceDlgUi.ui
ProcessWidgetUI.ui
DisplayProcessUi.ui
)
kde4_add_library(processui SHARED ${processui_LIB_SRCS})
target_link_libraries(processui ${KDE4_KDEUI_LIBS} processcore)
set_target_properties(processui
PROPERTIES VERSION ${GENERIC_LIB_VERSION}
SOVERSION ${GENERIC_LIB_SOVERSION}
${KDE4_DISABLE_PROPERTY_}LINK_INTERFACE_LIBRARIES "${KDE4_KDEUI_LIBS};processcore"
)
install(TARGETS processui ${INSTALL_TARGETS_DEFAULT_ARGS} )
#----------------------
KDE4_ADD_WIDGET_FILES(ksysguardwidgets_PART_SRCS ${ksysguard_WIDGETS})
kde4_add_plugin(ksysguardwidgets ${ksysguardwidgets_PART_SRCS})
target_link_libraries(ksysguardwidgets ${KDE4_KDEUI_LIBS} processui)
install(TARGETS ksysguardwidgets DESTINATION ${PLUGIN_INSTALL_DIR}/plugins/designer )
install( FILES ProcessModel.h DisplayProcessDlg.h ProcessFilter.h KTextEditVT.h ksysguardprocesslist.h DESTINATION ${INCLUDE_INSTALL_DIR}/ksysguard COMPONENT Devel)

View File

@ -0,0 +1,100 @@
/*
KSysGuard, the KDE System Guard
Copyright (C) 2007 Trent Waddington <trent.waddington@gmail.com>
Copyright (c) 2008 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "KMonitorProcessIO.h"
#include "DisplayProcessDlg.h"
#include "DisplayProcessDlg.moc"
#include "ui_DisplayProcessUi.h"
DisplayProcessDlg::DisplayProcessDlg(QWidget* parent, KSysGuard::Process *process)
: KDialog( parent )
{
setObjectName( "Display Process Dialog" );
setModal( false );
setCaption( i18n("Monitoring I/O for %1 (%2)", process->pid, process->name) );
setButtons( Close );
//enableLinkedHelp( true );
showButtonSeparator( true );
QWidget *widget = new QWidget(this);
setMainWidget(widget);
ui = new Ui_DisplayProcessUi();
ui->setupUi(widget);
ui->mTextEdit->setWhatsThis(i18n("The program '%1' (PID: %2) is being monitored for input and output through any file descriptor (stdin, stdout, stderr, open files, network connections, etc.). Data being written by the process is shown in red and data being read by the process is shown in blue.", process->name, process->pid));
if(!ui->mTextEdit->attach(process->pid)) {
ui->btnDetach->setText(i18n("&Attach"));
ui->btnDetach->setChecked(true);
ui->btnPause->setText(i18n("&Pause"));
ui->btnPause->setChecked(false);
ui->btnPause->setEnabled(false);
}
connect(ui->btnPause, SIGNAL(toggled(bool)), this, SLOT(slotBtnPause(bool)));
connect(ui->btnDetach, SIGNAL(toggled(bool)), this, SLOT(slotBtnDetach(bool)));
}
DisplayProcessDlg::~DisplayProcessDlg() {
ui->mTextEdit->detach();
}
void DisplayProcessDlg::slotButtonClicked(int)
{
ui->mTextEdit->detach();
accept();
}
QSize DisplayProcessDlg::sizeHint() const {
return QSize(600,600);
}
void DisplayProcessDlg::slotBtnPause(bool pause) {
if(pause) {
ui->mTextEdit->pauseProcesses();
ui->btnPause->setText(i18n("&Resume"));
} else {
ui->mTextEdit->resumeProcesses();
ui->btnPause->setText(i18n("&Pause"));
}
}
void DisplayProcessDlg::slotBtnDetach(bool detach) {
if(detach) {
ui->btnDetach->setText(i18n("&Attach"));
ui->btnDetach->setChecked(true);
ui->btnPause->setText(i18n("&Pause"));
ui->btnPause->setChecked(false);
ui->btnPause->setEnabled(false);
ui->mTextEdit->detach();
} else {
if(!ui->mTextEdit->reattach()) {
//failed to attached
ui->btnDetach->setText(i18n("&Attach"));
ui->btnDetach->setChecked(true);
ui->btnPause->setText(i18n("&Pause"));
ui->btnPause->setChecked(false);
ui->btnPause->setEnabled(false);
} else
ui->btnDetach->setText(i18n("&Detach"));
}
}

View File

@ -0,0 +1,53 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 2008 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _DisplayProcessDlg_h_
#define _DisplayProcessDlg_h_
#include <kdialog.h>
#include <kprocess.h>
#include "processes.h"
class Ui_DisplayProcessUi;
class DisplayProcessDlg : public KDialog
{
Q_OBJECT
public:
DisplayProcessDlg(QWidget* parent, KSysGuard::Process *process);
~DisplayProcessDlg();
/** Returns the default size of the dialog. Reimplmentation from QDialog */
virtual QSize sizeHint() const;
public Q_SLOTS:
/** Close button has been clicked. Reimplementation from KDialog */
virtual void slotButtonClicked(int);
void slotBtnPause(bool pause);
void slotBtnDetach(bool detach);
private:
Ui_DisplayProcessUi *ui;
};
#endif

View File

@ -0,0 +1,170 @@
<ui version="4.0" >
<class>DisplayProcessUi</class>
<widget class="QWidget" name="DisplayProcessUi" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>681</width>
<height>863</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<property name="spacing" >
<number>0</number>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="KMonitorProcessIO" name="mTextEdit" />
</item>
<item>
<widget class="QCheckBox" name="chkColorOutput" >
<property name="toolTip" >
<string>Interpret output as containing VT100 commands. For console-based programs.</string>
</property>
<property name="whatsThis" >
<string>If this is checked then ANSI escape sequences will be interpreted and not displayed. Useful for when monitoring bash.</string>
</property>
<property name="text" >
<string>Interpret ANSI escape sequences</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="chkMonitorChildProcesses" >
<property name="statusTip" >
<string>Show the output from processes launched by this process.</string>
</property>
<property name="whatsThis" >
<string>Follow fork and clone commands to monitor child processes</string>
</property>
<property name="text" >
<string>Monitor child processes</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QPushButton" name="btnPause" >
<property name="toolTip" >
<string>Pause the process and its output</string>
</property>
<property name="whatsThis" >
<string>Stop reading the output from the process. This will cause the process to be blocked, effectively pausing it. The process can be resumed by resuming, detaching or closing the dialog.</string>
</property>
<property name="text" >
<string>&amp;Pause</string>
</property>
<property name="checkable" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>58</width>
<height>27</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="btnDetach" >
<property name="statusTip" >
<string>Stop monitoring the process</string>
</property>
<property name="whatsThis" >
<string>Stop monitoring the process and allow the process to resume running.</string>
</property>
<property name="text" >
<string>&amp;Detach</string>
</property>
<property name="checkable" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KMonitorProcessIO</class>
<extends>KTextEditVT</extends>
<header>KMonitorProcessIO.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mTextEdit</tabstop>
<tabstop>chkColorOutput</tabstop>
<tabstop>chkMonitorChildProcesses</tabstop>
<tabstop>btnPause</tabstop>
<tabstop>btnDetach</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>chkColorOutput</sender>
<signal>toggled(bool)</signal>
<receiver>mTextEdit</receiver>
<slot>setParseAnsiEscapeCodes(bool)</slot>
<hints>
<hint type="sourcelabel" >
<x>55</x>
<y>795</y>
</hint>
<hint type="destinationlabel" >
<x>51</x>
<y>254</y>
</hint>
</hints>
</connection>
<connection>
<sender>chkMonitorChildProcesses</sender>
<signal>toggled(bool)</signal>
<receiver>mTextEdit</receiver>
<slot>setIncludeChildProcesses(bool)</slot>
<hints>
<hint type="sourcelabel" >
<x>281</x>
<y>822</y>
</hint>
<hint type="destinationlabel" >
<x>277</x>
<y>258</y>
</hint>
</hints>
</connection>
<connection>
<sender>btnDetach</sender>
<signal>toggled(bool)</signal>
<receiver>btnPause</receiver>
<slot>setDisabled(bool)</slot>
<hints>
<hint type="sourcelabel" >
<x>624</x>
<y>847</y>
</hint>
<hint type="destinationlabel" >
<x>75</x>
<y>837</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,330 @@
/*
KSysGuard, the KDE System Guard
Copyright (C) 2007 Trent Waddington <trent.waddington@gmail.com>
Copyright (c) 2008 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
aint with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "../config-ksysguard.h"
#include <klocale.h>
#include <kdebug.h>
#include <QTimer>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#ifdef HAVE_SYS_PTRACE_H
#include <sys/ptrace.h>
#endif
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/syscall.h>
#include <sys/user.h>
#include <ctype.h>
#ifdef __i386__
#define REG_ORIG_ACCUM(regs) regs.orig_eax
#define REG_ACCUM(regs) regs.eax
#define REG_PARAM1(regs) regs.ebx
#define REG_PARAM2(regs) regs.ecx
#define REG_PARAM3(regs) regs.edx
#endif
#ifdef __amd64__
#define REG_ORIG_ACCUM(regs) regs.orig_rax
#define REG_ACCUM(regs) regs.rax
#define REG_PARAM1(regs) regs.rdi
#define REG_PARAM2(regs) regs.rsi
#define REG_PARAM3(regs) regs.rdx
#endif
#if defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__PPC__) || defined(powerpc)
#define REG_ORIG_ACCUM(regs) regs.gpr[0]
#define REG_ACCUM(regs) regs.gpr[3]
#define REG_PARAM1(regs) regs.orig_gpr3
#define REG_PARAM2(regs) regs.gpr[4]
#define REG_PARAM3(regs) regs.gpr[5]
#ifndef PT_ORIG_R3
#define PT_ORIG_R3 34
#endif
#endif
#ifdef __ia64__
#undef slots
#include <sys/rse.h>
#define REG_ORIG_ACCUM(regs) regs.pt.gr[15]
#define REG_ACCUM(regs) (regs.pt.gr[10] ? -regs.pt.gr[8] : regs.pt.gr[8])
#define REG_PARAM1(regs) regs.arg[0]
#define REG_PARAM2(regs) regs.arg[1]
#define REG_PARAM3(regs) regs.arg[2]
#endif
#include "KMonitorProcessIO.h"
#include "KMonitorProcessIO.moc"
KMonitorProcessIO::KMonitorProcessIO(QWidget* parent, int pid)
: KTextEditVT( parent ), mPid(pid)
{
mIncludeChildProcesses = true;
remove_duplicates = false;
mUpdateInterval = 20;
mTimer.setSingleShot(false);
connect(&mTimer, SIGNAL(timeout()), this, SLOT(update()));
lastdir = 3; //an invalid direction, so that the color gets set the first time
setReadOnly(true);
setParseAnsiEscapeCodes(true);
document()->setMaximumBlockCount(100);
mCursor = textCursor();
if(pid == -1)
return;
attach(mPid);
}
KMonitorProcessIO::~KMonitorProcessIO() {
detach();
}
int KMonitorProcessIO::updateInterval() const {
return mUpdateInterval;
}
void KMonitorProcessIO::setUpdateInterval(int msecs) {
mUpdateInterval = msecs;
if(mTimer.isActive()) {
mTimer.stop();
mTimer.start(msecs); //Start with the new interval time
}
}
void KMonitorProcessIO::detach() {
foreach(int pid, attached_pids) {
detach(pid);
}
}
int KMonitorProcessIO::attachedPid() const {
return mPid;
}
void KMonitorProcessIO::detach(int pid) {
int status;
#ifdef HAVE_SYS_PTRACE_H
if(!ptrace(PTRACE_DETACH, pid, 0, 0)) {
//successfully detached
} else if(kill(pid, 0) < 0) {
if(errno != ESRCH)
kDebug() << "Something seriously strange when trying to detach.";
} else if (kill(pid, SIGSTOP) < 0) {
if (errno != ESRCH)
kDebug() << "Something seriously strange when trying to detach and then trying to stop the process";
} else {
for (;;) {
if (waitpid(pid, &status, 0) < 0) {
if (errno != ECHILD)
kDebug() << "Something seriously strange when trying to detach and waiting for process to stop";
break;
}
if (!WIFSTOPPED(status)) {
/* Au revoir, mon ami. */
break;
}
if (WSTOPSIG(status) == SIGSTOP) {
//Okay process is now stopped. Lets try detaching again. Silly linux.
if (ptrace(PTRACE_DETACH,pid, 0, 0) < 0) {
if (errno != ESRCH)
kDebug() << "Something seriously strange when trying to detach the second time.";
/* I died trying. */
}
break;
}
// we didn't manage to stop the process. Lets try continuing it and the stopping it
if (ptrace(PTRACE_CONT, pid, 0, 0) < 0) {
if (errno != ESRCH)
kDebug() << "Something seriously strange when trying to detach and continue";
break;
}
}
}
#endif
attached_pids.removeAll(pid);
if(attached_pids.isEmpty()) {
mTimer.stop();
}
}
bool KMonitorProcessIO::reattach() {
if(mPid == -1)
return false;
return attach(mPid);
}
bool KMonitorProcessIO::attach(int pid) {
if(pid == -1) {
//Indicates to detach all
detach();
return false;
}
#ifdef HAVE_SYS_PTRACE_H
if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) {
kDebug() << "Failed to attach to process " << pid;
if(attached_pids.isEmpty()) {
mTimer.stop();
insertHtml(i18n("<br/><i><font color=\"gray\">Failed to attach to process %1</font></i><br/>", pid));
return false;
}
} else {
if(attached_pids.isEmpty()) {
//First process added. Automatically start timer
ptrace(PTRACE_SYSCALL, pid, 0, 0);
mTimer.start(mUpdateInterval);
if(mPid == -1)
mPid = pid;
}
attached_pids.append(pid);
}
return true;
#else
return false;
#endif
}
void KMonitorProcessIO::update(bool modified)
{
#ifdef HAVE_SYS_PTRACE_H
static QColor writeColor = QColor(255,0,0);
static QColor readColor = QColor(0,0,255);
int status;
int pid = waitpid(-1, &status, WNOHANG | WUNTRACED | WCONTINUED);
if (pid == -1 || !WIFSTOPPED(status)) {
if(modified)
ensureCursorVisible();
return;
}
#if defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__PPC__) || defined(powerpc)
struct pt_regs regs;
regs.gpr[0] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R0, 0);
regs.gpr[3] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R3, 0);
regs.gpr[4] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R4, 0);
regs.gpr[5] = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_R5, 0);
regs.orig_gpr3 = ptrace(PTRACE_PEEKUSER, pid, 4 * PT_ORIG_R3, 0);
#endif
#ifdef __ia64__
struct {
struct pt_all_user_regs pt;
unsigned long arg[3];
} regs;
ptrace(PTRACE_GETREGS, pid, 0, &regs.pt);
if (REG_ORIG_ACCUM(regs) >= 0) {
unsigned long *out0 = ia64_rse_skip_regs((unsigned long *)regs.pt.ar[17], -(regs.pt.cfm & 0x7f) + ((regs.pt.cfm >> 7) & 0x7f));
regs.arg[0] = ptrace(PTRACE_PEEKDATA, pid, ia64_rse_skip_regs(out0, 0), 0);
regs.arg[1] = ptrace(PTRACE_PEEKDATA, pid, ia64_rse_skip_regs(out0, 1), 0);
regs.arg[2] = ptrace(PTRACE_PEEKDATA, pid, ia64_rse_skip_regs(out0, 2), 0);
}
#endif
#if defined __i386__ || defined __amd64__
struct user_regs_struct regs;
ptrace(PTRACE_GETREGS, pid, 0, &regs);
#endif
/*unsigned int b = ptrace(PTRACE_PEEKTEXT, pid, regs.eip, 0);*/
if (mIncludeChildProcesses && (
#ifdef SYS_fork
REG_ORIG_ACCUM(regs) == SYS_fork ||
#endif
#ifdef SYS_clone
REG_ORIG_ACCUM(regs) == SYS_clone ||
#endif
#ifdef SYS_clone2
REG_ORIG_ACCUM(regs) == SYS_clone2 ||
#endif
0)) {
if (REG_ACCUM(regs) > 0)
attach(REG_ACCUM(regs));
}
if ((REG_ORIG_ACCUM(regs) == SYS_read || REG_ORIG_ACCUM(regs) == SYS_write) && (REG_PARAM3(regs) == REG_ACCUM(regs))) {
for (unsigned long i = 0; i < REG_PARAM3(regs); i++) {
union {
unsigned long l;
unsigned char c[sizeof(long)];
} a;
a.l = ptrace(PTRACE_PEEKDATA, pid, REG_PARAM2(regs) + i, 0);
if(!modified) {
//Before we add text or change the color, make sure we are at the end
moveCursor(QTextCursor::End);
}
if(REG_ORIG_ACCUM(regs) != lastdir) {
if(REG_ORIG_ACCUM(regs) == SYS_read)
setTextColor(readColor);
else
setTextColor(writeColor);
lastdir = REG_ORIG_ACCUM(regs);
}
for (unsigned j = 0; j < sizeof(a.c) && i < REG_PARAM3(regs); i++, j++) {
unsigned char c = a.c[j];
/** Use the KTextEditVT specific function to parse the character 'c' */
insertVTChar(QChar(c));
}
}
modified = true;
}
ptrace(PTRACE_SYSCALL, pid, 0, 0);
update(modified);
#endif
}
void KMonitorProcessIO::setIncludeChildProcesses(bool include) {
mIncludeChildProcesses = include;
}
bool KMonitorProcessIO::includeChildProcesses() const {
return mIncludeChildProcesses;
}
KMonitorProcessIO::State KMonitorProcessIO::state() const {
if(attached_pids.isEmpty())
return Detached;
if(mTimer.isActive())
return AttachedRunning;
return AttachedPaused;
}
void KMonitorProcessIO::pauseProcesses() {
if(state() == AttachedRunning) {
mTimer.stop();
}
}
void KMonitorProcessIO::resumeProcesses() {
if(state() == AttachedPaused)
mTimer.start(mUpdateInterval);
}
void KMonitorProcessIO::setState(State new_state) {
if(new_state == AttachedPaused) pauseProcesses();
if(new_state == AttachedRunning) resumeProcesses();
if(new_state == Detached) detach();
}

View File

@ -0,0 +1,115 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 2008 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _KMonitorProcessIO_h_
#define _KMonitorProcessIO_h_
#include <QtCore/QTimer>
#include "KTextEditVT.h"
#include <kdialog.h>
#include <kprocess.h>
#include "processes.h"
class KDE_EXPORT KMonitorProcessIO : public KTextEditVT
{
Q_OBJECT
Q_PROPERTY( bool includeChildProcesses READ includeChildProcesses WRITE setIncludeChildProcesses )
Q_PROPERTY( bool updateInterval READ updateInterval WRITE setUpdateInterval )
Q_PROPERTY( int attachedPid READ attachedPid WRITE attach )
Q_PROPERTY( State status READ state WRITE setState )
Q_ENUMS( State )
public:
KMonitorProcessIO(QWidget* parent, int pid = -1);
~KMonitorProcessIO();
/** Whether to include the output from child processes. If true, forks and clones will be monitored */
bool includeChildProcesses() const;
/** Interval to poll for new ptrace input. Recommended around 20 (milliseconds). Note that the process
* being monitored cannot do anything if it is waiting for us to update.
* Default is 20 (ms)
*/
int updateInterval() const;
/** Set interval to poll for new ptrace input. Recommended around 20 (milliseconds). Note that the process
* being monitored cannot do anything if it is waiting for us to update.
* Default is 20 (ms)
*/
void setUpdateInterval(int msecs);
/** Detached state indicates that we are not connected to the process and not monitoring it.
*
* AttachedRunning state indicates that we are attached to the process and displaying its output.
*
* AttachedPaused state indicates that we are attached but not reading its output. This will block the process until we resume or detach.
*/
enum State { Detached, AttachedRunning, AttachedPaused };
/** Return the current state. */
KMonitorProcessIO::State state() const;
public Q_SLOTS:
/** Set whether to include the output from child processes. If true, forks and clones will be monitored */
void setIncludeChildProcesses(bool include);
/** If the state is in AttachedRunning, change to AttachedPaused. This will block the process until we resume or detach.*/
void pauseProcesses();
/** If the state is in AttachedPaused, change to AttachedRunning. This will allow the process to run again. */
void resumeProcesses();
/** Stop monitoring all processes*/
void detach();
/** Stop monitoring the given process */
void detach(int pid);
/** Start monitoring the given process. If this is the first process being monitored, the state is set to
* AttachedRunning if possible and attachedPid() will return @p pid
* @return true if successfully reattached. Can fail if process has disappeared or we do not have the right to attach. */
bool attach(int pid);
/** Reattach the pid that was first attached.
* @return true if successfully reattached. Can fail if process has disappeared or we do not have the right to attach. */
bool reattach();
/** Return the main pid that we are monitoring.*/
int attachedPid() const;
/** Attempts to set the state. Check status() to confirm whether the state has changed successfully. */
void setState(State new_state);
Q_SIGNALS:
void finished();
private Q_SLOTS:
/** Read in the next bit of data and display it. This should be called very frequently. */
void update(bool modified=false);
private:
KProcess mIOProcess;
KTextEditVT *mTextEdit;
QTimer mTimer;
int mPid;
QList<int> attached_pids;
int mUpdateInterval;
bool mIncludeChildProcesses;
bool remove_duplicates;
unsigned int lastdir;
QTextCursor mCursor;
};
#endif

View File

@ -0,0 +1,7 @@
[KMonitorProcessIO]
ToolTip=Monitors the input and output of a running process.
WhatsThis=A widget for showing the input and output of a running process, including stdin, stdout, stderr and any file descriptors. Be careful as this can make the watched process quite slow.
Group=KSysGuard (KDE)
ConstructorArgs=(parent)
IncludeFile=KMonitorProcessIO.h

View File

@ -0,0 +1,170 @@
/*
KSysGuard, the KDE System Guard
Copyright (C) 2007 Trent Waddington <trent.waddington@gmail.com>
Copyright (c) 2008 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <klocale.h>
#include "KTextEditVT.h"
#include "KTextEditVT.moc"
#include <kglobalsettings.h>
KTextEditVT::KTextEditVT(QWidget* parent)
: QTextEdit( parent )
{
mParseAnsi = true;
escape_sequence = false;
escape_CSI = false;
escape_OSC = false;
escape_number1 = -1;
escape_number_seperator = false;
escape_number2 = -1;
escape_code = 0;
setFont( KGlobalSettings::fixedFont() );
}
void KTextEditVT::insertVTChar(const QChar & c) {
if(escape_sequence) {
if(escape_CSI || escape_OSC) {
if(c.isDigit()) {
if(!escape_number_seperator) {
if(escape_number1 == -1)
escape_number1 = c.digitValue();
else
escape_number1 = escape_number1*10 + c.digitValue();
} else {
if(escape_number2 == -1)
escape_number2 = c.digitValue();
else
escape_number2 = escape_number2*10 + c.digitValue();
}
} else if(c == ';') {
escape_number_seperator = true;
} else if(escape_OSC && c==7) { //Throw away any letters that are not OSC
escape_code = c;
} else if(escape_CSI)
escape_code = c;
} else if(c=='[') {
escape_CSI = true;
} else if(c==']') {
escape_OSC = true;
}
else if(c=='(' || c==')') {}
else
escape_code = c;
if(!escape_code.isNull()) {
//We've read in the whole escape sequence. Now parse it
if(escape_code == 'm') { // change color
switch(escape_number2){
case 0: //all off
setFontWeight(QFont::Normal);
setTextColor(Qt::black);
break;
case 1: //bold
setFontWeight(QFont::Bold);
break;
case 31: //red
setTextColor(Qt::red);
break;
case 32: //green
setTextColor(Qt::green);
break;
case 33: //yellow
setTextColor(Qt::yellow);
break;
case 34: //blue
setTextColor(Qt::blue);
break;
case 35: //magenta
setTextColor(Qt::magenta);
break;
case 36: //cyan
setTextColor(Qt::cyan);
break;
case -1:
case 30: //black
case 39: //reset
case 37: //white
setTextColor(Qt::black);
break;
}
}
escape_code = 0;
escape_number1 = -1;
escape_number2 = -1;
escape_CSI = false;
escape_OSC = false;
escape_sequence = false;
escape_number_seperator = false;
}
} else if(c == 0x0d) {
insertPlainText(QChar('\n'));
} else if(c.isPrint() || c == '\n') {
insertPlainText(QChar(c));
} else if(mParseAnsi) {
if(c == 127 || c == 8) { // delete or backspace, respectively
textCursor().deletePreviousChar();
} else if(c==27) { // escape key
escape_sequence = true;
} else if(c==0x9b) { // CSI - equivalent to esc [
escape_sequence = true;
escape_CSI = true;
} else if(c==0x9d) { // OSC - equivalent to esc ]
escape_sequence = true;
escape_OSC = true;
}
} else if(!c.isNull()) {
insertPlainText("[");
QByteArray num;
num.setNum(c.toAscii());
insertPlainText(num);
insertPlainText("]");
}
}
void KTextEditVT::insertVTText(const QByteArray & string)
{
int size= string.size();
for(int i =0; i < size; i++)
insertVTChar(QChar(string.at(i)));
}
void KTextEditVT::insertVTText(const QString & string)
{
int size= string.size();
for(int i =0; i < size; i++)
insertVTChar(string.at(i));
}
void KTextEditVT::setParseAnsiEscapeCodes(bool parseAnsi)
{
mParseAnsi = parseAnsi;
}
bool KTextEditVT::parseAnsiEscapeCodes() const
{
return mParseAnsi;
}

View File

@ -0,0 +1,104 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 2008 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _KTextEditVT_h_
#define _KTextEditVT_h_
#include <QtGui/QTextEdit>
#include <kdemacros.h>
/*
* \class KTextEditVT
* \brief The KTextEditVT class provides a widget that is used to edit and display
* both plain and rich text with the additional function of being able to
* programmatically append VT100 formatted text. For example to display the output
* from console programs.
*
* This class can be used to display the output of VT100 formatted text with
* ANSI escape code - for example output from the command 'ls --color'.
*
* Only a very limited number of ansi escapes sequences will have an affect. Unrecognised
* ansi escape sequences will be ignored and not displayed. Patches are welcome to support
* more of the sequences.
*
* This output can be then be inserted at the current cursor position by calling
* insertVTText(string);
*
* For example:
*
* \code
* insertVTText(QString("Hi") + QChar(08) + "ello");
* \endcode
*
* will insert the text "Hello" at the current character position.
* (Character 08 is the literal backspace character. Treated as equivalent to character 127)
*/
class KDE_EXPORT KTextEditVT : public QTextEdit
{
Q_OBJECT
Q_PROPERTY( bool parseAnsiEscapeCodes READ parseAnsiEscapeCodes WRITE setParseAnsiEscapeCodes )
public:
KTextEditVT(QWidget* parent);
/** Whether to parse ANSI display code. If turned off the escape sequence will be shown literally. */
bool parseAnsiEscapeCodes() const;
public Q_SLOTS:
/** Set whether to parse ANSI display code. If turned off the escape sequence will be shown literally. */
void setParseAnsiEscapeCodes(bool displayall);
/** Insert the given string at the current position based on the current state.
* This is interpreted in a VT100 encoding. Backspace and delete will delete the previous character,
* escape sequences can move the cursor and set the current color etc.
*
* This just calls insertVTChar for each character in the string
*/
void insertVTText(const QByteArray & string);
/** Insert the given string at the current position based on the current state.
* This is interpreted in a VT100 encoding. Backspace and delete will delete the previous character,
* escape sequences can move the cursor and set the current color etc.
*
* This just calls insertVTChar for each character in the string
*/
void insertVTText(const QString & string);
/** Insert the given character at the current position based on the current state.
* This is interpreted in a VT100 encoding. Backspace and delete will delete the previous character,
* escape sequences can move the cursor and set the current color etc.
*/
void insertVTChar(const QChar & c);
private:
bool mParseAnsi;
bool escape_sequence;
bool escape_CSI;
bool escape_OSC;
int escape_number1;
int escape_number2;
bool escape_number_seperator;
QChar escape_code;
};
#endif

View File

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp *.cc -o $podir/processui.pot

View File

@ -0,0 +1,149 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 2006-2007 John Tapsell <john.tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
/* For getuid() */
#include <unistd.h>
#include <sys/types.h>
#include <QVariant>
#include <kdebug.h>
#include "ProcessModel.h"
#include "ProcessModel_p.h"
#include "ProcessFilter.h"
bool ProcessFilter::filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const
{
if( (mFilter == AllProcesses || mFilter == AllProcessesInTreeForm)
&& filterRegExp().isEmpty()) return true; //Shortcut for common case
ProcessModel *model = static_cast<ProcessModel *>(sourceModel());
const KSysGuard::Process *process;
if(model->isSimpleMode()) {
if(source_parent.isValid()) {
kDebug() << "Serious error with data. In simple mode, there should be no children";
return true;
}
process = model->getProcessAtIndex(source_row);
} else {
KSysGuard::Process *parent_process = NULL;
if(source_parent.isValid()) {
parent_process = reinterpret_cast<KSysGuard::Process *>(source_parent.internalPointer());
Q_ASSERT(parent_process);
} else {
//if(!model->isSimpleMode()) {
parent_process = model->getProcess(0); //Get our 'special' process which should have the root init child
Q_ASSERT(parent_process);
//}
}
if(!model->isSimpleMode() && source_row >= parent_process->children.size()) {
kDebug() << "Serious error with data. Source row requested for a non existent row. Requested " << source_row << " of " << parent_process->children.size() << " for " << parent_process->pid;
return true;
}
process = parent_process->children.at(source_row);
}
Q_ASSERT(process);
long uid = process->uid;
long euid = process->euid;
bool accepted = true;
switch(mFilter) {
case AllProcesses:
case AllProcessesInTreeForm:
break;
case SystemProcesses:
if( uid >= 100 && model->canUserLogin(uid))
accepted = false;
break;
case UserProcesses:
if( (uid < 100 || !model->canUserLogin(uid)) && (euid < 100 || !model->canUserLogin(euid)))
accepted = false;
break;
case OwnProcesses: {
long ownuid = getuid();
if(uid != ownuid && process->suid != ownuid && process->fsuid != ownuid && euid != ownuid)
accepted = false;
break;
}
case ProgramsOnly:
if(process->tty.isEmpty()) {
if(!model->hasGUIWindow(process->pid))
accepted = false;
} else {
// login and getty kinda _are_ the tty, so I do not really count them as 'programs'. So make a special case and hide them
// Their ppid are 1 (init) so by checking we try to avoid false matches, and speed up checking overall
if(process->parent_pid == 1 && (process->name == "login" || process->name.endsWith("getty")))
accepted = false;
}
break;
default:
break;
}
if(accepted) {
if(filterRegExp().isEmpty()) return true;
//Allow the user to search by PID
if(QString::number(process->pid).contains(filterRegExp())) return true;
//None of our tests have rejected it. Pass it on to qsortfilterproxymodel's filter
if(QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent))
return true;
}
//We did not accept this row at all.
//If we are in flat mode, then give up now
if(mFilter != AllProcessesInTreeForm)
return false;
//one of our children might be accepted, so accept this row if our children are accepted.
QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent);
for(int i = 0 ; i < sourceModel()->rowCount(source_index); i++) {
if(filterAcceptsRow(i, source_index)) return true;
}
return false;
}
bool ProcessFilter::lessThan(const QModelIndex &left, const QModelIndex &right) const
{
if(right.isValid() && left.isValid()) {
Q_ASSERT(left.model());
Q_ASSERT(right.model());
QVariant l = (left.model() ? left.model()->data(left, ProcessModel::SortingValueRole) : QVariant());
QVariant r = (right.model() ? right.model()->data(right, ProcessModel::SortingValueRole) : QVariant());
if(l.isValid() && r.isValid())
return l.toDouble() < r.toDouble();
}
return QSortFilterProxyModel::lessThan(left,right);
}
void ProcessFilter::setFilter(State filter) {
mFilter = filter;
filterChanged();//Tell the proxy view to refresh all its information
}
#include "ProcessFilter.moc"

View File

@ -0,0 +1,56 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org>
Copyright (c) 2006 John Tapsell <john.tapsell@kdemail.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESSFILTER_H_
#define PROCESSFILTER_H_
#include <QtGui/QSortFilterProxyModel>
#include <QtCore/QObject>
#include <kdemacros.h>
class QModelIndex;
class KDE_EXPORT ProcessFilter : public QSortFilterProxyModel
{
Q_OBJECT
Q_ENUMS(State)
public:
enum State {AllProcesses=0,AllProcessesInTreeForm, SystemProcesses, UserProcesses, OwnProcesses, ProgramsOnly};
ProcessFilter(QObject *parent=0) : QSortFilterProxyModel(parent) {mFilter = AllProcesses;}
virtual ~ProcessFilter() {}
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
State filter() const {return mFilter; }
public Q_SLOTS:
void setFilter(State index);
protected:
virtual bool filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const;
State mFilter;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,143 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org>
Copyright (c) 2006 John Tapsell <john.tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESSMODEL_H_
#define PROCESSMODEL_H_
#include <QtCore/QAbstractItemModel>
#include <kdemacros.h>
namespace KSysGuard {
class Processes;
class Process;
}
class ProcessModelPrivate;
class KDE_EXPORT ProcessModel : public QAbstractItemModel
{
Q_OBJECT
Q_ENUMS(Units)
public:
ProcessModel(QObject* parent = 0, const QString &host = QString() );
virtual ~ProcessModel();
/* Functions for our Model for QAbstractItemModel*/
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount ( const QModelIndex & parent = QModelIndex() ) const;
QVariant data(const QModelIndex &index, int role) const;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const;
QModelIndex parent ( const QModelIndex & index ) const;
bool hasChildren ( const QModelIndex & parent) const;
/* Functions for setting the model */
/** Setup the column headings by inserting the appropriate headings into the model.
* Can be called more than once to retranslate the headings if the system language changes.
*/
void setupHeader();
/** Update data. You can pass in the time between updates to only update if there hasn't
* been an update within the last @p updateDurationMSecs milliseconds */
void update(int updateDurationMSecs = 0);
/** Return a string with the pid of the process and the name of the process. E.g. 13343: ksyguard
*/
QString getStringForProcess(KSysGuard::Process *process) const;
KSysGuard::Process *getProcess(long long pid);
/** This is used from ProcessFilter to get the process at a given index when in flat mode */
KSysGuard::Process *getProcessAtIndex(int index) const;
/** Returns whether this user can log in or not.
* @see mUidCanLogin
*/
bool canUserLogin(long long uid) const;
/** In simple mode, everything is flat, with no icons, few if any colors, no xres etc.
* This can be changed at any time. It is a fairly quick operation. Basically it resets the model
*/
void setSimpleMode(bool simple);
/** In simple mode, everything is flat, with no icons, few if any colors, no xres etc
*/
bool isSimpleMode() const;
/** Returns the total amount of physical memory in the machine. */
long long totalMemory() const;
/** This returns a QModelIndex for the given process. It has to look up the parent for this pid, find the offset this
* pid is from the parent, and return that. It's not that slow, but does involve a couple of hash table lookups.
*/
QModelIndex getQModelIndex ( KSysGuard::Process *process, int column) const;
/** Whether this is showing the processes for the current machine
*/
bool isLocalhost() const;
/** The host name that this widget is showing the processes of */
QString hostName() const;
/** Whether this process has a GUI window */
bool hasGUIWindow(long long pid) const;
/** Returns for process controller pointer for this model
*/
KSysGuard::Processes *processController(); ///The processes instance
/** The headings in the model. The order here is the order that they are shown
* in. If you change this, make sure you also change the
* setup header function, and make sure you increase PROCESSHEADERVERSION. This will ensure
* that old saved settings won't be used
*/
#define PROCESSHEADERVERSION 1
enum { HeadingName=0, HeadingUser, HeadingPid, HeadingTty, HeadingNiceness, HeadingCPUUsage, HeadingVmSize, HeadingMemory, HeadingSharedMemory, HeadingCommand, HeadingXTitle };
enum { UidRole = Qt::UserRole, SortingValueRole, WindowIdRole, TotalMemoryRole, NumberOfProcessorsRole };
bool showTotals() const;
/** When displaying memory sizes, this is the units it should be displayed in */
enum Units { UnitsKB, UnitsMB, UnitsGB };
/** Set the units memory sizes etc should be displayed in */
void setUnits(Units units);
/** The units memory sizes etc should be displayed in */
Units units() const;
/** Take an amount in kb, and return a string in the units set by setUnits() */
QString formatMemoryInfo(long amountInKB) const;
/** Retranslate the GUI, for when the system language changes */
void retranslateUi();
public Q_SLOTS:
/** Whether to show the total cpu for the process plus all of its children */
void setShowTotals(bool showTotals);
private:
ProcessModelPrivate* const d;
friend class ProcessModelPrivate;
};
#endif

View File

@ -0,0 +1,186 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 2006-2007 John Tapsell <john.tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PROCESSMODEL_P_H_
#define PROCESSMODEL_P_H_
#include <kapplication.h>
#include <kuser.h>
#include <QPixmap>
#include <QObject>
#include <QList>
#include <QVariant>
#include <QHash>
#include <QSet>
#include <QTime>
#ifdef Q_WS_X11
#include <kwindowsystem.h>
#include <netwm.h>
#include <QtGui/QX11Info>
#include <X11/Xatom.h>
#include <kxerrorhandler.h>
#endif
#include "processcore/process.h"
#ifdef Q_WS_X11
struct WindowInfo {
QPixmap icon;
WId wid;
NETWinInfo *netWinInfo;
};
#endif
class ProcessModel;
class ProcessModelPrivate : public QObject
{
Q_OBJECT
public:
ProcessModelPrivate();
~ProcessModelPrivate();
public slots:
#ifdef Q_WS_X11
/** When an X window is changed, this is called */
void windowChanged(WId wid, unsigned int properties);
/** When an X window is created, this is called
*/
void windowAdded(WId wid);
/** When an X window is closed, this is called
*/
void windowRemoved(WId wid);
#endif
/** Change the data for a process. This is called from KSysGuard::Processes
* if @p onlyCpuOrMem is set, only the total cpu usuage is updated.
* process->changes contains a bitfield of what has been changed
*/
void processChanged(KSysGuard::Process *process, bool onlyCpuOrMem);
/** Called from KSysGuard::Processes
* This indicates we are about to insert a process in the model. Emit the appropriate signals
*/
void beginInsertRow( KSysGuard::Process *parent);
/** Called from KSysGuard::Processes
* We have finished inserting a process
*/
void endInsertRow();
/** Called from KSysGuard::Processes
* This indicates we are about to remove a process in the model. Emit the appropriate signals
*/
void beginRemoveRow( KSysGuard::Process *process);
/** Called from KSysGuard::Processes
* We have finished removing a process
*/
void endRemoveRow();
/** Called from KSysGuard::Processes
* This indicates we are about to move a process in the model from one parent process to another. Emit the appropriate signals
*/
void beginMoveProcess(KSysGuard::Process *process, KSysGuard::Process *new_parent);
/** Called from KSysGuard::Processes
* We have finished moving a process
*/
void endMoveRow();
public:
/** On X11 system, connects to the signals emitted when windows are created/destroyed */
void setupWindows();
/** Connects to the host */
void setupProcesses();
/** A mapping of running,stopped,etc to a friendly description like 'Stopped, either by a job control signal or because it is being traced.'*/
QString getStatusDescription(KSysGuard::Process::ProcessStatus status) const;
/** Return a qt markup tooltip string for a local user. It will have their full name etc.
* This will be slow the first time, as it practically indirectly reads the whole of /etc/passwd
* But the second time will be as fast as hash lookup as we cache the result
*/
inline QString getTooltipForUser(const KSysGuard::Process *process) const;
/** Return a username for a local user if it can, otherwise just their uid.
* This may have been given from the result of "ps" (but not necessarily). If it's not found, then it
* needs to find out the username from the uid. This will be slow the first time, as it practically indirectly reads the whole of /etc/passwd
* But the second time will be as fast as hash lookup as we cache the result
*
* If withuid is set, and the username is found, return: "username (Uid: uid)"
*/
inline QString getUsernameForUser(long long uid, bool withuid) const;
/** Return the groupname for a given gid. This is in the form of "gid" if not known, or
* "groupname (Uid: gid)" if known.
*/
inline QString getGroupnameForGroup(long long gid) const;
/** @see setIsLocalhost */
bool mIsLocalhost;
/** A caching hash for tooltips for a user.
* @see getTooltipForUser */
mutable QHash<long long,QString> mUserTooltips;
/** A caching hash for username for a user uid, or just their uid if it can't be found (as a long long)
* @see getUsernameForUser */
mutable QHash<long long, QString> mUserUsername;
/** A mapping of a user id to whether this user can log in. We have to guess based on the shell. All are set to true to non localhost.
* It is set to:
* 0 if the user cannot login
* 1 is the user can login
* The reason for using an int and not a bool is so that we can do mUidCanLogin.value(uid,-1) and thus we get a tristate for whether
* they are logged in, not logged in, or not known yet.
* */
mutable QHash<long long, int> mUidCanLogin;
/** A translated list of headings (column titles) in the order we want to display them. Used in headerData() */
QStringList mHeadings;
QMultiHash< long long, WindowInfo> mPidToWindowInfo; ///Map a process pid to X window info if available
QHash< WId, long long> mWIdToPid; ///Map an X window id to a process pid
bool mShowChildTotals; ///If set to true, a parent will return the CPU usage of all its children recursively
bool mSimple; ///In simple mode, the model returns everything as flat, with no icons, etc. This is set by changing cmbFilter
QTime mLastUpdated; ///Time that we last updated the processes.
long long mMemTotal; /// the total amount of physical memory in kb in the machine. We can used this to determine the percentage of memory an app is using
int mNumProcessorCores; /// The number of (enabled) processor cores in the this machine
KSysGuard::Processes *mProcesses; ///The processes instance
bool mIsChangingLayout;
QPixmap mBlankPixmap; ///Used to pad out process names which don't have an icon
/** When displaying memory sizes, this is the units it should be displayed in */
int mUnits;
/** The hostname */
QString mHostName;
ProcessModel* q;
};
#endif

View File

@ -0,0 +1,155 @@
<ui version="4.0" >
<class>ProcessWidget</class>
<widget class="QWidget" name="ProcessWidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>490</width>
<height>472</height>
</rect>
</property>
<layout class="QVBoxLayout" >
<property name="leftMargin" >
<number>0</number>
</property>
<property name="topMargin" >
<number>0</number>
</property>
<property name="rightMargin" >
<number>0</number>
</property>
<property name="bottomMargin" >
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" >
<item>
<widget class="QPushButton" name="btnKillProcess" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>&amp;Kill Process</string>
</property>
<property name="flat" >
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="KLineEdit" name="txtFilter" >
<property name="styleSheet" >
<string>QLineEdit { padding-right: 16; }</string>
</property>
<property name="urlDropsEnabled" >
<bool>false</bool>
</property>
<property name="clickMessage" >
<string>Quick search</string>
</property>
<property name="showClearButton" stdset="0" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cmbFilter" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Fixed" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maxCount" >
<number>10</number>
</property>
<property name="sizeAdjustPolicy" >
<enum>QComboBox::AdjustToContentsOnFirstShow</enum>
</property>
<item>
<property name="text" >
<string>All Processes</string>
</property>
</item>
<item>
<property name="text" >
<string>All Processes, Tree</string>
</property>
</item>
<item>
<property name="text" >
<string>System Processes</string>
</property>
</item>
<item>
<property name="text" >
<string>User Processes</string>
</property>
</item>
<item>
<property name="text" >
<string>Own Processes</string>
</property>
</item>
<item>
<property name="text" >
<string>Programs Only</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTreeView" name="treeView" >
<property name="editTriggers" >
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors" >
<bool>true</bool>
</property>
<property name="selectionMode" >
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<property name="selectionBehavior" >
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="rootIsDecorated" >
<bool>false</bool>
</property>
<property name="uniformRowHeights" >
<bool>true</bool>
</property>
<property name="itemsExpandable" >
<bool>true</bool>
</property>
<property name="sortingEnabled" >
<bool>true</bool>
</property>
<property name="animated" >
<bool>false</bool>
</property>
<property name="allColumnsShowFocus" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KLineEdit</class>
<extends>QLineEdit</extends>
<header>klineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,180 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 1999 Chris Schlaeger <cs@kde.org>
Copyright (c) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <klocale.h>
#include <kdebug.h>
#include "ReniceDlg.moc"
#include <QListWidget>
#include <QButtonGroup>
#include "ui_ReniceDlgUi.h"
#include "processcore/process.h"
ReniceDlg::ReniceDlg(QWidget* parent, const QStringList& processes, int currentCpuPrio, int currentCpuSched, int currentIoPrio, int currentIoSched )
: KDialog( parent )
{
setObjectName( "Renice Dialog" );
setModal( true );
setCaption( i18n("Renice Process") );
setButtons( Ok | Cancel );
showButtonSeparator( true );
previous_cpuscheduler = 0;
connect( this, SIGNAL( okClicked() ), SLOT( slotOk() ) );
if(currentIoSched == KSysGuard::Process::None) {
// CurrentIoSched == 0 means that the priority is set automatically.
// Using the formula given by the linux kernel Documentation/block/ioprio
currentIoPrio = (currentCpuPrio+20)/5;
}
if(currentIoSched == (int)KSysGuard::Process::BestEffort && currentIoPrio == (currentCpuPrio+20)/5) {
// Unfortunately, in linux you can't ever set a process back to being None. So we fake it :)
currentIoSched = KSysGuard::Process::None;
}
ioniceSupported = (currentIoPrio != -2);
QWidget *widget = new QWidget(this);
setMainWidget(widget);
ui = new Ui_ReniceDlgUi();
ui->setupUi(widget);
ui->listWidget->insertItems(0, processes);
cpuScheduler = new QButtonGroup(this);
cpuScheduler->addButton(ui->radioNormal, (int)KSysGuard::Process::Other);
cpuScheduler->addButton(ui->radioBatch, (int)KSysGuard::Process::Batch);
cpuScheduler->addButton(ui->radioFIFO, (int)KSysGuard::Process::Fifo);
cpuScheduler->addButton(ui->radioRR, (int)KSysGuard::Process::RoundRobin);
if(currentCpuSched >= 0) { //negative means none of these
QAbstractButton *sched = cpuScheduler->button(currentCpuSched);
if(sched) {
sched->setChecked(true); //Check the current scheduler
previous_cpuscheduler = currentCpuSched;
}
}
cpuScheduler->setExclusive(true);
ioScheduler = new QButtonGroup(this);
ioScheduler->addButton(ui->radioIONormal, (int)KSysGuard::Process::None);
ioScheduler->addButton(ui->radioIdle, (int)KSysGuard::Process::Idle);
ioScheduler->addButton(ui->radioRealTime, (int)KSysGuard::Process::RealTime);
ioScheduler->addButton(ui->radioBestEffort, (int)KSysGuard::Process::BestEffort);
if(currentIoSched >= 0) { //negative means none of these
QAbstractButton *iosched = ioScheduler->button(currentIoSched);
if(iosched)
iosched->setChecked(true); //Check the current io scheduler
}
ioScheduler->setExclusive(true);
if(ioniceSupported)
ui->sliderIO->setValue(currentIoPrio);
ui->sliderCPU->setValue(currentCpuPrio);
ui->imgCPU->setPixmap( KIcon("cpu").pixmap(128, 128) );
ui->imgIO->setPixmap( KIcon("drive-harddisk").pixmap(128, 128) );
newCPUPriority = 40;
connect(cpuScheduler, SIGNAL(buttonClicked(int)), this, SLOT(cpuSchedulerChanged(int)));
connect(ioScheduler, SIGNAL(buttonClicked(int)), this, SLOT(updateUi()));
connect(ui->sliderCPU, SIGNAL(valueChanged(int)), this, SLOT(cpuSliderChanged(int)));
connect(ui->sliderIO, SIGNAL(valueChanged(int)), this, SLOT(ioSliderChanged(int)));
updateUi();
}
void ReniceDlg::ioSliderChanged(int value) {
ui->sliderIO->setToolTip(QString::number(value));
}
void ReniceDlg::cpuSchedulerChanged(int value) {
if(value != previous_cpuscheduler) {
if( (value == (int)KSysGuard::Process::Other || value == KSysGuard::Process::Batch) &&
(previous_cpuscheduler == (int)KSysGuard::Process::Fifo || previous_cpuscheduler == (int)KSysGuard::Process::RoundRobin)) {
int slider = -ui->sliderCPU->value() * 2 / 5 + 20;
setSliderRange();
ui->sliderCPU->setValue( slider );
} else if( (previous_cpuscheduler == (int)KSysGuard::Process::Other || previous_cpuscheduler == KSysGuard::Process::Batch) &&
(value == (int)KSysGuard::Process::Fifo || value == (int)KSysGuard::Process::RoundRobin)) {
int slider = (-ui->sliderCPU->value() + 20) * 5 / 2;
setSliderRange();
ui->sliderCPU->setValue( slider );
}
}
previous_cpuscheduler = value;
updateUi();
}
void ReniceDlg::cpuSliderChanged(int value) {
if(cpuScheduler->checkedId() == (int)KSysGuard::Process::Other || cpuScheduler->checkedId() == (int)KSysGuard::Process::Batch) {
if( ioScheduler->checkedId() == -1 || ioScheduler->checkedId() == (int)KSysGuard::Process::None) {
//ionice is 'Normal', thus automatically calculated based on cpunice
ui->sliderIO->setValue((value+20)/5);
}
}
ui->sliderCPU->setToolTip(QString::number(value));
}
void ReniceDlg::updateUi() {
bool cpuPrioEnabled = ( cpuScheduler->checkedId() != -1);
bool ioPrioEnabled = ( ioniceSupported && ioScheduler->checkedId() != -1 && ioScheduler->checkedId() != (int)KSysGuard::Process::Idle && ioScheduler->checkedId() != (int)KSysGuard::Process::None);
ui->sliderCPU->setEnabled(cpuPrioEnabled);
ui->lblCpuLow->setEnabled(cpuPrioEnabled);
ui->lblCpuHigh->setEnabled(cpuPrioEnabled);
ui->sliderIO->setEnabled(ioPrioEnabled);
ui->lblIOLow->setEnabled(ioPrioEnabled);
ui->lblIOHigh->setEnabled(ioPrioEnabled);
setSliderRange();
cpuSliderChanged(ui->sliderCPU->value());
ioSliderChanged(ui->sliderIO->value());
}
void ReniceDlg::setSliderRange() {
if(cpuScheduler->checkedId() == (int)KSysGuard::Process::Other || cpuScheduler->checkedId() == (int)KSysGuard::Process::Batch) {
//The slider is setting the priority, so goes from 19 to -20. We cannot actually do this with a slider, so instead we go from -19 to 20, and negate later
if(ui->sliderCPU->value() > 20) ui->sliderCPU->setValue(20);
ui->sliderCPU->setInvertedAppearance(true);
ui->sliderCPU->setMinimum(-19);
ui->sliderCPU->setMaximum(20);
ui->sliderCPU->setTickInterval(5);
} else {
if(ui->sliderCPU->value() < 1) ui->sliderCPU->setValue(1);
ui->sliderCPU->setInvertedAppearance(false);
ui->sliderCPU->setMinimum(1);
ui->sliderCPU->setMaximum(99);
ui->sliderCPU->setTickInterval(12);
}
}
void ReniceDlg::slotOk()
{
newCPUPriority = ui->sliderCPU->value();
newIOPriority = ui->sliderIO->value();
newCPUSched = cpuScheduler->checkedId();
newIOSched = ioScheduler->checkedId();
}

View File

@ -0,0 +1,67 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 2006-2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _ReniceDlg_h_
#define _ReniceDlg_h_
#include <kdialog.h>
class Ui_ReniceDlgUi;
class QButtonGroup;
/**
* This class creates and handles a simple dialog to change the scheduling
* priority of a process.
*/
class ReniceDlg : public KDialog
{
Q_OBJECT
public:
/** Let the user specify the new priorities of the @p processes given, using the given current values.
* @p currentCpuSched The current Cpu Scheduler of the processes. Set to -1 to they have different schedulers
* @p currentIoSched The current I/O Scheduler of the processes. Set to -1 to they have different schedulers. Leave as the default -2 if not supported
*/
ReniceDlg(QWidget* parent, const QStringList& processes, int currentCpuPrio, int currentCpuSched, int currentIoPrio=-2, int currentIoSched=-2);
int newCPUPriority;
int newIOPriority;
int newCPUSched;
int newIOSched;
bool ioniceSupported;
public Q_SLOTS:
void slotOk();
void updateUi();
void cpuSliderChanged(int value);
void ioSliderChanged(int value);
void cpuSchedulerChanged(int value);
private:
void setSliderRange();
Ui_ReniceDlgUi *ui;
QButtonGroup *cpuScheduler;
QButtonGroup *ioScheduler;
int previous_cpuscheduler;
};
#endif

View File

@ -0,0 +1,443 @@
<ui version="4.0" >
<class>ReniceDlgUi</class>
<widget class="QWidget" name="ReniceDlgUi" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>652</width>
<height>397</height>
</rect>
</property>
<layout class="QVBoxLayout" >
<property name="spacing" >
<number>0</number>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lblMessage1" >
<property name="text" >
<string>Change scheduling priority for:</string>
</property>
<property name="alignment" >
<set>Qt::AlignVCenter</set>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QListWidget" name="listWidget" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="contextMenuPolicy" >
<enum>Qt::NoContextMenu</enum>
</property>
<property name="selectionMode" >
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="uniformItemSizes" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="imgCPU" >
<property name="minimumSize" >
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="text" >
<string/>
</property>
<property name="textFormat" >
<enum>Qt::AutoText</enum>
</property>
<property name="pixmap" >
<pixmap/>
</property>
</widget>
</item>
<item row="0" column="1" >
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="label" >
<property name="font" >
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text" >
<string>CPU Scheduler</string>
</property>
<property name="alignment" >
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioNormal" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The standard time-sharing scheduler for processes without special requirements.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Normal Scheduling: Default Linux time-sharing (Other)&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;">Normal&lt;span style=" font-weight:400;"> is the standard Linux time-sharing scheduler that is intended for all processes that do not require special static priority real-time mechanisms. The process to run is chosen from the list of other&lt;/span> Normal&lt;span style=" font-weight:400;"> or &lt;/span>Batch&lt;span style=" font-weight:400;"> processes based on a dynamic priority that is determined only inside this list. The dynamic priority is based on the priority level given and increased for each time-quantum the process is ready to run, but denied to run by the scheduler. This ensures fair progress among all Normal processes.&lt;/span>&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>Normal</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioBatch" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">For CPU-intensive non-interactive processes. Process is mildly disfavored in scheduling decisions.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Batch Scheduling&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-weight:600;">&lt;span style=" font-weight:400; font-style:italic;">(Since Linux 2.6.16.)&lt;/span>&lt;span style=" font-weight:400;"> This policy is similar to &lt;/span>Normal&lt;span style=" font-weight:400;">, except that this policy will cause the scheduler to always assume that the process is CPU-intensive. Consequently, the scheduler will apply a small scheduling penalty so that this process is mildly disfavored in scheduling decisions. This policy is useful for workloads that are non-interactive, but do not want to lower their nice value, and for workloads that want a deterministic scheduling policy without interactivity causing extra preemptions (between the workload's tasks).&lt;/span>&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>Batch</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioRR" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Process will run whenever runnable. Higher priority than Normal or Batch. Has Timeslicing.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Round Robin Scheduling&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Round Robin&lt;/span> is a simple enhancement of &lt;span style=" font-weight:600;">FIFO&lt;/span>. Everything described below for &lt;span style=" font-weight:600;">FIFO&lt;/span> also applies to &lt;span style=" font-weight:600;">Round Robin&lt;/span>, except that each process is only allowed to run for a maximum time quantum.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>Round Robin</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioFIFO" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Process will run whenever runnable. Higher priority than Normal or Batch. No timeslicing.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">First In-First Out Scheduling&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">When a &lt;span style=" font-weight:600;">FIFO&lt;/span> process becomes runnable, it will always immediately preempt any currently running &lt;span style=" font-weight:600;">Normal&lt;/span> or &lt;span style=" font-weight:600;">Batch&lt;/span> process.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>FIFO</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="2" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="3" >
<widget class="QLabel" name="imgIO" >
<property name="minimumSize" >
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>128</width>
<height>128</height>
</size>
</property>
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap/>
</property>
</widget>
</item>
<item row="0" column="4" >
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="label_3" >
<property name="maximumSize" >
<size>
<width>16777215</width>
<height>26</height>
</size>
</property>
<property name="font" >
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text" >
<string>I/O Scheduler</string>
</property>
<property name="alignment" >
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioIONormal" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Process's priority is based on the CPU priority&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Normal Scheduling&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">This is the same as &lt;span style=" font-weight:600;">Best Effort&lt;/span> scheduling, except that the priority is calculated automatically based on the CPU priority. Processes with a higher priority will take priority for access to the hard disk. Programs running at the same &lt;span style=" font-weight:600;">Best Effort/Normal&lt;/span> priority are served in a &lt;span style=" font-weight:600;">Round Robin&lt;/span> fashion.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>Normal</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioIdle" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Process can only use the hard disk when no other process has used it very recently.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Idle Scheduling&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">A program running with &lt;span style=" font-weight:600;">Idle&lt;/span> I/O priority will only get disk time when no other program has asked for disk I/O for a defined grace period. The impact of &lt;span style=" font-weight:600;">Idle&lt;/span> I/O processes on normal system activity should be zero. Priority is not applicable to this scheduling class.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>Idle</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioBestEffort" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Process is given higher priority to access the hard disk than Normal.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Best Effort Scheduling&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Processes with a higher priority will take priority for access to the hard disk. Programs running at the same &lt;span style=" font-weight:600;">Best Effort/Normal&lt;/span> priority are served in a &lt;span style=" font-weight:600;">Round Robin&lt;/span> fashion.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>Best Effort</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioRealTime" >
<property name="toolTip" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Process gets immediate access to the hard disk whenever needed, regardless of what else is going on.&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="whatsThis" >
<string>&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">&lt;span style=" font-weight:600;">Real Time Scheduling&lt;/span>&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The &lt;span style=" font-weight:600;">Real Time&lt;/span> scheduling class is given first access to the disk, regardless of what else is going on in the system. Thus the &lt;span style=" font-weight:600;">Real Time&lt;/span> class needs to be used with some care, as it can starve other processes. As with the &lt;span style=" font-weight:600;">Best Effort&lt;/span> class, 8 priority levels are defined denoting how big a time slice a given process will receive on each scheduling window&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="text" >
<string>Real Time</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2" >
<layout class="QGridLayout" >
<item row="0" column="0" colspan="2" >
<widget class="QSlider" name="sliderCPU" >
<property name="minimum" >
<number>-19</number>
</property>
<property name="maximum" >
<number>20</number>
</property>
<property name="value" >
<number>0</number>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance" >
<bool>true</bool>
</property>
<property name="tickPosition" >
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval" >
<number>5</number>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="lblCpuLow" >
<property name="text" >
<string>Low Priority</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="lblCpuHigh" >
<property name="text" >
<string>High Priority</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="2" >
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::MinimumExpanding</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="3" colspan="2" >
<layout class="QGridLayout" >
<item row="0" column="0" colspan="2" >
<widget class="QSlider" name="sliderIO" >
<property name="maximum" >
<number>7</number>
</property>
<property name="pageStep" >
<number>1</number>
</property>
<property name="value" >
<number>3</number>
</property>
<property name="sliderPosition" >
<number>3</number>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance" >
<bool>true</bool>
</property>
<property name="tickPosition" >
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval" >
<number>1</number>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="lblIOLow" >
<property name="text" >
<string>Low Priority</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLabel" name="lblIOHigh" >
<property name="text" >
<string>High Priority</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<tabstops>
<tabstop>listWidget</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,17 @@
[Global]
PluginName=KSysGuardWidgets
[KSysGuardProcessList]
ToolTip=A list of processes (programs) running.
WhatsThis=A widget for showing all the processes running along with their memory usage and other details.
Group=KSysGuard (KDE)
ConstructorArgs=(parent)
IncludeFile=ksysguardprocesslist.h
[KTextEditVT]
ToolTip=A text box suitable for displaying output from VT console-based programs.
WhatsThis=A widget for displaying out from console based programs. Some VT100 style commands are interpreted (For example to change the color) as well as some non-printable characters (backspace/delete etc will delete the last character.). For example the output from 'ls --color' can be displayed.
Group=KSysGuard (KDE)
ConstructorArgs=(parent)
IncludeFile=KTextEditVT.h

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,202 @@
/*
KSysGuard, the KDE System Guard
Copyright (c) 1999, 2000 Chris Schlaeger <cs@kde.org>
Copyright (c) 2006 John Tapsell <john.tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _KSysGuardProcessList_h_
#define _KSysGuardProcessList_h_
#include <QtGui/QWidget>
#include <kapplication.h>
#include "ProcessModel.h"
#include "ProcessFilter.h"
#include "processes.h"
class QShowEvent;
class QHideEvent;
class QLineEdit;
class QTreeView;
struct KSysGuardProcessListPrivate;
extern KApplication* Kapp;
/**
* This widget implements a process list page. Besides the process
* list which is implemented as a ProcessList, it contains two
* comboxes and two buttons. The combo boxes are used to set the
* update rate and the process filter. The buttons are used to force
* an immediate update and to kill a process.
*/
class KDE_EXPORT KSysGuardProcessList : public QWidget
{
Q_OBJECT
Q_PROPERTY( bool showTotalsInTree READ showTotals WRITE setShowTotals )
Q_PROPERTY( ProcessFilter::State state READ state WRITE setState )
Q_PROPERTY( int updateIntervalMSecs READ updateIntervalMSecs WRITE setUpdateIntervalMSecs )
Q_PROPERTY( ProcessModel::Units units READ units WRITE setUnits )
Q_ENUMS( ProcessFilter::State )
Q_ENUMS( ProcessModel::Units )
public:
KSysGuardProcessList(QWidget* parent, const QString &hostName = QString());
virtual ~KSysGuardProcessList();
QLineEdit *filterLineEdit() const;
QTreeView *treeView() const;
/** Returns which processes we are currently filtering for and the way in which we show them.
* @see setState()
*/
ProcessFilter::State state() const;
/** Returns the number of milliseconds that have to elapse before updating the list of processes */
int updateIntervalMSecs() const;
/** Whether the widget will show child totals for CPU and Memory etc usage */
bool showTotals() const;
/** The units to display memory sizes etc in. E.g. kb/mb/gb */
ProcessModel::Units units() const;
/** Returns a list of the processes that have been selected by the user. */
QList<KSysGuard::Process *> selectedProcesses() const;
/** Save the current state of the widget to the given config group
*
* @param[in] cg Config group to add these settings to
* */
void saveSettings(KConfigGroup &cg);
/** Load the saved state of the widget from the given config group */
void loadSettings(const KConfigGroup &cg);
/** Returns the process model used. Use with caution. */
ProcessModel *processModel();
/** Restore the headings to the given state. */
void restoreHeaderState(const QByteArray & state);
public Q_SLOTS:
/** Inform the view that the user has changed the selection */
void selectionChanged();
/** Send a kill signal to all the processes that the user has selected. Pops up a dialog box to confirm with the user */
void killSelectedProcesses();
/** Send a signal to a list of given processes.
* @p pids A list of PIDs that should be sent the signal
* @p sig The signal to send.
* @return Whether the kill went ahead. True if successful or user cancelled. False if there was a problem
*/
bool killProcesses(const QList< long long> &pids, int sig);
/** Renice all the processes that the user has selected. Pops up a dialog box to ask for the nice value and confirm */
void reniceSelectedProcesses();
/** Change the CPU scheduler for the given of processes to the given scheduler, with the given scheduler priority.
* If the scheduler is Other or Batch, @p newCpuSchedPriority is ignored.
* @return Whether the cpu scheduler changing went ahead. True if successful or user cancelled. False if there was a problem
*/
bool changeCpuScheduler(const QList< long long> &pids, KSysGuard::Process::Scheduler newCpuSched, int newCpuSchedPriority);
/** Change the I/O scheduler for the given of processes to the given scheduler, with the given scheduler priority.
* If the scheduler is Other or Batch, @p newCpuSchedPriority is ignored.
* @return Whether the cpu scheduler changing went ahead. True if successful or user cancelled. False if there was a problem
*/
bool changeIoScheduler(const QList< long long> &pids, KSysGuard::Process::IoPriorityClass newIoSched, int newIoSchedPriority);
/** Renice the processes given to the given niceValue.
* @return Whether the kill went ahead. True if successful or user cancelled. False if there was a problem
* */
bool reniceProcesses(const QList<long long> &pids, int niceValue);
/** Fetch new process information and redraw the display */
void updateList();
/** Set which processes we are currently filtering for and the way in which we show them. */
void setState(ProcessFilter::State state);
/** Set the number of milliseconds that have to elapse before updating the list of processes */
void setUpdateIntervalMSecs(int intervalMSecs);
/** Set whether to show child totals for CPU and Memory etc usage */
void setShowTotals(bool showTotals);
/** Focus on a particular process, and select it */
void selectAndJumpToProcess(int pid);
/** The units to display memory sizes etc in. */
void setUnits(ProcessModel::Units unit);
/** Row was just inserted in the filter model */
void rowsInserted ( const QModelIndex & parent, int start, int end );
private Q_SLOTS:
/** Expand all the children, recursively, of the node given. Pass an empty QModelIndex to expand all the top level children */
void expandAllChildren(const QModelIndex &parent);
/** Expand init to show its children, but not the sub children processes. */
void expandInit();
/** Display a context menu for the column headings allowing the user to show or hide columns. */
void showColumnContextMenu(const QPoint &point);
/** Display a context menu for the given process allowing the user to kill etc the process */
void showProcessContextMenu(const QModelIndex &index);
/** Display a context menu for the selected processes allowing the user to kill etc the process */
void showProcessContextMenu(const QPoint &point);
/** Handle the situation where killing a process has failed - usually due to insufficent rights */
void killFailed();
/** Handle the situation where renicing a process has failed - usually due to insufficent rights */
void reniceFailed();
/** Handle the situation where ionice'ing a process has failed - usually due to insufficent rights */
void ioniceFailed();
/** Set state from combo box int value */
void setStateInt(int state);
/** Called when the text in the gui filter text box has changed */
void filterTextChanged(const QString &newText);
/** Called when one of the actions (kill, renice etc) is clicked etc */
void actionTriggered(QObject *object);
protected:
/** Inherit QWidget::showEvent(QShowEvent *) to enable the timer, for updates, when visible */
virtual void showEvent(QShowEvent*);
/** Inherit QWidget::hideEvent(QShowEvent *) to disable the timer, for updates, when not visible */
virtual void hideEvent(QHideEvent*);
/** Capture any change events sent to this widget. In particular QEvent::LanguageChange */
virtual void changeEvent ( QEvent * event );
bool eventFilter(QObject *obj, QEvent *event);
/** Retranslate the Ui as needed */
void retranslateUi();
private:
KSysGuardProcessListPrivate* const d;
};
#endif

View File

@ -0,0 +1,12 @@
set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_SOURCE_DIR}/../processcore/ )
kde4_add_unit_test(processtest processtest.cpp)
target_link_libraries(processtest processcore ${KDE4_KDECORE_LIBS} ${QT_QTTEST_LIBRARY})

View File

@ -0,0 +1,47 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <QtTest>
#include <QtCore>
#include <klocale.h>
#include <kdebug.h>
#include <qtest_kde.h>
#include <processui/ksysguardprocesslist.h>
#include "guitest.h"
void testGuiProcess::testGUI() {
KSysGuardProcessList processlist(NULL);
QTime t;
t.start();
for(int i =0; i < 10; i++) {
processlist.updateList();
}
kDebug() << "time taken: " << t.elapsed() << "ms";
}
QTEST_KDEMAIN(testGuiProcess, GUI)
#include "guitest.moc"

32
ksysguard/tests/guitest.h Normal file
View File

@ -0,0 +1,32 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef GUITESTPROCESS_H
#define GUITESTPROCESS_H
#include <QtCore/QObject>
class testGuiProcess: public QObject
{
Q_OBJECT
private slots:
void testGUI();
};
#endif

View File

@ -0,0 +1,124 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <QtTest>
#include <QtCore>
#include <klocale.h>
#include <qtest_kde.h>
#include <kdebug.h>
#include "processcore/processes.h"
#include "processcore/process.h"
#include "processtest.h"
void testProcess::testProcesses() {
KSysGuard::Processes *processController = KSysGuard::Processes::getInstance();
processController->updateAllProcesses();
QList<KSysGuard::Process *> processes = processController->getAllProcesses();
QSet<long> pids;
foreach( KSysGuard::Process *process, processes) {
if(process->pid == 0) continue;
QVERIFY(process->pid > 0);
QVERIFY(!process->name.isEmpty());
//test all the pids are unique
QVERIFY(!pids.contains(process->pid));
pids.insert(process->pid);
}
processController->updateAllProcesses();
QList<KSysGuard::Process *> processes2 = processController->getAllProcesses();
foreach( KSysGuard::Process *process, processes2) {
if(process->pid == 0) continue;
QVERIFY(process->pid > 0);
QVERIFY(!process->name.isEmpty());
//test all the pids are unique
if(!pids.contains(process->pid)) {
kDebug() << process->pid << " not found. " << process->name;
}
pids.remove(process->pid);
}
QVERIFY(processes2.size() == processes.size());
QCOMPARE(processes, processes2); //Make sure calling it twice gives the same results. The difference in time is so small that it really shouldn't have changed
}
unsigned long testProcess::countNumChildren(KSysGuard::Process *p) {
unsigned long total = p->children.size();
for(int i =0; i < p->children.size(); i++) {
total += countNumChildren(p->children[i]);
}
return total;
}
void testProcess::testProcessesTreeStructure() {
KSysGuard::Processes *processController = KSysGuard::Processes::getInstance();
processController->updateAllProcesses();
QList<KSysGuard::Process *> processes = processController->getAllProcesses();
foreach( KSysGuard::Process *process, processes) {
QCOMPARE(countNumChildren(process), process->numChildren);
for(int i =0; i < process->children.size(); i++) {
QVERIFY(process->children[i]->parent);
QCOMPARE(process->children[i]->parent, process);
}
}
}
void testProcess::testProcessesModification() {
//We will modify the tree, then re-call getProcesses and make sure that it fixed everything we modified
KSysGuard::Processes *processController = KSysGuard::Processes::getInstance();
processController->updateAllProcesses();
KSysGuard::Process *initProcess = processController->getProcess(1);
if(!initProcess || initProcess->numChildren < 3)
return;
QVERIFY(initProcess);
QVERIFY(initProcess->children[0]);
QVERIFY(initProcess->children[1]);
kDebug() << initProcess->numChildren;
initProcess->children[0]->parent = initProcess->children[1];
initProcess->children[1]->children.append(initProcess->children[0]);
initProcess->children[1]->numChildren++;
initProcess->numChildren--;
initProcess->children.removeAt(0);
}
void testProcess::testTime() {
//See how long it takes to get proccess information
KSysGuard::Processes *processController = KSysGuard::Processes::getInstance();
QTime t;
t.start();
for(int i =0; i < 100; i++)
processController->updateAllProcesses();
kDebug() << "Time elapsed: "<< t.elapsed() <<" ms, so " << t.elapsed()/100 << "ms" << endl;
QVERIFY(t.elapsed()/100 < 300); //It should take less than about 100ms. Anything longer than 300ms even on a slow system really needs to be optimised
}
QTEST_KDEMAIN_CORE(testProcess)
#include "processtest.moc"

View File

@ -0,0 +1,41 @@
/* This file is part of the KDE project
Copyright (C) 2007 John Tapsell <tapsell@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef TESTPROCESS_H
#define TESTPROCESS_H
#include <QtCore/QObject>
namespace KSysGuard
{
class Process;
}
class testProcess: public QObject
{
Q_OBJECT
private:
unsigned long countNumChildren(KSysGuard::Process *p);
private slots:
void testTime();
void testProcesses();
void testProcessesTreeStructure();
void testProcessesModification();
};
#endif

28
kworkspace/CMakeLists.txt Normal file
View File

@ -0,0 +1,28 @@
set(kworkspace_LIB_SRCS kdisplaymanager.cpp
kworkspace.cpp
kwindowlistmenu.cpp
)
set(ksmserver_xml ${KDEBASE_WORKSPACE_SOURCE_DIR}/ksmserver/org.kde.KSMServerInterface.xml)
qt4_add_dbus_interface( kworkspace_LIB_SRCS ${ksmserver_xml} ksmserver_interface )
set(kwin_xml ${KDEBASE_WORKSPACE_SOURCE_DIR}/kwin/org.kde.KWin.xml)
set_source_files_properties(${kwin_xml} PROPERTIES INCLUDE "interface_util.h")
qt4_add_dbus_interface( kworkspace_LIB_SRCS ${kwin_xml} kwin_interface )
kde4_add_library(kworkspace SHARED ${kworkspace_LIB_SRCS})
target_link_libraries(kworkspace ${KDE4_KDEUI_LIBS} ${X11_LIBRARIES} )
set_target_properties(kworkspace PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} )
install(TARGETS kworkspace ${INSTALL_TARGETS_DEFAULT_ARGS} )
install( FILES kdisplaymanager.h
kworkspace.h
kwindowlistmenu.h
DESTINATION ${INCLUDE_INSTALL_DIR}/kworkspace COMPONENT Devel )

21
kworkspace/Mainpage.dox Normal file
View File

@ -0,0 +1,21 @@
/** @mainpage Workspace library
libkworkspace provides functions to allow you to interact with the
%KDE session manager.
@authors
Matthias Kalle Dalheimer \<kalle@kde.org\><br>
Oswald Buddenhagen \<ossi@kde.org\><br>
Matthias Elter \<elter@kde.org\><br>
Matthias Ettrich \<ettrich@kde.org\>
@maintainers
[Unknown/None]
@licenses
@lgpl
*/
// DOXYGEN_SET_PROJECT_NAME = libkworkspace
// vim:ts=4:sw=4:expandtab:filetype=doxygen

2
kworkspace/Messages.sh Normal file
View File

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp -o $podir/libkworkspace.pot

View File

@ -0,0 +1,26 @@
/* This file is part of the KDE libraries
Copyright (C) 2008 Laurent Montel <montel@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef INTERFACEUTIL_H
#define INTERFACEUTIL_H
// needed by the DBUS interface
Q_DECLARE_METATYPE(QList<int>)
#endif

View File

@ -0,0 +1,458 @@
/*
Copyright (C) 2004 Oswald Buddenhagen <ossi@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kdisplaymanager.h"
#ifdef Q_WS_X11
#include <kapplication.h>
#include <klocale.h>
#include <QtDBus/QtDBus>
#include <QRegExp>
#include <X11/Xauth.h>
#include <X11/Xlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
static enum { Dunno, NoDM, NewKDM, OldKDM, GDM } DMType = Dunno;
static const char *ctl, *dpy;
class KDisplayManager::Private
{
public:
Private() : fd(-1) {}
~Private() {
if (fd >= 0)
close( fd );
}
int fd;
};
KDisplayManager::KDisplayManager() : d(new Private)
{
const char *ptr;
struct sockaddr_un sa;
if (DMType == Dunno) {
if (!(dpy = ::getenv( "DISPLAY" )))
DMType = NoDM;
else if ((ctl = ::getenv( "DM_CONTROL" )))
DMType = NewKDM;
else if ((ctl = ::getenv( "XDM_MANAGED" )) && ctl[0] == '/')
DMType = OldKDM;
else if (::getenv( "GDMSESSION" ))
DMType = GDM;
else
DMType = NoDM;
}
switch (DMType) {
default:
return;
case NewKDM:
case GDM:
if ((d->fd = ::socket( PF_UNIX, SOCK_STREAM, 0 )) < 0)
return;
sa.sun_family = AF_UNIX;
if (DMType == GDM) {
strcpy( sa.sun_path, "/var/run/gdm_socket" );
if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
strcpy( sa.sun_path, "/tmp/.gdm_socket" );
if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
::close( d->fd );
d->fd = -1;
break;
}
}
GDMAuthenticate();
} else {
if ((ptr = strchr( dpy, ':' )))
ptr = strchr( ptr, '.' );
snprintf( sa.sun_path, sizeof(sa.sun_path),
"%s/dmctl-%.*s/socket",
ctl, ptr ? int(ptr - dpy) : 512, dpy );
if (::connect( d->fd, (struct sockaddr *)&sa, sizeof(sa) )) {
::close( d->fd );
d->fd = -1;
}
}
break;
case OldKDM:
{
QString tf( ctl );
tf.truncate( tf.indexOf( ',' ) );
d->fd = ::open( tf.toLatin1(), O_WRONLY );
}
break;
}
}
KDisplayManager::~KDisplayManager()
{
delete d;
}
bool
KDisplayManager::exec( const char *cmd )
{
QByteArray buf;
return exec( cmd, buf );
}
/**
* Execute a KDM/GDM remote control command.
* @param cmd the command to execute. FIXME: undocumented yet.
* @param buf the result buffer.
* @return result:
* @li If true, the command was successfully executed.
* @p ret might contain addional results.
* @li If false and @p ret is empty, a communication error occurred
* (most probably KDM is not running).
* @li If false and @p ret is non-empty, it contains the error message
* from KDM.
*/
bool
KDisplayManager::exec( const char *cmd, QByteArray &buf )
{
bool ret = false;
int tl;
int len = 0;
if (d->fd < 0)
goto busted;
tl = strlen( cmd );
if (::write( d->fd, cmd, tl ) != tl) {
bust:
::close( d->fd );
d->fd = -1;
busted:
buf.resize( 0 );
return false;
}
if (DMType == OldKDM) {
buf.resize( 0 );
return true;
}
for (;;) {
if (buf.size() < 128)
buf.resize( 128 );
else if (buf.size() < len * 2)
buf.resize( len * 2 );
if ((tl = ::read( d->fd, buf.data() + len, buf.size() - len)) <= 0) {
if (tl < 0 && errno == EINTR)
continue;
goto bust;
}
len += tl;
if (buf[len - 1] == '\n') {
buf[len - 1] = 0;
if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') &&
(buf[1] == 'k' || buf[1] == 'K') && buf[2] <= ' ')
ret = true;
break;
}
}
return ret;
}
bool
KDisplayManager::canShutdown()
{
if (DMType == OldKDM)
return strstr( ctl, ",maysd" ) != 0;
QByteArray re;
if (DMType == GDM)
return exec( "QUERY_LOGOUT_ACTION\n", re ) && re.indexOf( "HALT" ) >= 0;
return exec( "caps\n", re ) && re.indexOf( "\tshutdown" ) >= 0;
}
void
KDisplayManager::shutdown( KWorkSpace::ShutdownType shutdownType,
KWorkSpace::ShutdownMode shutdownMode, /* NOT Default */
const QString &bootOption )
{
if (shutdownType == KWorkSpace::ShutdownTypeNone)
return;
bool cap_ask;
if (DMType == NewKDM) {
QByteArray re;
cap_ask = exec( "caps\n", re ) && re.indexOf( "\tshutdown ask" ) >= 0;
} else {
if (!bootOption.isEmpty())
return;
cap_ask = false;
}
if (!cap_ask && shutdownMode == KWorkSpace::ShutdownModeInteractive)
shutdownMode = KWorkSpace::ShutdownModeForceNow;
QByteArray cmd;
if (DMType == GDM) {
cmd.append( shutdownMode == KWorkSpace::ShutdownModeForceNow ?
"SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION " );
cmd.append( shutdownType == KWorkSpace::ShutdownTypeReboot ?
"REBOOT\n" : "HALT\n" );
} else {
cmd.append( "shutdown\t" );
cmd.append( shutdownType == KWorkSpace::ShutdownTypeReboot ?
"reboot\t" : "halt\t" );
if (!bootOption.isEmpty())
cmd.append( "=" ).append( bootOption.toLocal8Bit() ).append( "\t" );
cmd.append( shutdownMode == KWorkSpace::ShutdownModeInteractive ?
"ask\n" :
shutdownMode == KWorkSpace::ShutdownModeForceNow ?
"forcenow\n" :
shutdownMode == KWorkSpace::ShutdownModeTryNow ?
"trynow\n" : "schedule\n" );
}
exec( cmd.data() );
}
bool
KDisplayManager::bootOptions( QStringList &opts, int &defopt, int &current )
{
if (DMType != NewKDM)
return false;
QByteArray re;
if (!exec( "listbootoptions\n", re ))
return false;
opts = QString::fromLocal8Bit( re.data() ).split( '\t', QString::SkipEmptyParts );
if (opts.size() < 4)
return false;
bool ok;
defopt = opts[2].toInt( &ok );
if (!ok)
return false;
current = opts[3].toInt( &ok );
if (!ok)
return false;
opts = opts[1].split( ' ', QString::SkipEmptyParts );
for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it)
(*it).replace( "\\s", " " );
return true;
}
void
KDisplayManager::setLock( bool on )
{
if (DMType != GDM)
exec( on ? "lock\n" : "unlock\n" );
}
bool
KDisplayManager::isSwitchable()
{
if (DMType == OldKDM)
return dpy[0] == ':';
if (DMType == GDM)
return exec( "QUERY_VT\n" );
QByteArray re;
return exec( "caps\n", re ) && re.indexOf( "\tlocal" ) >= 0;
}
int
KDisplayManager::numReserve()
{
if (DMType == GDM)
return 1; /* Bleh */
if (DMType == OldKDM)
return strstr( ctl, ",rsvd" ) ? 1 : -1;
QByteArray re;
int p;
if (!(exec( "caps\n", re ) && (p = re.indexOf( "\treserve " )) >= 0))
return -1;
return atoi( re.data() + p + 9 );
}
void
KDisplayManager::startReserve()
{
if (DMType == GDM)
exec("FLEXI_XSERVER\n");
else
exec("reserve\n");
}
bool
KDisplayManager::localSessions( SessList &list )
{
if (DMType == OldKDM)
return false;
QByteArray re;
if (DMType == GDM) {
if (!exec( "CONSOLE_SERVERS\n", re ))
return false;
QStringList sess = QString(re.data() +3).split( QChar(';'), QString::SkipEmptyParts);
for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
QStringList ts = (*it).split( QChar(',') );
SessEnt se;
se.display = ts[0];
se.user = ts[1];
se.vt = ts[2].toInt();
se.session = "<unknown>";
se.self = ts[0] == ::getenv( "DISPLAY" ); /* Bleh */
se.tty = false;
list.append( se );
}
} else {
if (!exec( "list\talllocal\n", re ))
return false;
QStringList sess = QString(re.data() + 3).split(QChar('\t'), QString::SkipEmptyParts );
for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) {
QStringList ts = (*it).split( QChar(',') );
SessEnt se;
se.display = ts[0];
if (ts[1][0] == '@')
se.from = ts[1].mid( 1 ), se.vt = 0;
else
se.vt = ts[1].mid( 2 ).toInt();
se.user = ts[2];
se.session = ts[3];
se.self = (ts[4].indexOf( '*' ) >= 0);
se.tty = (ts[4].indexOf( 't' ) >= 0);
list.append( se );
}
}
return true;
}
void
KDisplayManager::sess2Str2( const SessEnt &se, QString &user, QString &loc )
{
if (se.tty) {
user = i18nc("user: ...", "%1: TTY login", se.user );
loc = se.vt ? QString("vt%1").arg( se.vt ) : se.display ;
} else {
user =
se.user.isEmpty() ?
se.session.isEmpty() ?
i18n("Unused") :
se.session == "<remote>" ?
i18n("X login on remote host") :
i18nc("... host", "X login on %1", se.session ) :
se.session == "<unknown>" ?
se.user :
i18nc("user: session type", "%1: %2",
se.user, se.session );
loc =
se.vt ?
QString("%1, vt%2").arg( se.display ).arg( se.vt ) :
se.display;
}
}
QString
KDisplayManager::sess2Str( const SessEnt &se )
{
QString user, loc;
sess2Str2( se, user, loc );
return i18nc("session (location)", "%1 (%2)", user, loc );
}
bool
KDisplayManager::switchVT( int vt )
{
if (DMType == GDM)
return exec( QString("SET_VT %1\n").arg(vt).toLatin1() );
return exec( QString("activate\tvt%1\n").arg(vt).toLatin1() );
}
void
KDisplayManager::lockSwitchVT( int vt )
{
if (switchVT( vt ))
{
QDBusInterface screensaver("org.freedesktop.ScreenSaver", "/ScreenSaver", "org.freedesktop.ScreenSaver");
screensaver.call( "Lock" );
}
}
void
KDisplayManager::GDMAuthenticate()
{
FILE *fp;
const char *dpy, *dnum, *dne;
int dnl;
Xauth *xau;
dpy = DisplayString( QX11Info::display() );
if (!dpy) {
dpy = ::getenv( "DISPLAY" );
if (!dpy)
return;
}
dnum = strchr( dpy, ':' ) + 1;
dne = strchr( dpy, '.' );
dnl = dne ? dne - dnum : strlen( dnum );
/* XXX should do locking */
if (!(fp = fopen( XauFileName(), "r" )))
return;
while ((xau = XauReadAuth( fp ))) {
if (xau->family == FamilyLocal &&
xau->number_length == dnl && !memcmp( xau->number, dnum, dnl ) &&
xau->data_length == 16 &&
xau->name_length == 18 && !memcmp( xau->name, "MIT-MAGIC-COOKIE-1", 18 ))
{
QString cmd( "AUTH_LOCAL " );
for (int i = 0; i < 16; i++)
cmd += QString::number( (uchar)xau->data[i], 16 ).rightJustified( 2, '0');
cmd += '\n';
if (exec( cmd.toLatin1() )) {
XauDisposeAuth( xau );
break;
}
}
XauDisposeAuth( xau );
}
fclose (fp);
}
#endif // Q_WS_X11

View File

@ -0,0 +1,102 @@
/*
Copyright (C) 2004,2005 Oswald Buddenhagen <ossi@kde.org>
Copyright (C) 2005 Stephan Kulow <coolo@kde.org>
This program is free software; you can redistribute it and/or
modify it under the terms of the Lesser GNU General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the Lesser GNU General Public License
along with this program; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KDISPLAYMANAGER_H
#define KDISPLAYMANAGER_H
#include "kworkspace.h"
#include <kdemacros.h>
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QByteArray>
struct KDE_EXPORT SessEnt {
QString display, from, user, session;
int vt;
bool self:1, tty:1;
};
typedef QList<SessEnt> SessList;
class KDE_EXPORT KDisplayManager {
#ifdef Q_WS_X11
public:
KDisplayManager();
~KDisplayManager();
bool canShutdown();
void shutdown( KWorkSpace::ShutdownType shutdownType,
KWorkSpace::ShutdownMode shutdownMode,
const QString &bootOption = QString() );
void setLock( bool on );
bool isSwitchable();
int numReserve();
void startReserve();
bool localSessions( SessList &list );
bool switchVT( int vt );
void lockSwitchVT( int vt );
bool bootOptions( QStringList &opts, int &dflt, int &curr );
static QString sess2Str( const SessEnt &se );
static void sess2Str2( const SessEnt &se, QString &user, QString &loc );
private:
bool exec( const char *cmd, QByteArray &ret );
bool exec( const char *cmd );
void GDMAuthenticate();
#else // Q_WS_X11
public:
KDisplayManager() {}
bool canShutdown() { return false; }
void shutdown( KWorkSpace::ShutdownType shutdownType,
KWorkSpace::ShutdownMode shutdownMode,
const QString &bootOption = QString() ) {}
void setLock( bool ) {}
bool isSwitchable() { return false; }
int numReserve() { return -1; }
void startReserve() {}
bool localSessions( SessList &list ) { return false; }
void switchVT( int vt ) {}
bool bootOptions( QStringList &opts, int &dflt, int &curr );
#endif // Q_WS_X11
private:
#ifdef Q_WS_X11
class Private;
Private * const d;
#endif // Q_WS_X11
}; // class KDisplayManager
#endif // KDISPLAYMANAGER_H

View File

@ -0,0 +1,228 @@
/*****************************************************************
Copyright (c) 2000 Matthias Elter <elter@kde.org>
Matthias Ettrich <ettrich@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************/
#include "kwindowlistmenu.h"
#include <QtCore/QBool>
#include <QtGui/QApplication>
#include <QtGui/QDesktopWidget>
#include <QtGui/QPainter>
#include <QtGui/QX11Info>
#include <QtDBus/QtDBus>
#include <klocale.h>
#include <kstringhandler.h>
#include <kstyle.h>
#include <kwindowsystem.h>
#include <netwm.h>
#undef Bool
#include "kwindowlistmenu.moc"
#include "kwin_interface.h"
static bool compareKWinWindowInfo( KWindowInfo* firstInfo, KWindowInfo* secondInfo )
{
QString firstTitle, secondTitle;
if ( firstInfo )
firstTitle = firstInfo->visibleNameWithState().toLower();
if ( secondInfo )
secondTitle = secondInfo->visibleNameWithState().toLower();
return firstTitle.compare( secondTitle ) >= 0;
}
class KWindowListMenu::Private
{
};
KWindowListMenu::KWindowListMenu( QWidget *parent )
: KMenu( parent ), d( new Private )
{
}
KWindowListMenu::~KWindowListMenu()
{
delete d;
}
static bool standaloneDialog( const KWindowInfo* info, const QList<KWindowInfo*>& list )
{
WId group = info->groupLeader();
if ( group == 0 )
return info->transientFor() == QX11Info::appRootWindow();
foreach ( KWindowInfo* info, list )
if ( info->groupLeader() == group )
return false;
return true;
}
void KWindowListMenu::init()
{
int numberOfDesktops = KWindowSystem::numberOfDesktops();
int currentDesktop = KWindowSystem::currentDesktop();
WId activeWindow = KWindowSystem::activeWindow();
// Make sure the popup is not too wide, otherwise clicking in the middle of kdesktop
// wouldn't leave any place for the popup, and release would activate some menu entry.
int maxwidth = qApp->desktop()->screenGeometry( this ).width() / 2 - 100;
clear();
QAction* unclutter = addAction( i18n("Unclutter Windows"),
this, SLOT( slotUnclutterWindows() ) );
QAction* cascade = addAction( i18n("Cascade Windows"),
this, SLOT( slotCascadeWindows() ) );
// if we only have one desktop we won't be showing titles, so put a separator in
if ( numberOfDesktops == 1 )
addSeparator();
QList<KWindowInfo> windows;
foreach ( WId id, KWindowSystem::windows() )
windows.append( KWindowSystem::windowInfo( id, NET::WMDesktop ) );
bool showAllDesktopsGroup = ( numberOfDesktops > 1 );
int i = 0;
for ( int j = 1; j <= numberOfDesktops + (showAllDesktopsGroup ? 1 : 0); j++ ) {
bool onAllDesktops = ( j > numberOfDesktops );
int items = 0;
// KDE4 porting - huh? didn't know you could set an item checked before it's created?
//if (!activeWindow && d == cd)
//setItemChecked(1000 + d, true);
QList<KWindowInfo*> list;
foreach (const KWindowInfo &wi, windows) {
if ( (wi.desktop() == j) || (onAllDesktops && wi.onAllDesktops())
|| (!showAllDesktopsGroup && wi.onAllDesktops()) ) {
list.append( new KWindowInfo( wi.win(),
NET::WMVisibleName | NET::WMState | NET::XAWMState | NET::WMWindowType,
NET::WM2GroupLeader | NET::WM2TransientFor ) );
}
}
qStableSort( list.begin(), list.end(), compareKWinWindowInfo );
foreach ( KWindowInfo* info, list ) {
++i;
QString itemText = fontMetrics().elidedText(info->visibleNameWithState(), Qt::ElideMiddle, maxwidth);
NET::WindowType windowType = info->windowType( NET::NormalMask | NET::DesktopMask
| NET::DockMask | NET::ToolbarMask | NET::MenuMask | NET::DialogMask
| NET::OverrideMask | NET::TopMenuMask | NET::UtilityMask | NET::SplashMask );
if ( (windowType == NET::Normal || windowType == NET::Unknown
|| (windowType == NET::Dialog && standaloneDialog( info, list )))
&& !(info->state() & NET::SkipTaskbar) ) {
QPixmap pm = KWindowSystem::icon( info->win(), 16, 16, true );
items++;
// ok, we have items on this desktop, let's show the title
if ( items == 1 && numberOfDesktops > 1 ) {
if( !onAllDesktops )
addTitle( KWindowSystem::desktopName( j ) );
else
addTitle( i18n( "On All Desktops" ) );
}
// Avoid creating unwanted accelerators.
itemText.replace( '&', QLatin1String( "&&" ));
QAction* action = addAction( pm, itemText, this, SLOT( slotForceActiveWindow() ) );
action->setData( (int)info->win() );
if ( info->win() == activeWindow )
action->setChecked( true );
}
}
if ( j == currentDesktop ) {
unclutter->setEnabled( items > 0 );
cascade->setEnabled( items > 0 );
}
qDeleteAll( list );
}
// no windows?
if ( i == 0 ) {
if ( numberOfDesktops > 1 )
addSeparator(); // because we don't have any titles, nor a separator
addAction( i18n( "No Windows" ) )->setEnabled( false );
}
}
void KWindowListMenu::slotForceActiveWindow()
{
QAction* window = qobject_cast<QAction*>(sender());
if (!window || !window->data().canConvert(QVariant::Int))
return;
KWindowSystem::forceActiveWindow(window->data().toInt());
}
void KWindowListMenu::slotSetCurrentDesktop()
{
QAction* window = qobject_cast<QAction*>(sender());
if (!window || !window->data().canConvert(QVariant::Int))
return;
KWindowSystem::setCurrentDesktop(window->data().toInt());
}
// This popup is much more useful from keyboard if it has the active
// window active by default - however, QPopupMenu tries hard to resist.
// QPopupMenu::popup() resets the active item, so this needs to be
// called after popup().
void KWindowListMenu::selectActiveWindow()
{
foreach (QAction* action, actions())
if (action->isChecked()) {
setActiveAction(action);
break;
}
}
void KWindowListMenu::slotUnclutterWindows()
{
org::kde::KWin kwin("org.kde.kwin", "/KWin", QDBusConnection::sessionBus());
kwin.unclutterDesktop();
}
void KWindowListMenu::slotCascadeWindows()
{
org::kde::KWin kwin("org.kde.kwin", "/KWin", QDBusConnection::sessionBus());
kwin.cascadeDesktop();
}

View File

@ -0,0 +1,79 @@
/*****************************************************************
Copyright (c) 2000 Matthias Elter <elter@kde.org>
Matthias Ettrich <ettrich@kde.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************/
#ifndef KWINDOWLISTMENU_H
#define KWINDOWLISTMENU_H
#include <kdemacros.h>
#include <kmenu.h>
/**
* This class provides a menu which contains actions
* to manage a window. It is used by the window manager
* for example an accessable there via the menu button of
* the window decoration.
*/
class KDE_EXPORT KWindowListMenu : public KMenu
{
Q_OBJECT
public:
/**
* Creates a new window list menu.
*
* @param parent The parent widget.
*/
explicit KWindowListMenu( QWidget *parent = 0 );
/**
* Destroys the window list menu.
*/
virtual ~KWindowListMenu();
/**
* Initializes the menu by filling it with actions
* for managing a window.
*/
void init();
public Q_SLOTS:
/**
* Pre-selects the active window in the popup menu, for faster
* keyboard navigation. Needs to be called after popup().
* Should not be used when the popup is invoked using the mouse.
*/
void selectActiveWindow();
protected Q_SLOTS:
void slotForceActiveWindow();
void slotSetCurrentDesktop();
void slotUnclutterWindows();
void slotCascadeWindows();
private:
class Private;
Private* const d;
};
#endif

236
kworkspace/kworkspace.cpp Normal file
View File

@ -0,0 +1,236 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kworkspace.h"
#include <QApplication>
#include <QDataStream>
#include <kapplication.h>
#include <QFile>
#include <QFileInfo>
#include <QTextStream>
#include <klocale.h>
#include <QDateTime>
#include <kstandarddirs.h>
#include <QtDBus/QtDBus>
#include <stdlib.h> // getenv()
#include <ksmserver_interface.h>
#include <kdefakes.h>
#include <QSocketNotifier>
#ifdef Q_WS_X11
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/SM/SMlib.h>
#include <fixx11h.h>
#endif
#ifdef Q_WS_X11
#define DISPLAY "DISPLAY"
#elif defined(Q_WS_QWS)
#define DISPLAY "QWS_DISPLAY"
#endif
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include "kworkspace_p.h"
namespace KWorkSpace
{
static void save_yourself_callback( SmcConn conn_P, SmPointer, int, Bool , int, Bool )
{
SmcSaveYourselfDone( conn_P, True );
}
static void dummy_callback( SmcConn, SmPointer )
{
}
KRequestShutdownHelper::KRequestShutdownHelper()
{
SmcCallbacks calls;
calls.save_yourself.callback = save_yourself_callback;
calls.die.callback = dummy_callback;
calls.save_complete.callback = dummy_callback;
calls.shutdown_cancelled.callback = dummy_callback;
char* id = NULL;
char err[ 11 ];
conn = SmcOpenConnection( NULL, NULL, 1, 0,
SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
| SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err );
if( id != NULL )
free( id );
if( conn == NULL )
return; // no SM
// set the required properties, mostly dummy values
SmPropValue propvalue[ 5 ];
SmProp props[ 5 ];
propvalue[ 0 ].length = sizeof( int );
int value0 = SmRestartNever; // so that this extra SM connection doesn't interfere
propvalue[ 0 ].value = &value0;
props[ 0 ].name = const_cast< char* >( SmRestartStyleHint );
props[ 0 ].type = const_cast< char* >( SmCARD8 );
props[ 0 ].num_vals = 1;
props[ 0 ].vals = &propvalue[ 0 ];
struct passwd* entry = getpwuid( geteuid() );
propvalue[ 1 ].length = entry != NULL ? strlen( entry->pw_name ) : 0;
propvalue[ 1 ].value = (SmPointer)( entry != NULL ? entry->pw_name : "" );
props[ 1 ].name = const_cast< char* >( SmUserID );
props[ 1 ].type = const_cast< char* >( SmARRAY8 );
props[ 1 ].num_vals = 1;
props[ 1 ].vals = &propvalue[ 1 ];
propvalue[ 2 ].length = 0;
propvalue[ 2 ].value = (SmPointer)( "" );
props[ 2 ].name = const_cast< char* >( SmRestartCommand );
props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 );
props[ 2 ].num_vals = 1;
props[ 2 ].vals = &propvalue[ 2 ];
propvalue[ 3 ].length = strlen( "requestshutdownhelper" );
propvalue[ 3 ].value = (SmPointer)"requestshutdownhelper";
props[ 3 ].name = const_cast< char* >( SmProgram );
props[ 3 ].type = const_cast< char* >( SmARRAY8 );
props[ 3 ].num_vals = 1;
props[ 3 ].vals = &propvalue[ 3 ];
propvalue[ 4 ].length = 0;
propvalue[ 4 ].value = (SmPointer)( "" );
props[ 4 ].name = const_cast< char* >( SmCloneCommand );
props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 );
props[ 4 ].num_vals = 1;
props[ 4 ].vals = &propvalue[ 4 ];
SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] };
SmcSetProperties( conn, 5, p );
notifier = new QSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )),
QSocketNotifier::Read, this );
connect( notifier, SIGNAL( activated( int )), SLOT( processData()));
}
KRequestShutdownHelper::~KRequestShutdownHelper()
{
if( conn != NULL )
{
delete notifier;
SmcCloseConnection( conn, 0, NULL );
}
}
void KRequestShutdownHelper::processData()
{
if( conn != NULL )
IceProcessMessages( SmcGetIceConnection( conn ), 0, 0 );
}
bool KRequestShutdownHelper::requestShutdown( ShutdownConfirm confirm )
{
if( conn == NULL )
return false;
SmcRequestSaveYourself( conn, SmSaveBoth, True, SmInteractStyleAny,
confirm == ShutdownConfirmNo, True );
// flush the request
IceFlush(SmcGetIceConnection(conn));
return true;
}
static KRequestShutdownHelper* helper = NULL;
static void cleanup_sm()
{
delete helper;
}
bool requestShutDown(
ShutdownConfirm confirm, ShutdownType sdtype, ShutdownMode sdmode )
{
QApplication::syncX();
kapp->updateRemoteUserTimestamp( "org.kde.ksmserver" );
/* use ksmserver's dcop interface if necessary */
if ( confirm == ShutdownConfirmYes ||
sdtype != ShutdownTypeDefault ||
sdmode != ShutdownModeDefault )
{
org::kde::KSMServerInterface ksmserver("org.kde.ksmserver", "/KSMServer", QDBusConnection::sessionBus());
QDBusReply<void> reply = ksmserver.logout((int)confirm, (int)sdtype, (int)sdmode);
return (reply.isValid());
}
if( helper == NULL )
{
helper = new KRequestShutdownHelper();
qAddPostRoutine(cleanup_sm);
}
return helper->requestShutdown( confirm );
}
bool canShutDown( ShutdownConfirm confirm,
ShutdownType sdtype,
ShutdownMode sdmode )
{
if ( confirm == ShutdownConfirmYes ||
sdtype != ShutdownTypeDefault ||
sdmode != ShutdownModeDefault )
{
org::kde::KSMServerInterface ksmserver("org.kde.ksmserver", "/KSMServer", QDBusConnection::sessionBus());
QDBusReply<bool> reply = ksmserver.canShutdown();
if (!reply.isValid()) {
return false;
}
return reply;
}
return true;
}
static QTime smModificationTime;
void propagateSessionManager()
{
QByteArray fName = QFile::encodeName(KStandardDirs::locateLocal("socket", "KSMserver"));
QString display = QString::fromLocal8Bit( ::getenv(DISPLAY) );
// strip the screen number from the display
display.remove(QRegExp("\\.[0-9]+$"));
int i;
while( (i = display.indexOf(':')) >= 0)
display[i] = '_';
fName += '_';
fName += display.toLocal8Bit();
QByteArray smEnv = ::getenv("SESSION_MANAGER");
bool check = smEnv.isEmpty();
if ( !check && smModificationTime.isValid() ) {
QFileInfo info( fName );
QTime current = info.lastModified().time();
check = current > smModificationTime;
}
if ( check ) {
QFile f( fName );
if ( !f.open( QIODevice::ReadOnly ) )
return;
QFileInfo info ( f );
smModificationTime = QTime( info.lastModified().time() );
QTextStream t(&f);
t.setCodec( "ISO 8859-1" );
QString s = t.readLine();
f.close();
::setenv( "SESSION_MANAGER", s.toLatin1(), true );
}
}
} // end namespace
#include "kworkspace_p.moc"

150
kworkspace/kworkspace.h Normal file
View File

@ -0,0 +1,150 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KWORKSPACE_H
#define KWORKSPACE_H
#include <kdemacros.h>
namespace KWorkSpace
{
/**
* The possible values for the @p confirm parameter of requestShutDown().
*/
enum ShutdownConfirm {
/**
* Obey the user's confirmation setting.
*/
ShutdownConfirmDefault = -1,
/**
* Don't confirm, shutdown without asking.
*/
ShutdownConfirmNo = 0,
/**
* Always confirm, ask even if the user turned it off.
*/
ShutdownConfirmYes = 1
};
/**
* The possible values for the @p sdtype parameter of requestShutDown().
*/
enum ShutdownType {
/**
* Select previous action or the default if it's the first time.
*/
ShutdownTypeDefault = -1,
/**
* Only show log out dialog
*/
ShutdownTypeNone = 0,
/**
* Log out and reboot the machine.
*/
ShutdownTypeReboot = 1,
/**
* Log out and halt the machine.
*/
ShutdownTypeHalt = 2,
/**
* Only log out
*/
ShutdownTypeLogout = 3
};
/**
* The possible values for the @p sdmode parameter of requestShutDown().
*/
enum ShutdownMode {
/**
* Select previous mode or the default if it's the first time.
*/
ShutdownModeDefault = -1,
/**
* Schedule a shutdown (halt or reboot) for the time all active sessions
* have exited.
*/
ShutdownModeSchedule = 0,
/**
* Shut down, if no sessions are active. Otherwise do nothing.
*/
ShutdownModeTryNow = 1,
/**
* Force shutdown. Kill any possibly active sessions.
*/
ShutdownModeForceNow = 2,
/**
* Pop up a dialog asking the user what to do if sessions are still active.
*/
ShutdownModeInteractive = 3
};
/**
* Asks the session manager to shut the session down.
*
* Using @p confirm == ShutdownConfirmYes or @p sdtype != ShutdownTypeDefault or
* @p sdmode != ShutdownModeDefault causes the use of ksmserver's DCOP
* interface. The remaining two combinations use the standard XSMP and
* will work with any session manager compliant with it.
*
* @param confirm Whether to ask the user if he really wants to log out.
* ShutdownConfirm
* @param sdtype The action to take after logging out. ShutdownType
* @param sdmode If/When the action should be taken. ShutdownMode
* @return true on success, false if the session manager could not be
* contacted.
*/
KDE_EXPORT bool requestShutDown( ShutdownConfirm confirm = ShutdownConfirmDefault,
ShutdownType sdtype = ShutdownTypeDefault,
ShutdownMode sdmode = ShutdownModeDefault );
/**
* Used to check whether a requestShutDown call with the same arguments
* has any chance of succeeding.
*
* For example, if KDE's own session manager cannot be contacted, we can't
* demand that the computer be shutdown, or force a confirmation dialog.
*
* Even if we can access the KDE session manager, the system or user
* configuration may prevent the user from requesting a shutdown or
* reboot.
*/
KDE_EXPORT bool canShutDown( ShutdownConfirm confirm = ShutdownConfirmDefault,
ShutdownType sdtype = ShutdownTypeDefault,
ShutdownMode sdmode = ShutdownModeDefault );
/**
* Propagates the network address of the session manager in the
* SESSION_MANAGER environment variable so that child processes can
* pick it up.
*
* If SESSION_MANAGER isn't defined yet, the address is searched in
* $HOME/.KSMserver.
*
* This function is called by clients that are started outside the
* session ( i.e. before ksmserver is started), but want to launch
* other processes that should participate in the session. Examples
* are kdesktop or kicker.
*/
KDE_EXPORT void propagateSessionManager();
}
#endif

49
kworkspace/kworkspace_p.h Normal file
View File

@ -0,0 +1,49 @@
/* This file is part of the KDE libraries
Copyright (C) 2007 Lubos Lunak <l.lunak@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KWORKSPACE_P_H
#define KWORKSPACE_P_H
#include "kworkspace.h"
class QSocketNotifier;
namespace KWorkSpace
{
// A class that creates another connection to ksmserver and handles it properly.
class KRequestShutdownHelper
: public QObject
{
Q_OBJECT
public:
KRequestShutdownHelper();
virtual ~KRequestShutdownHelper();
bool requestShutdown( ShutdownConfirm confirm );
private slots:
void processData();
private:
SmcConn connection() const { return conn; }
QSocketNotifier* notifier;
SmcConn conn;
};
}
#endif

View File

@ -0,0 +1,23 @@
project(libplasmaclock)
set(plasmaclock_LIB_SRCS
clocknumber.cpp
clockapplet.cpp
calendar.cpp
calendartable.cpp
toolbutton.cpp
)
kde4_add_ui_files(plasmaclock_LIB_SRCS calendar.ui timezonesConfig.ui)
kde4_add_library(plasmaclock SHARED ${plasmaclock_LIB_SRCS})
target_link_libraries(plasmaclock plasma ${KDE4_KDEUI_LIBS})
set_target_properties(plasmaclock PROPERTIES VERSION ${GENERIC_LIB_VERSION}
SOVERSION ${GENERIC_LIB_SOVERSION} )
#clockapplet.h
install(FILES clocknumber.h clockapplet.h calendar.h calendartable.h plasmaclock_export.h
${CMAKE_CURRENT_BINARY_DIR}/ui_calendar.h DESTINATION ${INCLUDE_INSTALL_DIR}/libplasmaclock COMPONENT Devel)
install(TARGETS plasmaclock ${INSTALL_TARGETS_DEFAULT_ARGS} )

2
libplasmaclock/Messages.sh Executable file
View File

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp -o $podir/libplasmaclock.pot

206
libplasmaclock/calendar.cpp Normal file
View File

@ -0,0 +1,206 @@
/*
* Copyright 2008 Davide Bettio <davide.bettio@kdemail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "calendar.h"
//Qt
#include <QtCore/QDate>
#include <QtGui/QPainter>
#include <QtGui/QWidget>
#include <QtGui/QGraphicsSceneWheelEvent>
#include <QtGui/QGraphicsLinearLayout>
#include <QtGui/QGraphicsProxyWidget>
//KDECore
#include <kglobal.h>
#include <kdebug.h>
//Plasma
#include <plasma/svg.h>
#include <plasma/theme.h>
#include <plasma/widgets/label.h>
#include "toolbutton.h"
namespace Plasma
{
class CalendarPrivate
{
public:
ToolButton *back;
Plasma::Label *spacer0;
Plasma::Label *month;
#ifdef COOL_SPINBOX
SpinBox *year;
#else
Plasma::Label *year;
#endif
Plasma::Label *spacer1;
ToolButton *forward;
Plasma::CalendarTable *calendarTable;
};
//TODO
Calendar::Calendar(const QDate &, QGraphicsWidget *parent)
: QGraphicsWidget(parent), d(new CalendarPrivate())
{
}
Calendar::Calendar(QGraphicsWidget *parent)
: QGraphicsWidget(parent), d(new CalendarPrivate())
{
QGraphicsLinearLayout *m_layout = new QGraphicsLinearLayout(Qt::Vertical, this);
QGraphicsLinearLayout *m_hLayout = new QGraphicsLinearLayout(m_layout);
d->calendarTable = new Plasma::CalendarTable(this);
d->calendarTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
connect(d->calendarTable, SIGNAL(displayedMonthChanged(int, int)), this, SLOT(displayedMonthChanged(int, int)));
QGraphicsProxyWidget *backProxy = new QGraphicsProxyWidget(this);
d->back = new ToolButton();
d->back->setText("<");
d->back->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(d->back, SIGNAL(clicked()), this, SLOT(prevMonth()));
backProxy->setWidget(d->back);
m_hLayout->addItem(backProxy);
d->spacer0 = new Plasma::Label(this);
d->spacer0->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
m_hLayout->addItem(d->spacer0);
d->month = new Plasma::Label(this);
d->month->setText(d->calendarTable->calendar()->monthName(d->calendarTable->calendar()->month(d->calendarTable->date()), d->calendarTable->calendar()->year(d->calendarTable->date())));
d->month->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_hLayout->addItem(d->month);
#ifdef COOL_SPINBOX
QGraphicsProxyWidget *yearProxy = new QGraphicsProxyWidget(this);
d->year = new SpinBox();
d->year->setRange(d->calendarTable->calendar()->year(d->calendarTable->calendar()->earliestValidDate()), d->calendarTable->calendar()->year(d->calendarTable->calendar()->latestValidDate()));
d->year->setValue(d->calendarTable->calendar()->year(d->calendarTable->date()));
yearProxy->setWidget(d->year);
m_hLayout->addItem(yearProxy);
#else
d->year = new Plasma::Label;
d->year->setText(QString::number(d->calendarTable->calendar()->year(d->calendarTable->date())));
d->year->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
m_hLayout->addItem(d->year);
#endif
d->spacer1 = new Plasma::Label(this);
d->spacer1->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
m_hLayout->addItem(d->spacer1);
QGraphicsProxyWidget *forwardProxy = new QGraphicsProxyWidget(this);
d->forward = new ToolButton();
d->forward->setText(">");
d->forward->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
connect(d->forward, SIGNAL(clicked()), this, SLOT(nextMonth()));
forwardProxy->setWidget(d->forward);
m_hLayout->addItem(forwardProxy);
m_layout->addItem(m_hLayout);
m_layout->addItem(d->calendarTable);
}
Calendar::~Calendar()
{
delete d;
}
bool Calendar::setCalendar(KCalendarSystem *calendar)
{
return d->calendarTable->setCalendar(calendar);
}
bool Calendar::setDate(const QDate &date)
{
return d->calendarTable->setDate(date);
}
const QDate& Calendar::date() const
{
return d->calendarTable->date();
}
void Calendar::displayedMonthChanged(int calendarSystemYear, int calendarSystemMonth)
{
d->month->setText(d->calendarTable->calendar()->monthName(calendarSystemMonth, calendarSystemYear));
#ifdef COOL_SPINBOX
d->year->setValue(calendarSystemYear);
#else
d->year->setText(QString::number(calendarSystemYear));
#endif
}
void Calendar::prevMonth()
{
QDate tmpDate = d->calendarTable->date();
QDate newDate;
int month = d->calendarTable->calendar()->month(tmpDate);
int year = d->calendarTable->calendar()->year(tmpDate);
if (month == 1){
month = 12;
year--;
}else{
month--;
}
if (d->calendarTable->calendar()->setYMD(newDate, year, month, d->calendarTable->calendar()->day(tmpDate))){
d->calendarTable->setDate(newDate);
}else if (d->calendarTable->calendar()->setYMD(newDate, year, month, 1)){
d->calendarTable->setDate(newDate);
}
}
void Calendar::nextMonth()
{
QDate tmpDate = d->calendarTable->date();
QDate newDate;
int month = d->calendarTable->calendar()->month(tmpDate);
int year = d->calendarTable->calendar()->year(tmpDate);
if (month == 12){
month = 1;
year++;
}else{
month++;
}
if (d->calendarTable->calendar()->setYMD(newDate, year, month, d->calendarTable->calendar()->day(tmpDate))){
d->calendarTable->setDate(newDate);
}else if (d->calendarTable->calendar()->setYMD(newDate, year, month, 1)){
d->calendarTable->setDate(newDate);
}
}
CalendarTable *Calendar::calendarTable() const
{
return d->calendarTable;
}
}
#include "calendar.moc"

71
libplasmaclock/calendar.h Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright 2008 Davide Bettio <davide.bettio@kdemail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_CALENDAR_H
#define PLASMA_CALENDAR_H
#include <QtGui/QGraphicsWidget>
#include "plasmaclock_export.h"
#include <kcalendarsystem.h>
#include "calendartable.h"
namespace Plasma
{
class CalendarTable;
class CalendarPrivate;
class PLASMACLOCK_EXPORT Calendar : public QGraphicsWidget
{
Q_OBJECT
public:
explicit Calendar(QGraphicsWidget *parent = 0);
Calendar(const QDate &, QGraphicsWidget *parent = 0);
~Calendar();
const KCalendarSystem *calendar () const;
bool setDate(const QDate &date);
const QDate& date() const;
bool setCalendar(KCalendarSystem *calendar = 0);
CalendarTable *calendarTable() const;
Q_SIGNALS:
void dateChanged(const QDate &cur, const QDate &old);
void dateChanged(const QDate &date);
void tableClicked();
private Q_SLOTS:
void displayedMonthChanged(int calendarSystemYear, int calendarSystemMonth);
void prevMonth();
void nextMonth();
private:
CalendarPrivate* const d;
};
}
#endif

View File

@ -0,0 +1,42 @@
<ui version="4.0" >
<class>calendar</class>
<widget class="QWidget" name="calendar" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>276</width>
<height>255</height>
</rect>
</property>
<property name="styleSheet" >
<string notr="true" />
</property>
<layout class="QVBoxLayout" >
<property name="spacing" >
<number>0</number>
</property>
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="KDatePicker" name="kdatepicker" >
<property name="autoFillBackground" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KDatePicker</class>
<extends>QFrame</extends>
<header>kdatepicker.h</header>
</customwidget>
</customwidgets>
<resources>
<include location="resource.qrc" />
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,533 @@
/*
* Copyright 2008 Davide Bettio <davide.bettio@kdemail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "calendartable.h"
//Qt
#include <QtCore/QDate>
#include <QtGui/QPainter>
#include <QtGui/QWidget>
#include <QtGui/QGraphicsSceneWheelEvent>
#include <QtGui/QStyleOptionGraphicsItem>
//KDECore
#include <kglobal.h>
#include <kdebug.h>
//Plasma
#include <plasma/svg.h>
#include <plasma/theme.h>
namespace Plasma
{
class CalendarCellBorder
{
public:
CalendarCellBorder(int c, int w, int d, CalendarTable::CellTypes t, QDate dt)
: cell(c),
week(w),
weekDay(d),
type(t),
date(dt)
{
}
int cell;
int week;
int weekDay;
CalendarTable::CellTypes type;
QDate date;
};
class CalendarTablePrivate
{
public:
CalendarTablePrivate(CalendarTable *q, const QDate &cDate = QDate::currentDate())
{
svg = new Svg();
svg->setImagePath("widgets/calendar");
svg->setContainsMultipleImages(true);
calendar = KGlobal::locale()->calendar();
date = cDate;
QDate currentDate = QDate::currentDate();
month = calendar->month(currentDate);
year = calendar->year(currentDate);
setupThemedElements();
QObject::connect(Plasma::Theme::defaultTheme(), SIGNAL(themeChanged()),
q, SLOT(setupThemedElements()));
}
~CalendarTablePrivate()
{
delete svg;
}
void setupThemedElements()
{
QColor c = Plasma::Theme::defaultTheme()->color(Plasma::Theme::HighlightColor);
c.setAlpha(180);
highlightBrush = QBrush(c);
}
int firstMonthDayIndex(int y, int m)
{
QDate myDate;
calendar->setYMD(myDate, y, m, 1);
return (((calendar->dayOfWeek(myDate) - 1) + (calendar->daysInWeek(date) - (calendar->weekStartDay() - 1))) % calendar->daysInWeek(date)) + 1;
}
QRectF hoveredCellRect(CalendarTable *q, const QPointF &hoverPoint)
{
hoverDay = -1;
hoverWeek = -1;
if (hoverPoint.isNull()) {
return QRectF();
}
if (hoverPoint.x() < centeringSpace + cellW + weekBarSpace) {
// skip the weekbar
return QRectF();
}
int x = (hoverPoint.x() - centeringSpace) / (cellW + cellSpace);
int y = (hoverPoint.y() - headerHeight - headerSpace) / (cellH + cellSpace);
if (x < 1 || x > 7 || y < 0 || y > 5) {
return QRectF();
}
//FIXME: this should be a hint or something somewhere
hoverDay = x - 1;
hoverWeek = y;
//kDebug () << x << y;
return QRectF(q->cellX(x - 1) - glowRadius, q->cellY(y) - glowRadius,
cellW + glowRadius * 2, cellH + glowRadius * 2);
}
void updateHoveredPainting(CalendarTable *q, const QPointF &hoverPoint)
{
QRectF newHoverRect = hoveredCellRect(q, hoverPoint);
// now update what is needed, and only what is needed!
if (newHoverRect.isValid() && newHoverRect != hoverRect) {
if (hoverRect.isValid()) {
q->update(hoverRect);
}
q->update(newHoverRect);
}
hoverRect = newHoverRect;
}
int cell(int week, int weekDay, CalendarTable::CellTypes *type, QDate &cellDate)
{
QDate myDate;
if ((week == 0) && (weekDay < firstMonthDayIndex(year, month))){
int prevMonth = (month == 1) ? 12 : month - 1;
calendar->setYMD(myDate, year, prevMonth, 1);
if (type) {
(*type) |= CalendarTable::NotInCurrentMonth;
}
calendar->setYMD(cellDate, (prevMonth == 12) ? year - 1 : year, prevMonth, calendar->daysInMonth(myDate) - (firstMonthDayIndex(year, month) - 1 - weekDay));
return calendar->daysInMonth(myDate) - (firstMonthDayIndex(year, month) - 1 - weekDay);
} else {
calendar->setYMD(myDate, year, month, 1);
int day = (week * calendar->daysInWeek(date) + weekDay) - firstMonthDayIndex(year, month) + 1;
if (day <= calendar->daysInMonth(myDate)) {
if (type) {
(*type) &= ~CalendarTable::NotInCurrentMonth;
}
calendar->setYMD(cellDate, year, month, day);
return day;
} else {
if (type) {
(*type) |= CalendarTable::NotInCurrentMonth;
}
int nextMonth = (month == 12) ? 1 : month + 1;
calendar->setYMD(cellDate, (nextMonth == 1) ? year + 1 : year, nextMonth, day - calendar->daysInMonth(myDate));
return day - calendar->daysInMonth(myDate);
}
}
}
Plasma::Svg *svg;
const KCalendarSystem *calendar;
QBrush highlightBrush;
QDate date;
QRectF hoverRect;
int month;
int year;
int hoverWeek;
int hoverDay;
int centeringSpace;
int cellW;
int cellH;
int cellSpace;
int headerHeight;
int headerSpace;
int weekBarSpace;
int glowRadius;
};
CalendarTable::CalendarTable(const QDate &date, QGraphicsWidget *parent)
: QGraphicsWidget(parent), d(new CalendarTablePrivate(this, date))
{
}
CalendarTable::CalendarTable(QGraphicsWidget *parent)
: QGraphicsWidget(parent), d(new CalendarTablePrivate(this))
{
setAcceptHoverEvents(true);
}
CalendarTable::~CalendarTable()
{
delete d;
}
const KCalendarSystem *CalendarTable::calendar() const
{
return d->calendar;
}
bool CalendarTable::setCalendar(KCalendarSystem *calendar)
{
d->calendar = calendar;
return false;
}
bool CalendarTable::setDate(const QDate &date)
{
int oldYear = d->year;
int oldMonth = d->month;
QDate oldDate = d->date;
d->date = date;
d->year = d->calendar->year(date);
d->month = d->calendar->month(date);
bool fullUpdate = false;
if (oldYear != d->year){
emit displayedYearChanged(d->year, d->month);
fullUpdate = true;
}
if (oldMonth != d->month){
emit displayedMonthChanged(d->year, d->month);
fullUpdate = true;
}
d->updateHoveredPainting(this, QPointF());
if (fullUpdate) {
update();
} else {
// only update the old and the new areas
int offset = d->firstMonthDayIndex(d->year, d->month);
int daysInWeek = d->calendar->daysInWeek(d->date);
int day = d->calendar->day(oldDate);
int x = ((offset + day - 1) % daysInWeek);
if (x == 0) {
x = daysInWeek;
}
int y = (offset + day - 2) / daysInWeek;
update(cellX(x - 1) - d->glowRadius, cellY(y) - d->glowRadius,
d->cellW + d->glowRadius * 2, d->cellH + d->glowRadius * 2);
day = d->calendar->day(date);
x = (offset + day - 1) % daysInWeek;
if (x == 0) {
x = daysInWeek;
}
y = (offset + day - 2) / daysInWeek;
update(cellX(x - 1) - d->glowRadius, cellY(y) - d->glowRadius,
d->cellW + d->glowRadius * 2, d->cellH + d->glowRadius * 2);
}
return false;
}
const QDate& CalendarTable::date() const
{
return d->date;
}
int CalendarTable::cellX(int weekDay)
{
return boundingRect().x() + d->centeringSpace +
d->weekBarSpace + d->cellW +
((d->cellW + d->cellSpace) * (weekDay));
}
int CalendarTable::cellY(int week)
{
return (int) boundingRect().y() + (d->cellW + d->cellSpace) * (week) + d->headerHeight + d->headerSpace;
}
void CalendarTable::wheelEvent(QGraphicsSceneWheelEvent * event)
{
Q_UNUSED(event);
if (event->delta() < 0) {
if (d->month == 12) {
d->month = 1;
d->year++;
emit displayedYearChanged(d->year, d->month);
} else {
d->month++;
}
emit displayedMonthChanged(d->year, d->month);
} else if (event->delta() > 0) {
if (d->month == 1) {
d->month = 12;
d->year--;
emit displayedYearChanged(d->year, d->month);
} else {
d->month--;
}
emit displayedMonthChanged(d->year, d->month);
}
update();
}
void CalendarTable::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
event->accept();
if ((event->pos().x() >= cellX(0)) && (event->pos().x() <= cellX(d->calendar->daysInWeek(d->date)) - d->cellSpace) &&
(event->pos().y() >= cellY(0)) && (event->pos().y() <= cellY(5) - d->cellSpace)){
int week = -1;
int weekDay = -1;
QDate cellDate;
for (int i = 0; i < d->calendar->daysInWeek(d->date); i++) {
if ((event->pos().x() >= cellX(i)) && (event->pos().x() <= cellX(i + 1) - d->cellSpace))
weekDay = i;
}
for (int i = 0; i < 5; i++) {
if ((event->pos().y() >= cellY(i)) && (event->pos().y() <= cellY(i + 1) - d->cellSpace))
week = i;
}
if ((week >= 0) && (weekDay >= 0)) {
d->hoverDay = -1;
d->hoverWeek = -1;
QDate tmpDate;
QDate oldDate = d->date;
d->cell(week, weekDay + 1, 0, tmpDate);
if (tmpDate == oldDate) {
return;
}
setDate(tmpDate);
emit dateChanged(tmpDate, oldDate);
emit dateChanged(tmpDate);
}
}
}
void CalendarTable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
mousePressEvent(event);
}
void CalendarTable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Q_UNUSED(event);
emit tableClicked();
}
void CalendarTable::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
{
d->updateHoveredPainting(this, event->pos());
}
void CalendarTable::resizeEvent(QGraphicsSceneResizeEvent * event)
{
Q_UNUSED(event);
QRectF r = contentsRect();
int rectSize = int(qMin(r.width() / 8, r.height() / 6));
//Using integers to help to keep things aligned to the grid
//kDebug() << r.width() << rectSize;
d->cellSpace = qMax(1, qMin(4, rectSize / 20));
d->headerSpace = d->cellSpace * 2;
d->weekBarSpace = d->cellSpace * 2 + 1;
d->cellH = rectSize - d->cellSpace;
d->cellW = rectSize - d->cellSpace;
d->glowRadius = d->cellW * .1;
d->headerHeight = (int) (d->cellH / 1.5);
d->centeringSpace = qMax(0, int((r.width() - (rectSize * 8) - (d->cellSpace * 7)) / 2));
}
void CalendarTable::paintCell(QPainter *p, int cell, int week, int weekDay, CellTypes type, const QDate &cellDate)
{
QString cellSuffix = cellSVGSuffix(cell, week, weekDay, type & NotInCurrentMonth, cellDate);
d->svg->paint(p, QRectF(cellX(weekDay), cellY(week), d->cellW, d->cellH), cellSuffix);
}
void CalendarTable::paintBorder(QPainter *p, int cell, int week, int weekDay, CellTypes type, const QDate &cellDate)
{
Q_UNUSED(cell);
Q_UNUSED(cellDate);
if (type & Hovered) {
p->fillRect(QRect(cellX(weekDay), cellY(week), d->cellW, d->cellH), d->highlightBrush);
}
QString elementId;
if (type & Today) {
elementId = "today";
} else if (type & Selected) {
elementId = "selected";
} else {
return;
}
d->svg->paint(p, QRectF(cellX(weekDay) - 1, cellY(week) - 1,
d->cellW + 1, d->cellH + 2),
elementId);
}
QString CalendarTable::cellSVGSuffix(int cell, int week, int weekDay, CellTypes type, const QDate &cellDate)
{
Q_UNUSED(week);
Q_UNUSED(weekDay);
Q_UNUSED(cellDate);
return QString::number(cell) + (type & NotInCurrentMonth ? "-grayed" : "");
}
void CalendarTable::paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(widget);
int daysInWeek = d->calendar->daysInWeek(d->date);
// Draw weeks numbers column and day header
QRectF r = boundingRect();
d->svg->paint(p, QRectF(r.x() + d->centeringSpace, cellY(0), d->cellW,
cellY(5) - cellY(0) - d->cellSpace), "weeksColumn");
d->svg->paint(p, QRectF(r.x() + d->centeringSpace, r.y(),
cellX(daysInWeek) - r.x() - d->cellSpace - d->centeringSpace, d->headerHeight), "weekDayHeader");
QList<CalendarCellBorder> borders;
QList<CalendarCellBorder> hovers;
QDate currentDate = QDate::currentDate(); //FIXME: calendar timezone
//kDebug() << "exposed: " << option->exposedRect;
for (int week = 0; week < 5; week++) {
for (int weekDay = 0; weekDay < daysInWeek; weekDay++) {
int x = cellX(weekDay);
int y = cellY(week);
QRectF cellRect(x, y, d->cellW, d->cellH);
if (!cellRect.intersects(option->exposedRect)) {
continue;
}
QDate cellDate(d->date.year(), d->date.month(), (week * 7) + (weekDay + 1));
CalendarTable::CellTypes type(CalendarTable::NoType);
// get cell info
int cell = d->cell(week, weekDay + 1, &type, cellDate);
// check what kind of cell we are
if (cellDate == currentDate) {
type |= Today;
}
if (cellDate == d->date) {
type |= Selected;
}
if (type != CalendarTable::NoType && type != CalendarTable::NotInCurrentMonth) {
borders.append(CalendarCellBorder(cell, week, weekDay, type, cellDate));
}
if (week == d->hoverWeek && weekDay == d->hoverDay) {
type |= Hovered;
hovers.append(CalendarCellBorder(cell, week, weekDay, type, cellDate));
}
paintCell(p, cell, week, weekDay, type, cellDate);
if (weekDay == 0) {
QRectF cellRect(r.x() + d->centeringSpace, y, d->cellW, d->cellH);
d->svg->paint(p, cellRect, "week" + QString::number(d->calendar->weekNumber(cellDate)));
}
}
}
// Draw days
if (option->exposedRect.intersects(QRect(r.x(), r.y(), r.width(), d->headerHeight))) {
p->setPen(Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
int weekStartDay = d->calendar->weekStartDay();
for (int i = 0; i < daysInWeek; i++){
int weekDay = ((i + weekStartDay - 1) % daysInWeek) + 1;
QString dayName = d->calendar->weekDayName(weekDay, KCalendarSystem::ShortDayName);
p->drawText(QRectF(cellX(i), r.y(), d->cellW, d->headerHeight), Qt::AlignCenter, dayName);
}
}
// Draw hovers
foreach (const CalendarCellBorder &border, hovers) {
p->save();
paintBorder(p, border.cell, border.week, border.weekDay, border.type, border.date);
p->restore();
}
// Draw borders
foreach (const CalendarCellBorder &border, borders) {
p->save();
paintBorder(p, border.cell, border.week, border.weekDay, border.type, border.date);
p->restore();
}
/*
p->save();
p->setPen(Qt::red);
p->drawRect(option->exposedRect.adjusted(1, 1, -2, -2));
p->restore();
*/
}
} //namespace Plasma
#include "calendartable.moc"

View File

@ -0,0 +1,90 @@
/*
* Copyright 2008 Davide Bettio <davide.bettio@kdemail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef PLASMA_CALENDARWIDGET_H
#define PLASMA_CALENDARWIDGET_H
#include <QtGui/QGraphicsWidget>
#include "plasmaclock_export.h"
#include <kcalendarsystem.h>
namespace Plasma
{
class CalendarTablePrivate;
class PLASMACLOCK_EXPORT CalendarTable : public QGraphicsWidget
{
Q_OBJECT
public:
enum CellType { NoType = 0,
Today = 1,
Selected = 2,
Hovered = 4,
NotInCurrentMonth = 8 };
Q_DECLARE_FLAGS(CellTypes, CellType)
explicit CalendarTable(QGraphicsWidget *parent = 0);
CalendarTable(const QDate &, QGraphicsWidget *parent = 0);
~CalendarTable();
const KCalendarSystem *calendar () const;
bool setDate(const QDate &date);
const QDate& date() const;
bool setCalendar(KCalendarSystem *calendar = 0);
Q_SIGNALS:
void dateChanged(const QDate &cur, const QDate &old);
void dateChanged(const QDate &date);
void tableClicked();
void displayedMonthChanged(int calendarSystemYear, int calendarSystemMonth);
void displayedYearChanged(int calendarSystemYear, int calendarSystemMonth);
protected:
int cellX(int weekDay);
int cellY(int week);
void paint(QPainter *p, const QStyleOptionGraphicsItem *option, QWidget *widget = 0);
void wheelEvent(QGraphicsSceneWheelEvent *event);
void mousePressEvent(QGraphicsSceneMouseEvent *event);
void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
void resizeEvent(QGraphicsSceneResizeEvent * event);
virtual void paintCell(QPainter *p, int cell, int week, int weekDay, CellTypes type, const QDate &cellDate);
virtual void paintBorder(QPainter *p, int cell, int week, int weekDay, CellTypes type, const QDate &cellDate);
virtual QString cellSVGSuffix(int cell, int week, int weekDay, CellTypes type, const QDate &cellDate);
private:
Q_PRIVATE_SLOT(d, void setupThemedElements())
friend class CalendarTablePrivate;
CalendarTablePrivate* const d;
};
}
Q_DECLARE_OPERATORS_FOR_FLAGS(Plasma::CalendarTable::CellTypes)
#endif

View File

@ -0,0 +1,379 @@
/***************************************************************************
* Copyright (C) 2007-2008 by Riccardo Iaconelli <riccardo@kde.org> *
* Copyright (C) 2007-2008 by Sebastian Kuegler <sebas@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#include "clockapplet.h"
#include <math.h>
#include <QtGui/QPainter>
#include <QtGui/QStyleOptionGraphicsItem>
#include <QtGui/QSpinBox>
#include <QtCore/QTimeLine>
#include <QtGui/QGraphicsProxyWidget>
#include <QtGui/QGraphicsSceneMouseEvent>
#include <QtGui/QGraphicsView>
#include <QtCore/QDate>
#include <KColorScheme>
#include <KConfigDialog>
#include <KConfigGroup>
#include <KDatePicker>
#include <KDebug>
#include <KDialog>
#include <KGlobalSettings>
#include <plasma/containment.h>
#include <plasma/corona.h>
#include <plasma/dataengine.h>
#include <plasma/dialog.h>
#include <plasma/extender.h>
#include <plasma/extenderitem.h>
#include <plasma/theme.h>
#include "calendar.h"
#include "ui_timezonesConfig.h"
class ClockApplet::Private
{
public:
Private()
: timezone(ClockApplet::localTimezone())
{}
Ui::timezonesConfig ui;
QString timezone;
QPoint clicked;
QStringList selectedTimezones;
QString prettyTimezone;
void addTzToTipText(QString &subText, QString tz)
{
Plasma::Applet applet;
Plasma::DataEngine::Data data = applet.dataEngine("time")->query(tz);
if (tz == "UTC") {
subText += "<br><b>UTC</b> ";
}
else {
subText += "<br><b>" + data["Timezone City"].toString().replace("_", " ")+"</b> ";
}
subText += KGlobal::locale()->formatTime(data["Time"].toTime(), false) + ", ";
subText += KGlobal::locale()->formatDate(data["Date"].toDate());
}
};
ClockApplet::ClockApplet(QObject *parent, const QVariantList &args)
: Plasma::PopupApplet(parent, args),
d(new Private)
{
setPopupIcon(QIcon());
}
ClockApplet::~ClockApplet()
{
delete d;
}
void ClockApplet::toolTipAboutToShow()
{
updateContent();
}
void ClockApplet::toolTipHidden()
{
Plasma::ToolTipManager::self()->clearContent(this);
}
void ClockApplet::updateContent()
{
Plasma::ToolTipContent tipData;
{
// the main text contains the current timezone's time and date
Plasma::DataEngine::Data data = dataEngine("time")->query(currentTimezone());
QString mainText = d->prettyTimezone + " ";
mainText += KGlobal::locale()->formatTime(data["Time"].toTime(), false) + "<br>";
mainText += KGlobal::locale()->formatDate(data["Date"].toDate());
tipData.setMainText(mainText);
}
QString subText;
if (!isLocalTimezone()) {
d->addTzToTipText(subText, localTimezone());
}
foreach (const QString &tz, getSelectedTimezones()) {
if (tz == currentTimezone()) {
continue;
}
d->addTzToTipText(subText, tz);
}
tipData.setSubText(subText);
// query for custom content
Plasma::ToolTipContent customContent = toolTipContent();
if (customContent.image().isNull()) {
tipData.setImage(KIcon("chronometer").pixmap(IconSize(KIconLoader::Desktop)));
} else {
tipData.setImage(customContent.image());
}
if (!customContent.mainText().isEmpty()) {
// add their main text
tipData.setMainText(customContent.mainText() + "<br>" + tipData.mainText());
}
if (!customContent.subText().isEmpty()) {
// add their sub text
tipData.setSubText(customContent.subText() + "<br>" + tipData.subText());
}
tipData.setAutohide(false);
Plasma::ToolTipManager::self()->setContent(this, tipData);
}
Plasma::ToolTipContent ClockApplet::toolTipContent()
{
return Plasma::ToolTipContent();
}
void ClockApplet::createConfigurationInterface(KConfigDialog *parent)
{
createClockConfigurationInterface(parent);
QWidget *widget = new QWidget();
d->ui.setupUi(widget);
parent->addPage(widget, i18n("Time Zones"), Applet::icon());
foreach (const QString &tz, d->selectedTimezones) {
d->ui.timeZones->setSelected(tz, true);
}
updateClockDefaultsTo();
int defaultSelection = d->ui.clockDefaultsTo->findText(currentTimezone());
if (defaultSelection >= 0) {
d->ui.clockDefaultsTo->setCurrentIndex(defaultSelection);
}
parent->setButtons( KDialog::Ok | KDialog::Cancel | KDialog::Apply );
connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
connect(d->ui.timeZones, SIGNAL(itemSelectionChanged()), this, SLOT(updateClockDefaultsTo()));
#if 0
#ifdef CLOCK_APPLET_CONF
ui.localTimeZone->setChecked(isLocalTimezone());
ui.timeZones->setEnabled(!isLocalTimezone());
foreach (const QString &str, selectedTimezones) {
ui.timeZones->setSelected(str, true);
}
#endif
#endif
}
void ClockApplet::createClockConfigurationInterface(KConfigDialog *parent)
{
Q_UNUSED(parent)
}
void ClockApplet::clockConfigAccepted()
{
}
void ClockApplet::configAccepted()
{
KConfigGroup cg = config();
d->selectedTimezones = d->ui.timeZones->selection();
cg.writeEntry("timeZones", d->selectedTimezones);
QString newTimezone;
if (d->ui.clockDefaultsTo->currentIndex() == 0) {
//The first position in ui.clockDefaultsTo is "Local"
newTimezone = localTimezone();
} else {
newTimezone = d->ui.clockDefaultsTo->currentText();
}
changeEngineTimezone(currentTimezone(), newTimezone);
setCurrentTimezone(newTimezone);
clockConfigAccepted();
constraintsEvent(Plasma::SizeConstraint);
update();
emit configNeedsSaving();
}
void ClockApplet::updateClockDefaultsTo()
{
QString oldSelection = d->ui.clockDefaultsTo->currentText();
d->ui.clockDefaultsTo->clear();
d->ui.clockDefaultsTo->addItems(d->ui.timeZones->selection());
d->ui.clockDefaultsTo->insertItem(0, "Local");
int newPosition = d->ui.clockDefaultsTo->findText(oldSelection);
if (newPosition >= 0) {
d->ui.clockDefaultsTo->setCurrentIndex(newPosition);
}
if (d->ui.clockDefaultsTo->count() > 1) {
d->ui.clockDefaultsTo->setEnabled(true);
}
else {
// Only "Local" in ui.clockDefaultsTo
d->ui.clockDefaultsTo->setEnabled(false);
}
}
void ClockApplet::changeEngineTimezone(const QString &oldTimezone, const QString &newTimezone)
{
// reimplemented by subclasses to get the new data
Q_UNUSED(oldTimezone);
Q_UNUSED(newTimezone);
}
void ClockApplet::wheelEvent(QGraphicsSceneWheelEvent *event)
{
if (d->selectedTimezones.count() < 1) {
return;
}
QString newTimezone;
if (isLocalTimezone()) {
if (event->delta() > 0) {
newTimezone = d->selectedTimezones.last();
} else {
newTimezone = d->selectedTimezones.first();
}
} else {
int current = d->selectedTimezones.indexOf(currentTimezone());
if (event->delta() > 0) {
int previous = current - 1;
if (previous < 0) {
newTimezone = localTimezone();
} else {
newTimezone = d->selectedTimezones.at(previous);
}
} else {
int next = current + 1;
if (next > d->selectedTimezones.count() - 1) {
newTimezone = localTimezone();
} else {
newTimezone = d->selectedTimezones.at(next);
}
}
}
changeEngineTimezone(currentTimezone(), newTimezone);
setCurrentTimezone(newTimezone);
update();
}
void ClockApplet::initExtenderItem(Plasma::ExtenderItem *item)
{
Plasma::Calendar *calendar = new Plasma::Calendar;
calendar->setMinimumSize(QSize(200, 180));
Plasma::DataEngine::Data data = dataEngine("time")->query(currentTimezone());
QDate date = data["Date"].toDate();
if (date.isValid()) {
calendar->setDate(date);
}
item->setWidget(calendar);
item->setTitle(i18n("Calendar"));
}
void ClockApplet::init()
{
KConfigGroup cg = config();
d->selectedTimezones = cg.readEntry("timeZones", QStringList());
d->timezone = cg.readEntry("timezone", d->timezone);
if (d->timezone == "UTC") {
d->prettyTimezone = d->timezone;
} else {
if (!isLocalTimezone()) {
QStringList tzParts = d->timezone.split("/");
d->prettyTimezone = tzParts.value(1);
} else {
d->prettyTimezone = localTimezone();
}
}
//avoid duplication
if (!extender()->item("calendar")) {
Plasma::ExtenderItem *eItem = new Plasma::ExtenderItem(extender());
eItem->setName("calendar");
initExtenderItem(eItem);
}
Plasma::ToolTipManager::self()->registerWidget(this);
}
void ClockApplet::setCurrentTimezone(const QString &tz)
{
if (d->timezone == tz) {
return;
}
d->timezone = tz;
if (tz == "UTC") {
d->prettyTimezone = tz;
} else {
QStringList tzParts = tz.split("/");
d->prettyTimezone = tzParts.value(1);
}
KConfigGroup cg = config();
cg.writeEntry("timezone", tz);
emit configNeedsSaving();
}
QString ClockApplet::currentTimezone() const
{
return d->timezone;
}
QString ClockApplet::prettyTimezone() const
{
return d->prettyTimezone;
}
QStringList ClockApplet::getSelectedTimezones() const
{
return d->selectedTimezones;
}
bool ClockApplet::isLocalTimezone() const
{
return d->timezone == localTimezone();
}
QString ClockApplet::localTimezone()
{
return "Local";
}
#include "clockapplet.moc"

View File

@ -0,0 +1,82 @@
/***************************************************************************
* Copyright (C) 2007-2008 by Riccardo Iaconelli <riccardo@kde.org> *
* Copyright (C) 2007-2008 by Sebastian Kuegler <sebas@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#ifndef CLOCKAPPLET_H
#define CLOCKAPPLET_H
#include <QtCore/QTime>
#include <QtCore/QDate>
#include <plasma/dataengine.h>
#include <plasma/dialog.h>
#include <plasma/popupapplet.h>
#include <plasma/tooltipmanager.h>
#include "plasmaclock_export.h"
class KDialog;
class KConfigDialog;
namespace Plasma
{
class Svg;
}
class PLASMACLOCK_EXPORT ClockApplet : public Plasma::PopupApplet
{
Q_OBJECT
public:
ClockApplet(QObject *parent, const QVariantList &args);
~ClockApplet();
void init();
QString currentTimezone() const;
QString prettyTimezone() const;
bool isLocalTimezone() const;
QStringList getSelectedTimezones() const;
static QString localTimezone();
public Q_SLOTS:
void toolTipAboutToShow();
void toolTipHidden();
protected:
virtual void createClockConfigurationInterface(KConfigDialog *parent);
virtual void clockConfigAccepted();
virtual void changeEngineTimezone(const QString &oldTimezone, const QString &newTimezone);
virtual Plasma::ToolTipContent toolTipContent();
void wheelEvent(QGraphicsSceneWheelEvent *event);
void createConfigurationInterface(KConfigDialog *parent);
void initExtenderItem(Plasma::ExtenderItem *item);
void updateContent();
protected Q_SLOTS:
void setCurrentTimezone(const QString &tz);
void configAccepted();
void updateClockDefaultsTo();
private:
class Private;
Private * const d;
};
#endif

View File

@ -0,0 +1,71 @@
/***************************************************************************
* Copyright (C) 2007 by Riccardo Iaconelli <riccardo@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#include "clocknumber.h"
class Number::Private
{
public:
char data;
};
Number::Number(QChar value)
: d(new Private)
{
d->data = value.toAscii();
}
Number::~Number()
{
delete d;
}
void Number::operator--()
{
if (d->data == '0') {
d->data = '9';
} else {
d->data--;
}
}
void Number::operator++()
{
if (d->data == '9') {
d->data = '0';
} else {
d->data++;
}
}
void Number::operator=(QChar value)
{
d->data = value.toAscii();
}
bool Number::operator==(char value)
{
return d->data == value;
}
Number::operator char()
{
return d->data;
}

View File

@ -0,0 +1,43 @@
/***************************************************************************
* Copyright (C) 2007 by Riccardo Iaconelli <riccardo@kde.org> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
***************************************************************************/
#ifndef CLOCKNUMBER_H
#define CLOCKNUMBER_H
#include <QtCore/QChar>
#include "plasmaclock_export.h"
class PLASMACLOCK_EXPORT Number
{
public:
Number(QChar value);
~Number();
void operator--();
void operator++();
void operator=(QChar value);
bool operator==(char value);
operator char();
private:
class Private;
Private * const d;
};
#endif

View File

@ -0,0 +1,40 @@
/* This file is part of the KDE project
Copyright (C) 2007 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef PLASMACLOCK_EXPORT_H
#define PLASMACLOCK_EXPORT_H
/* needed for KDE_EXPORT and KDE_IMPORT macros */
#include <kdemacros.h>
#ifndef PLASMACLOCK_EXPORT
# if defined(MAKE_PLASMACLOCK_LIB)
/* We are building this library */
# define PLASMACLOCK_EXPORT KDE_EXPORT
# else
/* We are using this library */
# define PLASMACLOCK_EXPORT KDE_IMPORT
# endif
#endif
# ifndef PLASMACLOCK_EXPORT_DEPRECATED
# define PLASMACLOCK_EXPORT_DEPRECATED KDE_DEPRECATED PLASMACLOCK_EXPORT
# endif
#endif

View File

@ -0,0 +1,96 @@
<ui version="4.0" >
<class>timezonesConfig</class>
<widget class="QWidget" name="timezonesConfig" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>308</width>
<height>227</height>
</rect>
</property>
<layout class="QVBoxLayout" >
<item>
<widget class="KTimeZoneWidget" name="timeZones" >
<property name="minimumSize" >
<size>
<width>300</width>
<height>150</height>
</size>
</property>
<property name="toolTip" >
<string>Select one or several time zones.</string>
</property>
<property name="whatsThis" >
<string>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
&lt;html>&lt;head>&lt;meta name="qrichtext" content="1" />&lt;style type="text/css">
p, li { white-space: pre-wrap; }
&lt;/style>&lt;/head>&lt;body style=" font-family:'Sans Serif'; font-size:10pt; font-weight:400; font-style:normal;">
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Your &lt;span style=" font-weight:600;">Local&lt;/span> time and time zone are defined in System Settings, in the Date and Time tab. As default, your plasma clock will use this setting.&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The plasma clock tooltip can display the time in several other time zones: to do so, select one or several more time zones in the list. Click on a line to select it and click on it again to deselect it. &lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">After you validate your choices with the OK button, when your mouse is over the clock, a tooltip will display the time in all the selected time zones.&lt;/p>
&lt;p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">To select a &lt;span style=" font-weight:600;">Default&lt;/span> time zone: you can either scroll over the clock with your mouse wheel and set the one you want or you can set it with "Clock defaults to:". .&lt;/p>&lt;/body>&lt;/html></string>
</property>
<property name="selectionMode" >
<enum>QAbstractItemView::MultiSelection</enum>
</property>
<column>
<property name="text" >
<string>Area</string>
</property>
</column>
<column>
<property name="text" >
<string>Region</string>
</property>
</column>
<column>
<property name="text" >
<string>Comment</string>
</property>
</column>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>Clock defaults to:</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QComboBox" name="clockDefaultsTo" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip" >
<string>The time the clock will display</string>
</property>
<property name="whatsThis" >
<string>The clock will display the time for the selected default zone.
Local is the time you set in System Settings.</string>
</property>
<property name="insertPolicy" >
<enum>QComboBox::InsertAlphabetically</enum>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KTimeZoneWidget</class>
<extends>QTreeWidget</extends>
<header>ktimezonewidget.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,127 @@
/*
* Copyright 2008 Marco Martin <notmart@gmail.com>
* Copyright 2008 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "toolbutton.h"
#include <QAction>
#include <QPainter>
#include <QPaintEvent>
#include <QStyle>
#include <QStyleOptionToolButton>
#include <plasma/paintutils.h>
#include <plasma/theme.h>
#include <plasma/framesvg.h>
ToolButton::ToolButton()
: QToolButton(),
m_action(0)
{
setAttribute(Qt::WA_NoSystemBackground);
m_background = new Plasma::FrameSvg(this);
m_background->setImagePath("widgets/button");
m_background->setCacheAllRenderedFrames(true);
m_background->setElementPrefix("plain");
}
ToolButton::ToolButton(QWidget *parent)
: QToolButton(parent),
m_action(0)
{
m_background = new Plasma::FrameSvg(this);
m_background->setImagePath("widgets/button");
m_background->setCacheAllRenderedFrames(true);
m_background->setElementPrefix("plain");
}
void ToolButton::setAction(QAction *action)
{
if (!action) {
return;
}
if (m_action) {
disconnect(m_action, SIGNAL(changed()), this, SLOT(syncToAction()));
disconnect(this, SIGNAL(clicked()), m_action, SLOT(trigger()));
}
m_action = action;
connect(m_action, SIGNAL(changed()), this, SLOT(syncToAction()));
connect(this, SIGNAL(clicked()), m_action, SLOT(trigger()));
connect(m_action, SIGNAL(destroyed(QObject*)), this, SLOT(actionDestroyed(QObject*)));
syncToAction();
}
void ToolButton::syncToAction()
{
if (!m_action) {
return;
}
setIcon(m_action->icon());
setText(m_action->text());
if (toolButtonStyle() == Qt::ToolButtonIconOnly) {
setToolTip(m_action->text());
}
setCheckable(m_action->isCheckable());
if (m_action->actionGroup()) {
setAutoExclusive(m_action->actionGroup()->isExclusive());
}
setEnabled(m_action->isEnabled());
}
void ToolButton::actionDestroyed(QObject *)
{
m_action = 0;
}
void ToolButton::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
QStyleOptionToolButton buttonOpt;
initStyleOption(&buttonOpt);
if ((buttonOpt.state & QStyle::State_MouseOver) || (buttonOpt.state & QStyle::State_On)) {
if (buttonOpt.state & QStyle::State_Sunken || (buttonOpt.state & QStyle::State_On)) {
m_background->setElementPrefix("pressed");
} else {
m_background->setElementPrefix("normal");
}
m_background->resizeFrame(size());
m_background->paintFrame(&painter);
buttonOpt.palette.setColor(QPalette::ButtonText, Plasma::Theme::defaultTheme()->color(Plasma::Theme::ButtonTextColor));
} else {
buttonOpt.palette.setColor(QPalette::ButtonText, Plasma::Theme::defaultTheme()->color(Plasma::Theme::TextColor));
}
style()->drawControl(QStyle::CE_ToolButtonLabel, &buttonOpt, &painter, this);
}
#include "toolbutton.moc"

View File

@ -0,0 +1,53 @@
/*
* Copyright 2008 Marco Martin <notmart@gmail.com>
* Copyright 2008 Aaron Seigo <aseigo@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef TOOLBUTTON_H
#define TOOLBUTTON_H
#include <QToolButton>
namespace Plasma
{
class FrameSvg;
}
class ToolButton: public QToolButton
{
Q_OBJECT
public:
ToolButton();
ToolButton(QWidget *parent);
void setAction(QAction *action);
protected:
void paintEvent(QPaintEvent *event);
protected slots:
void actionDestroyed(QObject *);
void syncToAction();
private:
QAction *m_action;
Plasma::FrameSvg *m_background;
};
#endif

View File

@ -0,0 +1,41 @@
project(nepomukquery)
include_directories(
${QT_INCLUDES}
${KDE4_INCLUDES}
${CMAKE_SOURCE_DIR}
${SOPRANO_INCLUDE_DIR}
${NEPOMUK_INCLUDE_DIR}
)
add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS})# -fPIC)
set(nepomukquery_SRC
result.cpp
term.cpp
query.cpp
queryparser.cpp
dbusoperators.cpp
)
kde4_add_library(nepomukquery SHARED ${nepomukquery_SRC}
)
set_target_properties(nepomukquery PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION})
target_link_libraries(nepomukquery
${QT_QTCORE_LIBRARY}
${KDE4_KDECORE_LIBS}
${SOPRANO_LIBRARIES}
)
install(TARGETS nepomukquery ${INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES
nepomukquery_export.h
queryparser.h
result.h
term.h
query.h
DESTINATION ${INCLUDE_INSTALL_DIR}/nepomuk
)

2
nepomukquery/Messages.sh Executable file
View File

@ -0,0 +1,2 @@
#! /usr/bin/env bash
$XGETTEXT *.cpp -o $podir/nepomuksearch.pot

View File

@ -0,0 +1,341 @@
/*
Copyright (c) 2008 Sebastian Trueg <trueg@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "dbusoperators.h"
#include <QtDBus/QDBusMetaType>
Q_DECLARE_METATYPE(Nepomuk::Search::Result)
Q_DECLARE_METATYPE(Nepomuk::Search::Term)
Q_DECLARE_METATYPE(Nepomuk::Search::Query)
Q_DECLARE_METATYPE(Soprano::Node)
Q_DECLARE_METATYPE(QList<int>)
Q_DECLARE_METATYPE(QList<Nepomuk::Search::Result>)
void Nepomuk::Search::registerDBusTypes()
{
qDBusRegisterMetaType<Nepomuk::Search::Result>();
qDBusRegisterMetaType<QList<Nepomuk::Search::Result> >();
qDBusRegisterMetaType<Nepomuk::Search::Term>();
qDBusRegisterMetaType<Nepomuk::Search::Query>();
qDBusRegisterMetaType<Soprano::Node>();
}
QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Result& result )
{
//
// Signature: (sda{s(isss)})
//
arg.beginStructure();
arg << result.resourceUri().toString() << result.score();
arg.beginMap( QVariant::String, qMetaTypeId<Soprano::Node>() );
QHash<QUrl, Soprano::Node> rp = result.requestProperties();
for ( QHash<QUrl, Soprano::Node>::const_iterator it = rp.constBegin(); it != rp.constEnd(); ++it ) {
arg.beginMapEntry();
arg << it.key().toString() << it.value();
arg.endMapEntry();
}
arg.endMap();
arg.endStructure();
return arg;
}
const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Result& result )
{
//
// Signature: (sda{s(isss)})
//
arg.beginStructure();
QString uri;
double score = 0.0;
arg >> uri >> score;
result = Nepomuk::Search::Result( QUrl( uri ), score );
arg.beginMap();
while ( !arg.atEnd() ) {
QString rs;
Soprano::Node node;
arg.beginMapEntry();
arg >> rs >> node;
arg.endMapEntry();
result.addRequestProperty( QUrl( rs ), node );
}
arg.endMap();
arg.endStructure();
return arg;
}
QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Term& term )
{
//
// Signature: (ii(isss)sss)
// i -> type
// i -> comparator type
// (isss) -> Soprano::LiteralValue encoded as a Soprano::Node for simplicity
// s -> resource
// s -> field
// s -> property
//
arg.beginStructure();
arg << ( int )term.type()
<< ( int )term.comparator()
<< Soprano::Node( term.value() )
<< term.resource().toString()
<< term.field()
<< term.property().toString();
arg.endStructure();
return arg;
}
const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Term& term )
{
//
// Signature: (ii(isss)sss)
// i -> type
// i -> comparator type
// (isss) -> Soprano::LiteralValue encoded as a Soprano::Node for simplicity
// s -> resource
// s -> field
// s -> property
//
arg.beginStructure();
int type = Nepomuk::Search::Term::InvalidTerm;
int comparator = Nepomuk::Search::Term::Equal;
Soprano::Node valueNode;
QString resource, field, property;
arg >> type
>> comparator
>> valueNode
>> resource
>> field
>> property;
term.setType( Nepomuk::Search::Term::Type( type ) );
term.setComparator( Nepomuk::Search::Term::Comparator( comparator ) );
if ( valueNode.isLiteral() )
term.setValue( valueNode.literal() );
if ( !resource.isEmpty() )
term.setResource( QUrl( resource ) );
if ( !field.isEmpty() )
term.setField( field );
if ( !property.isEmpty() )
term.setProperty( QUrl( property ) );
arg.endStructure();
return arg;
}
// streaming a Query object is a bit tricky as it is a set of nested Term objects
// DBus does not allow arbitrary nesting of objects, thus we use a little trick:
// We store all used Term objects in a list and use integer indices pointing into
// this list to describe the nesting within the Term objects. This also means that
// a Term's subTerm list is replaced with a list of indices
namespace {
/**
* Build term relations for the last term in the list
*/
void buildTermRelations( QList<Nepomuk::Search::Term>& terms, QHash<int, QList<int> >& termRelations ) {
QList<Nepomuk::Search::Term> subTerms = terms.last().subTerms();
int termIndex = terms.count()-1;
for ( int i = 0; i < subTerms.count(); ++i ) {
terms.append( subTerms[i] );
termRelations[termIndex].append( terms.count()-1 );
buildTermRelations( terms, termRelations );
}
}
}
QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Query& query )
{
//
// Signature: (isa(ii(isss)sss)a{iai}ia{sb})
// i -> type
// s -> sparql query
// a(ii(isss)sss) -> array of terms (first is root term)
// a{iai} -> hash of term relations
// i -> limit
// a{sb} -> request properties
//
arg.beginStructure();
arg << ( int )query.type() << query.sparqlQuery();
QList<Nepomuk::Search::Term> terms;
QHash<int, QList<int> > termRelations;
if ( query.type() == Nepomuk::Search::Query::PlainQuery ) {
terms.append( query.term() );
buildTermRelations( terms, termRelations );
}
arg << terms;
arg.beginMap( QVariant::Int, qMetaTypeId<QList<int> >() );
for( QHash<int, QList<int> >::const_iterator it = termRelations.constBegin();
it != termRelations.constEnd(); ++it ) {
arg.beginMapEntry();
arg << it.key() << it.value();
arg.endMapEntry();
}
arg.endMap();
arg << query.limit();
arg.beginMap( QVariant::String, QVariant::Bool );
QList<Nepomuk::Search::Query::RequestProperty> requestProperties = query.requestProperties();
foreach( const Nepomuk::Search::Query::RequestProperty& rp, requestProperties ) {
arg.beginMapEntry();
arg << rp.first.toString() << rp.second;
arg.endMapEntry();
}
arg.endMap();
arg.endStructure();
return arg;
}
namespace {
Nepomuk::Search::Term rebuildTermFromTermList( const QList<Nepomuk::Search::Term>& terms,
const QHash<int, QList<int> >& termRelations,
int index = 0 ) {
Nepomuk::Search::Term root = terms[index];
foreach( int i, termRelations[index] ) {
root.addSubTerm( rebuildTermFromTermList( terms, termRelations, i ) );
}
return root;
}
}
const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Query& query )
{
//
// Signature: (isa(ii(isss)sss)a{iai}ia{sb})
// i -> type
// s -> sparql query
// a(ii(isss)sss) -> array of terms (first is root term)
// a{iai} -> hash of term relations
// i -> limit
// a{sb} -> request properties
//
arg.beginStructure();
int type = Nepomuk::Search::Query::InvalidQuery;
QString sparqlQuery;
QList<Nepomuk::Search::Term> terms;
QHash<int, QList<int> > termRelations;
int limit = 0;
arg >> type
>> sparqlQuery
>> terms;
arg.beginMap();
while ( !arg.atEnd() ) {
int termIndex = 0;
QList<int> indices;
arg.beginMapEntry();
arg >> termIndex >> indices;
arg.endMapEntry();
termRelations.insert( termIndex, indices );
}
arg.endMap();
arg >> limit;
arg.beginMap();
while ( !arg.atEnd() ) {
QString prop;
bool optional = true;
arg.beginMapEntry();
arg >> prop >> optional;
arg.endMapEntry();
query.addRequestProperty( QUrl( prop ), optional );
}
arg.endMap();
arg.endStructure();
if ( Nepomuk::Search::Query::Type( type ) == Nepomuk::Search::Query::PlainQuery ) {
query.setTerm( rebuildTermFromTermList( terms, termRelations ) );
}
else {
query.setSparqlQuery( sparqlQuery );
}
query.setLimit( limit );
return arg;
}
QDBusArgument& operator<<( QDBusArgument& arg, const Soprano::Node& node )
{
//
// Signature: (isss)
//
arg.beginStructure();
arg << ( int )node.type() << node.toString() << node.language() << node.dataType().toString();
arg.endStructure();
return arg;
}
const QDBusArgument& operator>>( const QDBusArgument& arg, Soprano::Node& node )
{
//
// Signature: (isss)
//
arg.beginStructure();
int type;
QString value, language, dataTypeUri;
arg >> type >> value >> language >> dataTypeUri;
if ( type == Soprano::Node::LiteralNode ) {
node = Soprano::Node( Soprano::LiteralValue::fromString( value, dataTypeUri ), language );
}
else if ( type == Soprano::Node::ResourceNode ) {
node = Soprano::Node( QUrl( value ) );
}
else if ( type == Soprano::Node::BlankNode ) {
node = Soprano::Node( value );
}
else {
node = Soprano::Node();
}
arg.endStructure();
return arg;
}

View File

@ -0,0 +1,48 @@
/*
Copyright (c) 2008 Sebastian Trueg <trueg@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _NEPOMUK_SEARCH_DBUS_OPERATORS_H_
#define _NEPOMUK_SEARCH_DBUS_OPERATORS_H_
#include <QtDBus/QDBusArgument>
#include "result.h"
#include "query.h"
#include "term.h"
#include "nepomukquery_export.h"
namespace Nepomuk {
namespace Search {
NEPOMUKQUERY_EXPORT void registerDBusTypes();
}
}
NEPOMUKQUERY_EXPORT QDBusArgument& operator<<( QDBusArgument& arg, const Soprano::Node& );
NEPOMUKQUERY_EXPORT const QDBusArgument& operator>>( const QDBusArgument& arg, Soprano::Node& );
NEPOMUKQUERY_EXPORT QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Term& term );
NEPOMUKQUERY_EXPORT const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Term& );
NEPOMUKQUERY_EXPORT QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Query& query );
NEPOMUKQUERY_EXPORT const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Query& );
NEPOMUKQUERY_EXPORT QDBusArgument& operator<<( QDBusArgument& arg, const Nepomuk::Search::Result& );
NEPOMUKQUERY_EXPORT const QDBusArgument& operator>>( const QDBusArgument& arg, Nepomuk::Search::Result& );
#endif

View File

@ -0,0 +1,36 @@
/* This file is part of the KDE project
Copyright (C) 2007 David Faure <faure@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef NEPOMUKQUERY_EXPORT_H
#define NEPOMUKQUERY_EXPORT_H
/* needed for KDE_EXPORT and KDE_IMPORT macros */
#include <kdemacros.h>
#ifndef NEPOMUKQUERY_EXPORT
# if defined(MAKE_NEPOMUKQUERY_LIB)
/* We are building this library */
# define NEPOMUKQUERY_EXPORT KDE_EXPORT
# else
/* We are using this library */
# define NEPOMUKQUERY_EXPORT KDE_IMPORT
# endif
#endif
#endif

195
nepomukquery/query.cpp Normal file
View File

@ -0,0 +1,195 @@
/*
This file is part of the Nepomuk KDE project.
Copyright (C) 2008 Sebastian Trueg <trueg@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "query.h"
#include "term.h"
#include <QtCore/QSharedData>
#include <QtCore/QDebug>
class Nepomuk::Search::Query::Private : public QSharedData
{
public:
Private()
: type( InvalidQuery ),
limit( 0 ) {
}
Type type;
Term term;
QString sparqlQuery;
int limit;
QList<RequestProperty> requestProperties;
};
Nepomuk::Search::Query::Query()
: d( new Private() )
{
}
Nepomuk::Search::Query::Query( const Query& other )
{
d = other.d;
}
Nepomuk::Search::Query::Query( const Term& term )
: d ( new Private() )
{
d->type = PlainQuery;
d->term = term;
}
Nepomuk::Search::Query::Query( const QString& sparqlQuery )
: d ( new Private() )
{
d->type = SPARQLQuery;
d->sparqlQuery = sparqlQuery;
}
Nepomuk::Search::Query::~Query()
{
}
Nepomuk::Search::Query& Nepomuk::Search::Query::operator=( const Query& other )
{
d = other.d;
return *this;
}
Nepomuk::Search::Query::Type Nepomuk::Search::Query::type() const
{
return d->type;
}
Nepomuk::Search::Term Nepomuk::Search::Query::term() const
{
return d->term;
}
int Nepomuk::Search::Query::limit() const
{
return d->limit;
}
QString Nepomuk::Search::Query::sparqlQuery() const
{
return d->sparqlQuery;
}
void Nepomuk::Search::Query::setTerm( const Term& term )
{
d->term = term;
d->type = PlainQuery;
}
void Nepomuk::Search::Query::setLimit( int limit )
{
d->limit = limit;
}
void Nepomuk::Search::Query::setSparqlQuery( const QString& qs )
{
d->sparqlQuery = qs;
d->term = Term();
d->type = SPARQLQuery;
}
void Nepomuk::Search::Query::addRequestProperty( const QUrl& property, bool optional )
{
d->requestProperties.append( qMakePair( property, optional ) );
}
void Nepomuk::Search::Query::clearRequestProperties()
{
d->requestProperties.clear();
}
QList<Nepomuk::Search::Query::RequestProperty> Nepomuk::Search::Query::requestProperties() const
{
return d->requestProperties;
}
namespace {
bool compareRequestProperties( const QList<Nepomuk::Search::Query::RequestProperty>& rp1, const QList<Nepomuk::Search::Query::RequestProperty>& rp2 ) {
// brute force
foreach( const Nepomuk::Search::Query::RequestProperty& rp, rp1 ) {
if ( !rp2.contains( rp ) ) {
return false;
}
}
foreach( const Nepomuk::Search::Query::RequestProperty& rp, rp2 ) {
if ( !rp1.contains( rp ) ) {
return false;
}
}
return true;
}
}
bool Nepomuk::Search::Query::operator==( const Query& other ) const
{
if ( d->type == other.d->type &&
d->limit == other.d->limit ) {
if ( d->type == SPARQLQuery ) {
return( d->sparqlQuery == other.d->sparqlQuery &&
compareRequestProperties( d->requestProperties, other.d->requestProperties ) );
}
else {
return( d->term == other.d->term &&
compareRequestProperties( d->requestProperties, other.d->requestProperties ) );
}
}
return false;
}
QDebug operator<<( QDebug dbg, const Nepomuk::Search::Query& query )
{
dbg << "(Query" << query.term() << query.limit() << ")";
return dbg;
}
uint Nepomuk::Search::qHash( const Nepomuk::Search::Query& query )
{
if ( query.type() == Nepomuk::Search::Query::SPARQLQuery )
return qHash( query.sparqlQuery() );
else
return qHash( query.term() );
}

122
nepomukquery/query.h Normal file
View File

@ -0,0 +1,122 @@
/*
This file is part of the Nepomuk KDE project.
Copyright (C) 2008 Sebastian Trueg <trueg@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _NEPOMUK_SEARCH_QUERY_H_
#define _NEPOMUK_SEARCH_QUERY_H_
#include <QtCore/QSharedDataPointer>
#include <QtCore/QList>
#include <QtCore/QPair>
#include <QtCore/QDebug>
#include "nepomukquery_export.h"
class QUrl;
namespace Nepomuk {
namespace Search {
class Term;
/**
* \class Query query.h nepomuk/query.h
*
* \brief A Nepomuk desktop query.
*
* A query can either be based on Term or a more complex
* SPARQL query.
*
* \author Sebastian Trueg <trueg@kde.org>
*/
class NEPOMUKQUERY_EXPORT Query
{
public:
enum Type {
InvalidQuery,
PlainQuery,
SPARQLQuery
};
/**
* Create an empty invalid query object.
*/
Query();
/**
* Create a query of type PlainQuery based on
* \a term.
*/
Query( const Term& term );
/**
* Create a SPARQL query. The query has to have one select variable called "?r"
*/
explicit Query( const QString& sparqlQuery );
/**
* Copy constructor.
*/
Query( const Query& );
/**
* Destructor
*/
~Query();
/**
* Assignment operator
*/
Query& operator=( const Query& );
Type type() const;
Term term() const;
QString sparqlQuery() const;
int limit() const;
void setTerm( const Term& );
void setSparqlQuery( const QString& );
void setLimit( int );
/**
* Add a property that should be reported with each search result.
* \param property The requested property.
* \param optional If \p true the property is optional, meaning it can
* be empty ins earch results.
*/
void addRequestProperty( const QUrl& property, bool optional = true );
void clearRequestProperties();
typedef QPair<QUrl, bool> RequestProperty;
QList<RequestProperty> requestProperties() const;
bool operator==( const Query& ) const;
private:
class Private;
QSharedDataPointer<Private> d;
};
NEPOMUKQUERY_EXPORT uint qHash( const Nepomuk::Search::Query& );
}
}
NEPOMUKQUERY_EXPORT QDebug operator<<( QDebug, const Nepomuk::Search::Query& );
#endif

View File

@ -0,0 +1,279 @@
/*
This file is part of the Nepomuk KDE project.
Copyright (C) 2007 Sebastian Trueg <trueg@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "queryparser.h"
#include "query.h"
#include "term.h"
#include <QtCore/QRegExp>
#include <QtCore/QSet>
#include <KDebug>
#include <KLocale>
/* Advanced queries:
* select distinct ?r ?p ?x ?label ?comment where { { ?r ?p ?x . } UNION { ?r ?p ?r2 . ?r2 ?p2 ?x . } . FILTER(isLiteral(?x)) . FILTER REGEX(STR(?p),'hastag','i') . FILTER REGEX(STR(?x),'nepomuk','i') . OPTIONAL { { ?r <http://www.w3.org/2000/01/rdf-schema#label> ?label } UNION { ?r <http://www.semanticdesktop.org/ontologies/2007/08/15/nao#prefLabel> ?label . } UNION { ?r <http://freedesktop.org/standards/xesam/1.0/core#name> ?label . } . ?r <http://www.w3.org/2000/01/rdf-schema#comment> ?comment . } . }
*/
namespace {
// a field differs from a plain term in that it does never allow comparators
QString s_fieldNamePattern( "([^\\s\"':=<>]+|(?:([\"'])[^\"':=<>]+\\%1))" );
QString s_plainTermPattern( "([^\\s\"':=<>]+|(?:([\"'])[^\"']+\\%1))" );
QString s_inExclusionPattern( "([\\+\\-]?)" );
QString s_uriPattern( "<([^<>]+)>" );
QString s_comparatorPattern( "(:|\\<=|\\>=|=|\\<|\\>)" );
// match a simple search text
// captures: 1 - The optional + or - sign (may be empty)
// 2 - the search text (including optional paranthesis)
QRegExp s_plainTermRx( s_inExclusionPattern + s_plainTermPattern.arg( 3 ) );
// match a field search term: fieldname + relation (:, =, etc) + search text with optional paranthesis
// captures: 1 - The optional + or - sign (may be empty)
// 2 - fieldname
// 3 - relation
// 4 - search text (including optional paranthesis)
QRegExp s_fieldRx( s_inExclusionPattern + s_fieldNamePattern.arg( 3 ) + s_comparatorPattern + s_plainTermPattern.arg( 6 ) );
// match a property URI search term: property URI + relation (:, =, etc) + search text with optional paranthesis
// captures: 1 - The optional + or - sign (may be empty)
// 2 - property URI
// 3 - relation
// 4 - search text (including optional paranthesis)
QRegExp s_propertyRx( s_inExclusionPattern + s_uriPattern + s_comparatorPattern + s_plainTermPattern.arg( 5 ) );
// match a property URI search term: property URI + relation (:, =, etc) + resource URI
// captures: 1 - The optional + or - sign (may be empty)
// 2 - property URI
// 3 - resource URI
QRegExp s_resourceRx( s_inExclusionPattern + s_uriPattern + "(?::|=)" + s_uriPattern );
QRegExp s_fieldFieldRx( s_inExclusionPattern + s_fieldNamePattern.arg( 3 ) + s_comparatorPattern + "\\(" + s_fieldNamePattern.arg( 6 ) + s_comparatorPattern + s_plainTermPattern.arg( 9 ) + "\\)" );
Nepomuk::Search::Term::Comparator fieldTypeRelationFromString( const QString& s ) {
if ( s == "=" ) {
return Nepomuk::Search::Term::Equal;
}
else if ( s == ":" ) {
return Nepomuk::Search::Term::Contains;
}
else if ( s == ">" ) {
return Nepomuk::Search::Term::Greater;
}
else if ( s == "<" ) {
return Nepomuk::Search::Term::Smaller;
}
else if ( s == ">=" ) {
return Nepomuk::Search::Term::GreaterOrEqual;
}
else if ( s == "<=" ) {
return Nepomuk::Search::Term::SmallerOrEqual;
}
else {
kDebug() << "FIXME: Unsupported relation:" << s;
return Nepomuk::Search::Term::Equal;
}
}
QString stripQuotes( const QString& s ) {
if ( s[0] == '\'' ||
s[0] == '\"' ) {
return s.mid( 1 ).left( s.length()-2 );
}
else {
return s;
}
}
QUrl tryToBeIntelligentAboutParsingUrl( const QString& s ) {
if ( s.contains( '%' ) && !s.contains( '/' ) ) {
return QUrl::fromEncoded( s.toAscii() );
}
else {
return QUrl( s );
}
}
Soprano::LiteralValue createLiteral( const QString& s ) {
bool b = false;
int i = s.toInt( &b );
if ( b )
return Soprano::LiteralValue( i );
double d = s.toDouble( &b );
if ( b )
return Soprano::LiteralValue( d );
return s;
}
}
Nepomuk::Search::Query Nepomuk::Search::QueryParser::parseQuery( const QString& query )
{
QueryParser parser;
return parser.parse( query );
}
class Nepomuk::Search::QueryParser::Private
{
public:
QSet<QString> andKeywords;
QSet<QString> orKeywords;
};
Nepomuk::Search::QueryParser::QueryParser()
: d( new Private() )
{
QString andListStr = i18nc( "Boolean AND keyword in desktop search strings. You can add several variants separated by spaces, e.g. retain the English one alongside the translation; keywords are not case sensitive. Make sure there is no conflict with the OR keyword.", "and" );
foreach ( const QString &andKeyword, andListStr.split( " ", QString::SkipEmptyParts ) ) {
d->andKeywords.insert( andKeyword.toLower() );
}
QString orListStr = i18nc( "Boolean OR keyword in desktop search strings. You can add several variants separated by spaces, e.g. retain the English one alongside the translation; keywords are not case sensitive. Make sure there is no conflict with the AND keyword.", "or" );
foreach ( const QString &orKeyword, orListStr.split( " ", QString::SkipEmptyParts ) ) {
d->orKeywords.insert( orKeyword.toLower() );
}
}
Nepomuk::Search::QueryParser::~QueryParser()
{
delete d;
}
Nepomuk::Search::Query Nepomuk::Search::QueryParser::parse( const QString& query )
{
// TODO: a "real" parser which can handle all of the Xesam user language
// This one for example does not handle nesting at all.
QList<Term> terms;
bool inOrBlock = false;
bool inAndBlock = false;
int pos = 0;
while ( pos < query.length() ) {
// skip whitespace
while ( pos < query.length() && query[pos].isSpace() ) {
kDebug() << "Skipping space at" << pos;
++pos;
}
Term term;
if ( pos < query.length() ) {
if ( s_resourceRx.indexIn( query, pos ) == pos ) {
// FIXME: honour the +-
kDebug() << "matched resource term at" << pos << s_resourceRx.cap( 0 );
term = Term( tryToBeIntelligentAboutParsingUrl( s_resourceRx.cap( 2 ) ),
tryToBeIntelligentAboutParsingUrl( s_resourceRx.cap( 3 ) ) );
pos += s_resourceRx.matchedLength();
}
else if ( s_propertyRx.indexIn( query, pos ) == pos ) {
// FIXME: honour the +-
kDebug() << "matched property term at" << pos << s_propertyRx.cap( 0 );
term.setProperty( tryToBeIntelligentAboutParsingUrl( s_propertyRx.cap( 2 ) ) );
term.addSubTerm( Term( createLiteral( stripQuotes( s_propertyRx.cap( 4 ) ) ) ) );
QString comparator = s_propertyRx.cap( 3 );
term.setType( Term::ComparisonTerm );
term.setComparator( fieldTypeRelationFromString( comparator ) );
pos += s_propertyRx.matchedLength();
}
else if ( s_fieldFieldRx.indexIn( query, pos ) == pos ) {
kDebug() << "matched field field term at" << pos
<< s_fieldFieldRx.cap( 0 )
<< s_fieldFieldRx.cap( 2 )
<< s_fieldFieldRx.cap( 4 )
<< s_fieldFieldRx.cap( 5 )
<< s_fieldFieldRx.cap( 7 )
<< s_fieldFieldRx.cap( 8 );
term.setField( stripQuotes( s_fieldFieldRx.cap( 2 ) ) );
QString comparator = s_fieldFieldRx.cap( 4 );
term.setType( Term::ComparisonTerm );
term.setComparator( fieldTypeRelationFromString( comparator ) );
term.addSubTerm( Term( stripQuotes( s_fieldFieldRx.cap( 5 ) ), s_fieldFieldRx.cap( 8 ), fieldTypeRelationFromString( s_fieldFieldRx.cap( 7 ) ) ) );
pos += s_fieldFieldRx.matchedLength();
}
else if ( s_fieldRx.indexIn( query, pos ) == pos ) {
// FIXME: honour the +-
kDebug() << "matched field term at" << pos << s_fieldRx.cap( 0 ) << s_fieldRx.cap( 2 ) << s_fieldRx.cap( 4 ) << s_fieldRx.cap( 5 );
term.setField( stripQuotes( s_fieldRx.cap( 2 ) ) );
term.addSubTerm( Term( createLiteral( stripQuotes( s_fieldRx.cap( 5 ) ) ) ) );
QString comparator = s_fieldRx.cap( 4 );
term.setType( Term::ComparisonTerm );
term.setComparator( fieldTypeRelationFromString( comparator ) );
pos += s_fieldRx.matchedLength();
}
else if ( s_plainTermRx.indexIn( query, pos ) == pos ) {
// FIXME: honour the +-
QString value = stripQuotes( s_plainTermRx.cap( 2 ) );
if ( d->orKeywords.contains( value.toLower() ) ) {
inOrBlock = true;
}
else if ( d->andKeywords.contains( value.toLower() ) ) {
inAndBlock = true;
}
else {
kDebug() << "matched literal at" << pos << value;
term = Term( Soprano::LiteralValue( value ) );
}
pos += s_plainTermRx.matchedLength();
}
else {
kDebug() << "Invalid query at" << pos << query;
return Term();
}
if ( term.isValid() ) {
if ( inOrBlock && !terms.isEmpty() ) {
Term orTerm;
orTerm.setType( Term::OrTerm );
orTerm.addSubTerm( terms.takeLast() );
orTerm.addSubTerm( term );
terms.append( orTerm );
}
else if ( inAndBlock && !terms.isEmpty() ) {
Term andTerm;
andTerm.setType( Term::AndTerm );
andTerm.addSubTerm( terms.takeLast() );
andTerm.addSubTerm( term );
terms.append( andTerm );
}
else {
terms.append( term );
}
}
}
}
if ( terms.count() == 1 ) {
return terms[0];
}
else if ( terms.count() > 0 ) {
Term t;
t.setType( Term::AndTerm );
t.setSubTerms( terms );
return t;
}
else {
return Term();
}
}

View File

@ -0,0 +1,49 @@
/*
This file is part of the Nepomuk KDE project.
Copyright (C) 2007 Sebastian Trueg <trueg@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2 as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _NEPOMUK_SEARCH_QUERY_PARSER_H_
#define _NEPOMUK_SEARCH_QUERY_PARSER_H_
#include "query.h"
#include <QtCore/QString>
#include "nepomukquery_export.h"
namespace Nepomuk {
namespace Search {
class NEPOMUKQUERY_EXPORT QueryParser
{
public:
QueryParser();
~QueryParser();
Query parse( const QString& query );
static Query parseQuery( const QString& query );
private:
class Private;
Private* const d;
};
}
}
#endif

Some files were not shown because too many files have changed in this diff Show More