diff options
Diffstat (limited to 'arts/knotify')
-rw-r--r-- | arts/knotify/Makefile.am | 27 | ||||
-rw-r--r-- | arts/knotify/README | 33 | ||||
-rw-r--r-- | arts/knotify/knotify.cpp | 800 | ||||
-rw-r--r-- | arts/knotify/knotify.desktop | 119 | ||||
-rw-r--r-- | arts/knotify/knotify.h | 111 | ||||
-rw-r--r-- | arts/knotify/knotifytest.cpp | 19 |
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 ); + } +} |