summaryrefslogtreecommitdiffstats
path: root/arts/knotify
diff options
context:
space:
mode:
Diffstat (limited to 'arts/knotify')
-rw-r--r--arts/knotify/Makefile.am27
-rw-r--r--arts/knotify/README33
-rw-r--r--arts/knotify/knotify.cpp800
-rw-r--r--arts/knotify/knotify.desktop119
-rw-r--r--arts/knotify/knotify.h111
-rw-r--r--arts/knotify/knotifytest.cpp19
6 files changed, 1109 insertions, 0 deletions
diff --git a/arts/knotify/Makefile.am b/arts/knotify/Makefile.am
new file mode 100644
index 000000000..bf743ce45
--- /dev/null
+++ b/arts/knotify/Makefile.am
@@ -0,0 +1,27 @@
+
+INCLUDES= -I$(top_srcdir)/arts/kde -I$(includedir)/arts $(all_includes)
+
+####### Files
+
+kde_module_LTLIBRARIES = knotify.la
+
+knotify_la_SOURCES = knotify.cpp knotify.skel
+if include_ARTS
+knotify_la_LIBADD = -lsoundserver_idl -lqtmcop $(LIB_KDEUI) $(top_builddir)/arts/kde/libartskde.la
+endif
+knotify_la_LDFLAGS = $(all_libraries) -module -avoid-version
+knotify_la_METASOURCES = AUTO
+
+check_PROGRAMS = knotifytest
+knotifytest_SOURCES = knotifytest.cpp
+knotifytest_LDADD = $(LIB_KDECORE)
+knotifytest_LDFLAGS = $(all_libraries) $(KDE_RPATH)
+
+noinst_HEADERS = knotify.h
+
+kdelnkdir = $(kde_servicesdir)
+kdelnk_DATA = knotify.desktop
+
+messages:
+ $(XGETTEXT) knotify.cpp -o $(podir)/knotify.pot
+
diff --git a/arts/knotify/README b/arts/knotify/README
new file mode 100644
index 000000000..3257cdc33
--- /dev/null
+++ b/arts/knotify/README
@@ -0,0 +1,33 @@
+About KNotify
+
+Historic overview. From KDE-1 to KDE-2:
+---------------------------------------
+KDE-1 ships with a notification system for window manager events - a sound
+could be played for example on startup or when a window closes. This was a
+very limited notification system.
+
+KNotify in contrast is a very flexible notification system. It can easily be
+accessed by any application, and notifications can have several
+presentations: Sound is still supported, but you can as well display a
+message box, write a text to a log file or log window.
+
+The notification presentation will be user configurable. Some people don't
+like message boxes popping up at unexpected times, so they prefer sounds
+instead. Deaf people on the otehr hand will not be happy about desktop
+sounds.
+
+
+
+Usage:
+------
+1. Compile
+2. Start knotify
+3. Test it with knotifyclient.
+
+Ideas:
+------
+It might be useful to modify message presentation from time to time. For
+example, while the screen is locked, it is very likely that the user is not
+present. Thus, he will not be able to hear a "You have mail" sound (or to
+see a talk request). Re-routing this to a log window sounds like a very good
+idea.
diff --git a/arts/knotify/knotify.cpp b/arts/knotify/knotify.cpp
new file mode 100644
index 000000000..794ef3dcd
--- /dev/null
+++ b/arts/knotify/knotify.cpp
@@ -0,0 +1,800 @@
+/*
+ Copyright (c) 1997 Christian Esken (esken@kde.org)
+ 2000 Charles Samuels (charles@kde.org)
+ 2000 Stefan Schimanski (1Stein@gmx.de)
+ 2000 Matthias Ettrich (ettrich@kde.org)
+ 2000 Waldo Bastian <bastian@kde.org>
+ 2000-2003 Carsten Pfeiffer <pfeiffer@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, 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.
+*/
+
+// C headers
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <config.h>
+#ifndef WITHOUT_ARTS
+// aRts headers
+#include <connect.h>
+#include <dispatcher.h>
+#include <flowsystem.h>
+#include <qiomanager.h>
+#include <soundserver.h>
+#endif
+
+// QT headers
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+
+// KDE headers
+#include <dcopclient.h>
+#include <kaboutdata.h>
+#ifndef WITHOUT_ARTS
+#include <kartsdispatcher.h>
+#include <kartsserver.h>
+#endif
+#include <kcmdlineargs.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kpassivepopup.h>
+#include <kiconloader.h>
+#include <kmacroexpander.h>
+#ifndef WITHOUT_ARTS
+#include <kplayobjectfactory.h>
+#include <kaudiomanagerplay.h>
+#endif
+#include <kprocess.h>
+#include <kstandarddirs.h>
+#include <kuniqueapplication.h>
+#include <kwin.h>
+
+#include "knotify.h"
+#include "knotify.moc"
+
+class KNotifyPrivate
+{
+public:
+ KConfig* globalEvents;
+ KConfig* globalConfig;
+ QMap<QString, KConfig*> events;
+ QMap<QString, KConfig*> configs;
+ QString externalPlayer;
+ KProcess *externalPlayerProc;
+
+#ifndef WITHOUT_ARTS
+ QPtrList<KDE::PlayObject> playObjects;
+ QMap<KDE::PlayObject*,int> playObjectEventMap;
+ KAudioManagerPlay *audioManager;
+#endif
+ int externalPlayerEventId;
+
+ bool useExternal;
+ bool useArts;
+ int volume;
+ QTimer *playTimer;
+ bool inStartup;
+ QString startupEvents;
+};
+
+// Yes, it's ugly to put this here, but this facilitates the cautious startup
+// procedure.
+#ifndef WITHOUT_ARTS
+KArtsServer *soundServer = 0;
+#endif
+
+extern "C"{
+
+KDE_EXPORT int kdemain(int argc, char **argv)
+{
+ KAboutData aboutdata("knotify", I18N_NOOP("KNotify"),
+ "3.0", I18N_NOOP("KDE Notification Server"),
+ KAboutData::License_GPL, "(C) 1997-2003, KDE Developers");
+ aboutdata.addAuthor("Carsten Pfeiffer",I18N_NOOP("Current Maintainer"),"pfeiffer@kde.org");
+ aboutdata.addAuthor("Christian Esken",0,"esken@kde.org");
+ aboutdata.addAuthor("Stefan Westerfeld",I18N_NOOP("Sound support"),"stefan@space.twc.de");
+ aboutdata.addAuthor("Charles Samuels",I18N_NOOP("Previous Maintainer"),"charles@kde.org");
+
+ KCmdLineArgs::init( argc, argv, &aboutdata );
+ KUniqueApplication::addCmdLineOptions();
+
+
+ // initialize application
+ if ( !KUniqueApplication::start() ) {
+ kdDebug() << "Running knotify found" << endl;
+ return 0;
+ }
+
+ KUniqueApplication app;
+ app.disableSessionManagement();
+
+ // KNotify is started on KDE startup and on demand (using
+ // KNotifClient::startDaemon()) whenever a KNotify event occurs. Especially
+ // KWin may fire many events (e.g. when a window pops up). When we have
+ // problems with aRts or the installation, we might get an infinite loop
+ // of knotify crashing, popping up the crashhandler window and kwin firing
+ // another event, starting knotify again...
+ // We try to prevent this by tracking our startup and offer options to
+ // abort this.
+
+#ifndef WITHOUT_ARTS
+ KConfigGroup config( KGlobal::config(), "StartProgress" );
+ KConfig artsKCMConfig( "kcmartsrc" );
+ artsKCMConfig.setGroup( "Arts" );
+ bool useArts = artsKCMConfig.readBoolEntry( "StartServer", true );
+ if (useArts)
+ useArts = config.readBoolEntry( "Use Arts", useArts );
+ bool ok = config.readBoolEntry( "Arts Init", true );
+
+ if ( useArts && !ok )
+ {
+ if ( KMessageBox::questionYesNo(
+ 0L,
+ i18n("During the previous startup, KNotify crashed while creating "
+ "Arts::Dispatcher. Do you want to try again or disable "
+ "aRts sound output?\n\n"
+ "If you choose to disable aRts output now, you can re-enable "
+ "it later or select an alternate sound player "
+ "in the System Notifications control panel."),
+ i18n("KNotify Problem"),
+ i18n("&Try Again"),
+ i18n("D&isable aRts Output"),
+ "KNotifyStartProgress",
+ 0 /* don't call KNotify :) */
+ )
+ == KMessageBox::No )
+ {
+ useArts = false;
+ }
+ }
+
+ // when ArtsDispatcher crashes, we know it the next start.
+ config.writeEntry( "Arts Init", false );
+ config.writeEntry( "Use Arts", useArts );
+ config.sync();
+
+ KArtsDispatcher *dispatcher = 0;
+ if ( useArts )
+ {
+ dispatcher = new KArtsDispatcher;
+ soundServer = new KArtsServer;
+ }
+
+ // ok, seemed to work.
+ config.writeEntry("Arts Init", useArts );
+ config.sync();
+
+ ok = config.readBoolEntry( "KNotify Init", true );
+ if ( useArts && !ok )
+ {
+ if ( KMessageBox::questionYesNo(
+ 0L,
+ i18n("During the previous startup, KNotify crashed while instantiating "
+ "KNotify. Do you want to try again or disable "
+ "aRts sound output?\n\n"
+ "If you choose to disable aRts output now, you can re-enable "
+ "it later or select an alternate sound player "
+ "in the System Notifications control panel."),
+ i18n("KNotify Problem"),
+ i18n("&Try Again"),
+ i18n("D&isable aRts Output"),
+ "KNotifyStartProgress",
+ 0 /* don't call KNotify :) */
+ )
+ == KMessageBox::No )
+ {
+ useArts = false;
+ delete soundServer;
+ soundServer = 0L;
+ delete dispatcher;
+ dispatcher = 0L;
+ }
+ }
+
+ // when KNotify instantiation crashes, we know it the next start.
+ config.writeEntry( "KNotify Init", false );
+ config.writeEntry( "Use Arts", useArts );
+ config.sync();
+
+ // start notify service
+ KNotify *notify = new KNotify( useArts );
+
+ config.writeEntry( "KNotify Init", true );
+ config.sync();
+
+#else
+
+ // start notify service, without aRts
+ KNotify *notify = new KNotify( false );
+
+#endif
+
+ app.dcopClient()->setDefaultObject( "Notify" );
+ app.dcopClient()->setDaemonMode( true );
+ // kdDebug() << "knotify starting" << endl;
+
+ int ret = app.exec();
+ delete notify;
+#ifndef WITHOUT_ARTS
+ delete soundServer;
+ delete dispatcher;
+#endif
+ return ret;
+}
+}// end extern "C"
+
+KNotify::KNotify( bool useArts )
+ : QObject(), DCOPObject("Notify")
+{
+ d = new KNotifyPrivate;
+ d->globalEvents = new KConfig("knotify/eventsrc", true, false, "data");
+ d->globalConfig = new KConfig("knotify.eventsrc", true, false);
+ d->externalPlayerProc = 0;
+ d->useArts = useArts;
+ d->inStartup = true;
+#ifndef WITHOUT_ARTS
+ d->playObjects.setAutoDelete(true);
+ d->audioManager = 0;
+ if( useArts )
+ {
+ connect( soundServer, SIGNAL( restartedServer() ), this, SLOT( restartedArtsd() ) );
+ restartedArtsd(); //started allready need to initialize d->audioManager
+ }
+#endif
+
+ d->volume = 100;
+
+ d->playTimer = 0;
+
+ loadConfig();
+}
+
+KNotify::~KNotify()
+{
+ reconfigure();
+
+#ifndef WITHOUT_ARTS
+ d->playObjects.clear();
+
+ delete d->globalEvents;
+ delete d->globalConfig;
+ delete d->externalPlayerProc;
+ delete d->audioManager;
+#endif
+ delete d;
+}
+
+
+void KNotify::loadConfig() {
+ // load external player settings
+ KConfig *kc = KGlobal::config();
+ kc->setGroup("Misc");
+ d->useExternal = kc->readBoolEntry( "Use external player", false );
+ d->externalPlayer = kc->readPathEntry("External player");
+
+ // try to locate a suitable player if none is configured
+ if ( d->externalPlayer.isEmpty() ) {
+ QStringList players;
+ players << "wavplay" << "aplay" << "auplay";
+ QStringList::Iterator it = players.begin();
+ while ( d->externalPlayer.isEmpty() && it != players.end() ) {
+ d->externalPlayer = KStandardDirs::findExe( *it );
+ ++it;
+ }
+ }
+
+ // load default volume
+ d->volume = kc->readNumEntry( "Volume", 100 );
+}
+
+
+void KNotify::reconfigure()
+{
+ kapp->config()->reparseConfiguration();
+ loadConfig();
+
+ // clear loaded config files
+ d->globalConfig->reparseConfiguration();
+ for ( QMapIterator<QString,KConfig*> it = d->configs.begin(); it != d->configs.end(); ++it )
+ delete it.data();
+ d->configs.clear();
+}
+
+
+void KNotify::notify(const QString &event, const QString &fromApp,
+ const QString &text, QString sound, QString file,
+ int present, int level)
+{
+ notify( event, fromApp, text, sound, file, present, level, 0, 1 );
+}
+
+void KNotify::notify(const QString &event, const QString &fromApp,
+ const QString &text, QString sound, QString file,
+ int present, int level, int winId)
+{
+ notify( event, fromApp, text, sound, file, present, level, winId, 1 );
+}
+
+void KNotify::notify(const QString &event, const QString &fromApp,
+ const QString &text, QString sound, QString file,
+ int present, int level, int winId, int eventId )
+{
+ // kdDebug() << "event=" << event << " fromApp=" << fromApp << " text=" << text << " sound=" << sound <<
+ // " file=" << file << " present=" << present << " level=" << level << " winId=" << winId << " eventId=" << eventId << endl;
+ if( d->inStartup ) {
+ d->startupEvents += "(" + event + ":" + fromApp + ")";
+ }
+
+ QString commandline;
+ KConfig *eventsFile = NULL;
+ KConfig *configFile = NULL;
+
+ // check for valid events
+ if ( !event.isEmpty() ) {
+
+ // get config file
+ if ( d->events.contains( fromApp ) ) {
+ eventsFile = d->events[fromApp];
+ } else {
+ eventsFile=new KConfig(locate("data", fromApp+"/eventsrc"),true,false);
+ d->events.insert( fromApp, eventsFile );
+ }
+ if ( d->configs.contains( fromApp) ) {
+ configFile = d->configs[fromApp];
+ } else {
+ configFile=new KConfig(fromApp+".eventsrc",true,false);
+ d->configs.insert( fromApp, configFile );
+ }
+
+ if ( !eventsFile->hasGroup( event ) && isGlobal(event) )
+ {
+ eventsFile = d->globalEvents;
+ configFile = d->globalConfig;
+ }
+
+ eventsFile->setGroup( event );
+ configFile->setGroup( event );
+
+ // get event presentation
+ if ( present==-1 )
+ present = configFile->readNumEntry( "presentation", -1 );
+ if ( present==-1 )
+ present = eventsFile->readNumEntry( "default_presentation", 0 );
+
+ // get sound file name
+ if( present & KNotifyClient::Sound ) {
+ QString theSound = configFile->readPathEntry( "soundfile" );
+ if ( theSound.isEmpty() )
+ theSound = eventsFile->readPathEntry( "default_sound" );
+ if ( !theSound.isEmpty() )
+ sound = theSound;
+ }
+
+ // get log file name
+ if( present & KNotifyClient::Logfile ) {
+ QString theFile = configFile->readPathEntry( "logfile" );
+ if ( theFile.isEmpty() )
+ theFile = eventsFile->readPathEntry( "default_logfile" );
+ if ( !theFile.isEmpty() )
+ file = theFile;
+ }
+
+ // get default event level
+ if( present & KNotifyClient::Messagebox )
+ level = eventsFile->readNumEntry( "level", 0 );
+
+ // get command line
+ if (present & KNotifyClient::Execute ) {
+ commandline = configFile->readPathEntry( "commandline" );
+ if ( commandline.isEmpty() )
+ commandline = eventsFile->readPathEntry( "default_commandline" );
+ }
+ }
+
+ // emit event
+ if ( present & KNotifyClient::Sound ) // && QFile(sound).isReadable()
+ notifyBySound( sound, fromApp, eventId );
+
+ if ( present & KNotifyClient::Execute )
+ notifyByExecute( commandline, event, fromApp, text, winId, eventId );
+
+ if ( present & KNotifyClient::Logfile ) // && QFile(file).isWritable()
+ notifyByLogfile( text, file );
+
+ if ( present & KNotifyClient::Stderr )
+ notifyByStderr( text );
+
+ if ( present & KNotifyClient::Taskbar )
+ notifyByTaskbar( checkWinId( fromApp, winId ));
+
+ if ( present & KNotifyClient::PassivePopup )
+ notifyByPassivePopup( text, fromApp, eventsFile, checkWinId( fromApp, winId ));
+ else if ( present & KNotifyClient::Messagebox )
+ notifyByMessagebox( text, level, checkWinId( fromApp, winId ));
+
+ QByteArray qbd;
+ QDataStream ds(qbd, IO_WriteOnly);
+ ds << event << fromApp << text << sound << file << present << level
+ << winId << eventId;
+ emitDCOPSignal("notifySignal(QString,QString,QString,QString,QString,int,int,int,int)", qbd);
+
+}
+
+
+bool KNotify::notifyBySound( const QString &sound, const QString &appname, int eventId )
+{
+ if (sound.isEmpty()) {
+ soundFinished( eventId, NoSoundFile );
+ return false;
+ }
+
+ bool external = d->useExternal && !d->externalPlayer.isEmpty();
+ // get file name
+ QString soundFile(sound);
+ if ( QFileInfo(sound).isRelative() )
+ {
+ QString search = QString("%1/sounds/%2").arg(appname).arg(sound);
+ soundFile = KGlobal::instance()->dirs()->findResource("data", search);
+ if ( soundFile.isEmpty() )
+ soundFile = locate( "sound", sound );
+ }
+ if ( soundFile.isEmpty() || isPlaying( soundFile ) )
+ {
+ soundFinished( eventId, soundFile.isEmpty() ? NoSoundFile : FileAlreadyPlaying );
+ return false;
+ }
+
+
+ // kdDebug() << "KNotify::notifyBySound - trying to play file " << soundFile << endl;
+
+ if (!external) {
+ //If we disabled using aRts, just return,
+ //(If we don't, we'll blow up accessing the null soundServer)
+ if (!d->useArts)
+ {
+ soundFinished( eventId, NoSoundSupport );
+ return false;
+ }
+
+#ifndef WITHOUT_ARTS
+ // play sound finally
+ while( d->playObjects.count()>5 )
+ abortFirstPlayObject();
+
+ KDE::PlayObjectFactory factory(soundServer->server());
+ if( d->audioManager )
+ factory.setAudioManagerPlay( d->audioManager );
+ KURL soundURL;
+ soundURL.setPath(soundFile);
+ KDE::PlayObject *playObject = factory.createPlayObject(soundURL, false);
+
+ if (playObject->isNull())
+ {
+ soundFinished( eventId, NoSoundSupport );
+ delete playObject;
+ return false;
+ }
+
+ if ( d->volume != 100 )
+ {
+ // It works to access the playObject immediately because we don't allow
+ // non-file URLs for sounds.
+ Arts::StereoVolumeControl volumeControl = Arts::DynamicCast(soundServer->server().createObject("Arts::StereoVolumeControl"));
+ Arts::PlayObject player = playObject->object();
+ Arts::Synth_AMAN_PLAY ap = d->audioManager->amanPlay();
+ if( ! volumeControl.isNull() && ! player.isNull() && ! ap.isNull() )
+ {
+ volumeControl.scaleFactor( d->volume/100.0 );
+
+ ap.stop();
+ Arts::disconnect( player, "left", ap, "left" );
+ Arts::disconnect( player, "right", ap, "right" );
+
+ ap.start();
+ volumeControl.start();
+
+ Arts::connect(player,"left",volumeControl,"inleft");
+ Arts::connect(player,"right",volumeControl,"inright");
+
+ Arts::connect(volumeControl,"outleft",ap,"left");
+ Arts::connect(volumeControl,"outright",ap,"right");
+
+ player._addChild( volumeControl, "volume" );
+ }
+ }
+
+ playObject->play();
+ d->playObjects.append( playObject );
+ d->playObjectEventMap.insert( playObject, eventId );
+
+ if ( !d->playTimer )
+ {
+ d->playTimer = new QTimer( this );
+ connect( d->playTimer, SIGNAL( timeout() ), SLOT( playTimeout() ) );
+ }
+ if ( !d->playTimer->isActive() )
+ d->playTimer->start( 1000 );
+#endif
+ return true;
+
+ } else if(!d->externalPlayer.isEmpty()) {
+ // use an external player to play the sound
+ KProcess *proc = d->externalPlayerProc;
+ if (!proc)
+ {
+ proc = d->externalPlayerProc = new KProcess;
+ connect( proc, SIGNAL( processExited( KProcess * )),
+ SLOT( slotPlayerProcessExited( KProcess * )));
+ }
+ if (proc->isRunning())
+ {
+ soundFinished( eventId, PlayerBusy );
+ return false; // Skip
+ }
+ proc->clearArguments();
+ (*proc) << d->externalPlayer << QFile::encodeName( soundFile );
+ d->externalPlayerEventId = eventId;
+ proc->start(KProcess::NotifyOnExit);
+ return true;
+ }
+
+ soundFinished( eventId, Unknown );
+ return false;
+}
+
+bool KNotify::notifyByMessagebox(const QString &text, int level, WId winId)
+{
+ // ignore empty messages
+ if ( text.isEmpty() )
+ return false;
+
+ // display message box for specified event level
+ switch( level ) {
+ default:
+ case KNotifyClient::Notification:
+ KMessageBox::informationWId( winId, text, i18n("Notification"), 0, false );
+ break;
+ case KNotifyClient::Warning:
+ KMessageBox::sorryWId( winId, text, i18n("Warning"), false );
+ break;
+ case KNotifyClient::Error:
+ KMessageBox::errorWId( winId, text, i18n("Error"), false );
+ break;
+ case KNotifyClient::Catastrophe:
+ KMessageBox::errorWId( winId, text, i18n("Catastrophe!"), false );
+ break;
+ }
+
+ return true;
+}
+
+bool KNotify::notifyByPassivePopup( const QString &text,
+ const QString &appName,
+ KConfig* eventsFile,
+ WId senderWinId )
+{
+ KIconLoader iconLoader( appName );
+ if ( eventsFile != NULL ) {
+ KConfigGroup config( eventsFile, "!Global!" );
+ QString iconName = config.readEntry( "IconName", appName );
+ QPixmap icon = iconLoader.loadIcon( iconName, KIcon::Small );
+ QString title = config.readEntry( "Comment", appName );
+ KPassivePopup::message(title, text, icon, senderWinId);
+ } else
+ kdError() << "No events for app " << appName << "defined!" <<endl;
+
+ return true;
+}
+
+bool KNotify::notifyByExecute(const QString &command, const QString& event,
+ const QString& fromApp, const QString& text,
+ int winId, int eventId) {
+ if (!command.isEmpty()) {
+ // kdDebug() << "executing command '" << command << "'" << endl;
+ QMap<QChar,QString> subst;
+ subst.insert( 'e', event );
+ subst.insert( 'a', fromApp );
+ subst.insert( 's', text );
+ subst.insert( 'w', QString::number( winId ));
+ subst.insert( 'i', QString::number( eventId ));
+ QString execLine = KMacroExpander::expandMacrosShellQuote( command, subst );
+ if ( execLine.isEmpty() )
+ execLine = command; // fallback
+
+ KProcess p;
+ p.setUseShell(true);
+ p << execLine;
+ p.start(KProcess::DontCare);
+ return true;
+ }
+ return false;
+}
+
+
+bool KNotify::notifyByLogfile(const QString &text, const QString &file)
+{
+ // ignore empty messages
+ if ( text.isEmpty() )
+ return true;
+
+ // open file in append mode
+ QFile logFile(file);
+ if ( !logFile.open(IO_WriteOnly | IO_Append) )
+ return false;
+
+ // append msg
+ QTextStream strm( &logFile );
+ strm << "- KNotify " << QDateTime::currentDateTime().toString() << ": ";
+ strm << text << endl;
+
+ // close file
+ logFile.close();
+ return true;
+}
+
+bool KNotify::notifyByStderr(const QString &text)
+{
+ // ignore empty messages
+ if ( text.isEmpty() )
+ return true;
+
+ // open stderr for output
+ QTextStream strm( stderr, IO_WriteOnly );
+
+ // output msg
+ strm << "KNotify " << QDateTime::currentDateTime().toString() << ": ";
+ strm << text << endl;
+
+ return true;
+}
+
+bool KNotify::notifyByTaskbar( WId win )
+{
+ if( win == 0 )
+ return false;
+ KWin::demandAttention( win );
+ return true;
+}
+
+bool KNotify::isGlobal(const QString &eventname)
+{
+ return d->globalEvents->hasGroup( eventname );
+}
+
+void KNotify::setVolume( int volume )
+{
+ if ( volume<0 ) volume=0;
+ if ( volume>=100 ) volume=100;
+ d->volume = volume;
+}
+
+void KNotify::playTimeout()
+{
+#ifndef WITHOUT_ARTS
+ for ( QPtrListIterator< KDE::PlayObject > it(d->playObjects); *it;)
+ {
+ QPtrListIterator< KDE::PlayObject > current = it;
+ ++it;
+ if ( (*current)->state() != Arts::posPlaying )
+ {
+ QMap<KDE::PlayObject*,int>::Iterator eit = d->playObjectEventMap.find( *current );
+ if ( eit != d->playObjectEventMap.end() )
+ {
+ soundFinished( *eit, PlayedOK );
+ d->playObjectEventMap.remove( eit );
+ }
+ d->playObjects.remove( current );
+ }
+ }
+ if ( !d->playObjects.count() )
+ d->playTimer->stop();
+#endif
+}
+
+bool KNotify::isPlaying( const QString& soundFile ) const
+{
+#ifndef WITHOUT_ARTS
+ for ( QPtrListIterator< KDE::PlayObject > it(d->playObjects); *it; ++it)
+ {
+ if ( (*it)->mediaName() == soundFile )
+ return true;
+ }
+#endif
+ return false;
+}
+
+void KNotify::slotPlayerProcessExited( KProcess *proc )
+{
+ soundFinished( d->externalPlayerEventId,
+ (proc->normalExit() && proc->exitStatus() == 0) ? PlayedOK : Unknown );
+}
+
+void KNotify::abortFirstPlayObject()
+{
+#ifndef WITHOUT_ARTS
+ QMap<KDE::PlayObject*,int>::Iterator it = d->playObjectEventMap.find( d->playObjects.getFirst() );
+ if ( it != d->playObjectEventMap.end() )
+ {
+ soundFinished( it.data(), Aborted );
+ d->playObjectEventMap.remove( it );
+ }
+ d->playObjects.removeFirst();
+#endif
+}
+
+void KNotify::soundFinished( int eventId, PlayingFinishedStatus reason )
+{
+ QByteArray data;
+ QDataStream stream( data, IO_WriteOnly );
+ stream << eventId << (int) reason;
+
+ DCOPClient::mainClient()->emitDCOPSignal( "KNotify", "playingFinished(int,int)", data );
+}
+
+WId KNotify::checkWinId( const QString &appName, WId senderWinId )
+{
+ if ( senderWinId == 0 )
+ {
+ QCString senderId = kapp->dcopClient()->senderId();
+ QCString compare = (appName + "-mainwindow").latin1();
+ int len = compare.length();
+ // kdDebug() << "notifyByPassivePopup: appName=" << appName << " sender=" << senderId << endl;
+
+ QCStringList objs = kapp->dcopClient()->remoteObjects( senderId );
+ for (QCStringList::ConstIterator it = objs.begin(); it != objs.end(); ++it ) {
+ QCString obj( *it );
+ if ( obj.left(len) == compare) {
+ // kdDebug( ) << "found " << obj << endl;
+ QCString replyType;
+ QByteArray data, replyData;
+
+ if ( kapp->dcopClient()->call(senderId, obj, "getWinID()", data, replyType, replyData) ) {
+ QDataStream answer(replyData, IO_ReadOnly);
+ if (replyType == "int") {
+ answer >> senderWinId;
+ // kdDebug() << "SUCCESS, found getWinID(): type='" << QString(replyType)
+ // << "' senderWinId=" << senderWinId << endl;
+ }
+ }
+ }
+ }
+ }
+ return senderWinId;
+}
+
+void KNotify::restartedArtsd()
+{
+#ifndef WITHOUT_ARTS
+ delete d->audioManager;
+ d->audioManager = new KAudioManagerPlay( soundServer );
+ d->audioManager->setTitle( i18n( "KDE System Notifications" ) );
+ d->audioManager->setAutoRestoreID( "KNotify Aman Play" );
+#endif
+}
+
+void KNotify::sessionReady()
+{
+ if( d->inStartup && !d->startupEvents.isEmpty())
+ kdDebug() << "There were knotify events while startup:" << d->startupEvents << endl;
+ d->inStartup = false;
+}
+
+// vim: sw=4 sts=4 ts=8 et
diff --git a/arts/knotify/knotify.desktop b/arts/knotify/knotify.desktop
new file mode 100644
index 000000000..da950fa8a
--- /dev/null
+++ b/arts/knotify/knotify.desktop
@@ -0,0 +1,119 @@
+[Desktop Entry]
+Type=Service
+Name=KNotify
+Name[af]=Knotify
+Name[ar]=برنامج الإبلاغ
+Name[bn]=কে-নোটিফাই
+Name[csb]=Òdkôzanié
+Name[cy]=KHysbyu
+Name[eo]=Katentigilo
+Name[fo]=KÁminning
+Name[hi]=के-नोटिफाई
+Name[ka]=სისტემური შეტყობინება
+Name[ko]=K알림이
+Name[mn]=Сонордуулга
+Name[ne]=केनोटिफाइ
+Name[nso]=KLemosa
+Name[pa]=ਕੇਟਿੱਪਣੀ
+Name[pl]=Powiadamianie
+Name[pt_BR]=Notificações
+Name[ru]=Системные сообщения
+Name[ss]=KNotify
+Name[sv]=Knotify
+Name[ta]=கேகுறிப்பெடு
+Name[te]=కెనోటిఫై
+Name[tg]=Хабарҳои системавӣ
+Name[th]=ระบบแจ้งเตือน - K
+Name[ven]=U divhadza ha K
+Name[zu]=I-KNotify
+Exec=knotify
+Comment=KDE Notification Daemon
+Comment[af]=Kde Inkennisstelling Bediener
+Comment[ar]=مراقب تنبيهات كيدي
+Comment[az]=KDE Bildiriş Demonu
+Comment[be]=Сервіс нагадванняў KDE
+Comment[bg]=Сървър за съобщения
+Comment[bn]=কে.ডি.ই নোটিশ সরবরাহকারী ডিমন
+Comment[br]=Diaoul Kemennadenn KDE
+Comment[bs]=KDE Sistemska obavještenja
+Comment[ca]=Dimoni de notificacions per al KDE
+Comment[cs]=Démon pro systémová hlášení prostředí KDE
+Comment[csb]=Demon òdkôzëwaniô KDE
+Comment[cy]=Daemon Hysbysu KDE
+Comment[da]=KDE Bekendtgørelsesdæmon
+Comment[de]=KDE-Benachrichtigungsprogramm
+Comment[el]=Δαίμονας ειδοποίησης του KDE
+Comment[eo]=KDE-Sistematentigo-demono
+Comment[es]=Demonio de notificación de KDE
+Comment[et]=KDE süsteemsete märguannete deemon
+Comment[eu]=KDEko jakinarazpenaren daemona
+Comment[fa]=شبح اخطار KDE
+Comment[fi]=KDE:n huomautuspalvelin
+Comment[fo]=Áminningarandi KDE's
+Comment[fr]=Démon de notifications de KDE
+Comment[fy]=KDE's systeemberjochtenprogramma
+Comment[ga]=Deamhan Fógartha KDE
+Comment[gl]=Demo de notificacións de KDE
+Comment[he]=תהליך הרקע הודעות של KDE
+Comment[hi]=केडीई सूचना ङेमन
+Comment[hr]=KDE demon obavještavanja
+Comment[hsb]=KDE-demon za zdźělenki
+Comment[hu]=KDE figyelmeztető szolgáltatás
+Comment[id]=Daemon pemberitahuan KDE
+Comment[is]=KDE tilkynningapúkinn
+Comment[it]=Demone avvisi di KDE
+Comment[ja]=KDE 通知デーモン
+Comment[ka]=KDE შეტყობინებების შიკრიკი
+Comment[kk]=KDE құлақтандыру қызметі
+Comment[ko]=KDE용 알림 서버
+Comment[lb]=KDE-Norichtendämon
+Comment[lt]=KDE pranešimų tarnyba
+Comment[lv]=KDE Apziņošanas Dēmons
+Comment[mi]=Kaikorero KDE
+Comment[mk]=KDE даемон за известувања
+Comment[mn]=KDE-Сонордуулга программ
+Comment[ms]=Daemon Pemberitahuan KDE
+Comment[mt]=Daemon tan-notifika KDE
+Comment[nb]=KDE Varslings-nisse
+Comment[nds]=KDE-Dämoon för Bescheden
+Comment[ne]=KDE सूचना डेइमन
+Comment[nl]=KDE's systeemnotificatieprogramma
+Comment[nn]=KDE-varselnisse
+Comment[nso]=Daemon ya Tsebiso ya KDE
+Comment[oc]=Dimoni de notificacion KDE
+Comment[pa]=KDE ਟਿੱਪਣੀ ਪੇਸ਼ਕਾਰ
+Comment[pl]=Demon powiadamiania KDE
+Comment[pt]=Servidor de mensagens do KDE
+Comment[pt_BR]=Serviço de notificação do KDE
+Comment[ro]=Demon de notificare KDE
+Comment[ru]=Системные сообщения KDE
+Comment[rw]=Dayimoni y'Imenyesha KDE
+Comment[se]=KDE dieđihanbálvá
+Comment[sk]=KDE Oznamovací démon
+Comment[sl]=Sistemska obvestila KDE
+Comment[sq]=KDE Demoni i Njoftimit
+Comment[sr]=KDE Демон за обавештавање
+Comment[sr@Latn]=KDE Demon za obaveštavanje
+Comment[ss]=I-daemon yekwatisa ku KDE
+Comment[sv]=KDE:s underrättelsedemon
+Comment[ta]=கேடிஇ அறிவிப்பு டேமன்
+Comment[te]=కెడిఈ ప్రకటనల సూత్రధారి
+Comment[tg]=Хабарҳои системавии KDE
+Comment[th]=เดมอนการแจ้งเตือนของ KDE
+Comment[tr]=KDE Bilgilendirme Programı
+Comment[tt]=KDE'nıñ Kisätü Xezmäte
+Comment[uk]=Демон сповіщення про нову пошту
+Comment[uz]=KDE xabarnomalar xizmati
+Comment[uz@cyrillic]=KDE хабарномалар хизмати
+Comment[ven]=Daemon yau divhadza ya KDE
+Comment[vi]=Trình nền thông báo của KDE
+Comment[wa]=Démon di notifiaedje di KDE
+Comment[xh]=Daemon Ulwaziso lwe KDE
+Comment[zh_CN]=KDE 通告守护进程
+Comment[zh_HK]=KDE 通知伺服程式
+Comment[zh_TW]=KDE 通知服務程式
+Comment[zu]=Isaziso se-Daemon ye-KDE
+Icon=knotify
+ServiceTypes=KNotify
+X-DCOP-ServiceType=Unique
+X-KDE-StartupNotify=false
diff --git a/arts/knotify/knotify.h b/arts/knotify/knotify.h
new file mode 100644
index 000000000..c98be2e97
--- /dev/null
+++ b/arts/knotify/knotify.h
@@ -0,0 +1,111 @@
+/*
+ Copyright (c) 1997 Christian Esken (esken@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, 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 KNOTIFY_H
+#define KNOTIFY_H
+
+#include <qobject.h>
+#include <knotifyclient.h>
+#include <dcopobject.h>
+
+class KNotifyPrivate;
+class KProcess;
+class KConfig;
+
+class KNotify : public QObject, public DCOPObject
+{
+Q_OBJECT
+K_DCOP
+
+public:
+ KNotify( bool useArts );
+ ~KNotify();
+
+ enum PlayingFinishedStatus
+ {
+ PlayedOK = 0, // success, all following mean failure
+ NoSoundFile,
+ FileAlreadyPlaying,
+ NoSoundSupport,
+ PlayerBusy,
+ Aborted,
+ Unknown = 5000
+ };
+
+protected:
+k_dcop:
+ // deprecated
+ void notify(const QString &event, const QString &fromApp,
+ const QString &text, QString sound, QString file,
+ int present, int level);
+
+ // deprecated
+ void notify(const QString &event, const QString &fromApp,
+ const QString &text, QString sound, QString file,
+ int present, int level, int winId);
+
+ void notify(const QString &event, const QString &fromApp,
+ const QString &text, QString sound, QString file,
+ int present, int level, int winId, int eventId);
+
+
+ void reconfigure();
+ void setVolume( int volume );
+ void sessionReady(); // from ksmserver
+
+private:
+ bool notifyBySound(const QString &sound, const QString &appname, int eventId);
+ bool notifyByMessagebox(const QString &text, int level, WId winId);
+ bool notifyByLogfile(const QString &text, const QString &file);
+ bool notifyByStderr(const QString &text);
+ bool notifyByPassivePopup(const QString &text, const QString &appName,
+ KConfig* eventsFile, WId winId );
+ bool notifyByExecute(const QString &command,
+ const QString& event,
+ const QString& fromApp,
+ const QString& text,
+ int winId,
+ int eventId );
+ bool notifyByTaskbar( WId winId );
+
+ bool isPlaying( const QString& soundFile ) const;
+
+ void soundFinished( int eventId, PlayingFinishedStatus reason );
+ void abortFirstPlayObject();
+
+ WId checkWinId( const QString& appName, WId senderWinId );
+
+ /**
+ * checks if eventname is a global event (exists in config/eventsrc)
+ **/
+ bool isGlobal(const QString &eventname);
+
+private slots:
+ void playTimeout();
+ void slotPlayerProcessExited( KProcess *proc );
+ void restartedArtsd();
+
+private:
+ KNotifyPrivate* d;
+ void loadConfig();
+};
+
+
+#endif
+
diff --git a/arts/knotify/knotifytest.cpp b/arts/knotify/knotifytest.cpp
new file mode 100644
index 000000000..9ca10cb9d
--- /dev/null
+++ b/arts/knotify/knotifytest.cpp
@@ -0,0 +1,19 @@
+#include <string>
+#include <stdio.h>
+#include <kapplication.h>
+#include <knotifyclient.h>
+
+int main(int argc, char **argv)
+{
+ KApplication app(argc, argv, "knotifytest");
+
+//
+ while (1) {
+ char inp = getc(stdin);
+
+ if ( inp=='q' || inp==27 ) break;
+ if ( inp=='1' ) KNotifyClient::userEvent( "Foo", KNotifyClient::Sound, KNotifyClient::Default, "KDE_Window_DeIconify.ogg" );
+ if ( inp=='2' ) KNotifyClient::userEvent( "MessageBox Event", KNotifyClient::Messagebox );
+ if ( inp=='3' ) KNotifyClient::userEvent( "Stderr Event", KNotifyClient::Stderr );
+ }
+}