26 #include <sys/types.h>
33 #include <dispatcher.h>
34 #include <flowsystem.h>
35 #include <qiomanager.h>
36 #include <soundserver.h>
41 #include <tqfileinfo.h>
42 #include <tqstringlist.h>
43 #include <tqtextstream.h>
46 #include <dcopclient.h>
47 #include <kaboutdata.h>
49 #include <kartsdispatcher.h>
50 #include <kartsserver.h>
52 #include <kcmdlineargs.h>
57 #include <kmessagebox.h>
58 #include <kpassivepopup.h>
59 #include <kiconloader.h>
60 #include <kmacroexpander.h>
62 #include <kplayobjectfactory.h>
63 #include <kaudiomanagerplay.h>
66 #include <kstandarddirs.h>
67 #include <kuniqueapplication.h>
71 #include "knotify.moc"
78 TQMap<TQString, KConfig*> events;
79 TQMap<TQString, KConfig*> configs;
80 TQString externalPlayer;
84 TQPtrList<KDE::PlayObject> playObjects;
85 TQMap<KDE::PlayObject*,int> playObjectEventMap;
88 int externalPlayerEventId;
95 TQString startupEvents;
106 KDE_EXPORT
int kdemain(
int argc,
char **argv)
109 "3.0",
I18N_NOOP(
"KDE Notification Server"),
110 KAboutData::License_GPL,
"(C) 1997-2003, KDE Developers");
111 aboutdata.addAuthor(
"Carsten Pfeiffer",
I18N_NOOP(
"Current Maintainer"),
"pfeiffer@kde.org");
112 aboutdata.addAuthor(
"Christian Esken",0,
"esken@kde.org");
113 aboutdata.addAuthor(
"Stefan Westerfeld",
I18N_NOOP(
"Sound support"),
"stefan@space.twc.de");
114 aboutdata.addAuthor(
"Charles Samuels",
I18N_NOOP(
"Previous Maintainer"),
"charles@kde.org");
140 KConfig artsKCMConfig(
"kcmartsrc" );
141 artsKCMConfig.setGroup(
"Arts" );
142 bool useArts = artsKCMConfig.readBoolEntry(
"StartServer",
true );
144 useArts = config.readBoolEntry(
"Use Arts", useArts );
145 bool ok = config.readBoolEntry(
"Arts Init",
true );
147 if ( useArts && !ok )
151 i18n(
"During the previous startup, KNotify crashed while creating "
152 "Arts::Dispatcher. Do you want to try again or disable "
153 "aRts sound output?\n\n"
154 "If you choose to disable aRts output now, you can re-enable "
155 "it later or select an alternate sound player "
156 "in the System Notifications control panel."),
157 i18n(
"KNotify Problem"),
159 i18n(
"D&isable aRts Output"),
160 "KNotifyStartProgress",
170 config.writeEntry(
"Arts Init",
false );
171 config.writeEntry(
"Use Arts", useArts );
182 config.writeEntry(
"Arts Init", useArts );
185 ok = config.readBoolEntry(
"KNotify Init",
true );
186 if ( useArts && !ok )
190 i18n(
"During the previous startup, KNotify crashed while instantiating "
191 "KNotify. Do you want to try again or disable "
192 "aRts sound output?\n\n"
193 "If you choose to disable aRts output now, you can re-enable "
194 "it later or select an alternate sound player "
195 "in the System Notifications control panel."),
196 i18n(
"KNotify Problem"),
198 i18n(
"D&isable aRts Output"),
199 "KNotifyStartProgress",
213 config.writeEntry(
"KNotify Init",
false );
214 config.writeEntry(
"Use Arts", useArts );
218 KNotify *notify =
new KNotify( useArts );
220 config.writeEntry(
"KNotify Init",
true );
226 KNotify *notify =
new KNotify(
false );
230 app.
dcopClient()->setDefaultObject(
"Notify" );
234 int ret = app.exec();
244 KNotify::KNotify(
bool useArts )
245 : TQObject(), DCOPObject(
"Notify")
247 d =
new KNotifyPrivate;
248 d->globalEvents =
new KConfig(
"knotify/eventsrc",
true,
false,
"data");
249 d->globalConfig =
new KConfig(
"knotify.eventsrc",
true,
false);
250 d->externalPlayerProc = 0;
251 d->useArts = useArts;
254 d->playObjects.setAutoDelete(
true);
258 connect( soundServer, TQT_SIGNAL( restartedServer() ),
this, TQT_SLOT( restartedArtsd() ) );
275 d->playObjects.clear();
277 delete d->globalEvents;
278 delete d->globalConfig;
279 delete d->externalPlayerProc;
280 delete d->audioManager;
286 void KNotify::loadConfig() {
290 d->useExternal = kc->
readBoolEntry(
"Use external player",
false );
294 if ( d->externalPlayer.isEmpty() ) {
295 TQStringList players;
296 players <<
"wavplay" <<
"aplay" <<
"auplay";
297 TQStringList::Iterator it = players.begin();
298 while ( d->externalPlayer.isEmpty() && it != players.end() ) {
309 void KNotify::reconfigure()
311 kapp->config()->reparseConfiguration();
315 d->globalConfig->reparseConfiguration();
316 for ( TQMapIterator<TQString,KConfig*> it = d->configs.begin(); it != d->configs.end(); ++it )
322 void KNotify::notify(
const TQString &event,
const TQString &fromApp,
323 const TQString &text, TQString sound, TQString file,
324 int present,
int level)
326 notify( event, fromApp, text, sound, file, present, level, 0, 1 );
329 void KNotify::notify(
const TQString &event,
const TQString &fromApp,
330 const TQString &text, TQString sound, TQString file,
331 int present,
int level,
int winId)
333 notify( event, fromApp, text, sound, file, present, level, winId, 1 );
336 void KNotify::notify(
const TQString &event,
const TQString &fromApp,
337 const TQString &text, TQString sound, TQString file,
338 int present,
int level,
int winId,
int eventId )
343 d->startupEvents +=
"(" +
event +
":" + fromApp +
")";
346 TQString commandline;
351 if ( !event.isEmpty() ) {
354 if ( d->events.contains( fromApp ) ) {
355 eventsFile = d->events[fromApp];
357 eventsFile=
new KConfig(
locate(
"data", fromApp+
"/eventsrc"),
true,
false);
358 d->events.insert( fromApp, eventsFile );
360 if ( d->configs.contains( fromApp) ) {
361 configFile = d->configs[fromApp];
363 configFile=
new KConfig(fromApp+
".eventsrc",
true,
false);
364 d->configs.insert( fromApp, configFile );
367 if ( !eventsFile->
hasGroup( event ) && isGlobal(event) )
369 eventsFile = d->globalEvents;
370 configFile = d->globalConfig;
378 present = configFile->
readNumEntry(
"presentation", -1 );
380 present = eventsFile->
readNumEntry(
"default_presentation", 0 );
383 if( present & KNotifyClient::Sound ) {
384 TQString theSound = configFile->
readPathEntry(
"soundfile" );
385 if ( theSound.isEmpty() )
387 if ( !theSound.isEmpty() )
392 if( present & KNotifyClient::Logfile ) {
394 if ( theFile.isEmpty() )
396 if ( !theFile.isEmpty() )
401 if( present & KNotifyClient::Messagebox )
407 if ( commandline.isEmpty() )
408 commandline = eventsFile->
readPathEntry(
"default_commandline" );
413 if ( present & KNotifyClient::Sound )
414 notifyBySound( sound, fromApp, eventId );
416 if ( present & KNotifyClient::Execute )
417 notifyByExecute( commandline, event, fromApp, text, winId, eventId );
419 if ( present & KNotifyClient::Logfile )
420 notifyByLogfile( text, file );
422 if ( present & KNotifyClient::Stderr )
423 notifyByStderr( text );
426 notifyByTaskbar( checkWinId( fromApp, winId ));
429 notifyByPassivePopup( text, fromApp, eventsFile, checkWinId( fromApp, winId ));
430 else if ( present & KNotifyClient::Messagebox )
431 notifyByMessagebox( text, level, checkWinId( fromApp, winId ));
434 TQDataStream ds(qbd, IO_WriteOnly);
435 ds <<
event << fromApp << text << sound << file << present << level
437 emitDCOPSignal(
"notifySignal(TQString,TQString,TQString,TQString,TQString,int,int,int,int)", qbd);
442 bool KNotify::notifyBySound(
const TQString &sound,
const TQString &appname,
int eventId )
444 if (sound.isEmpty()) {
445 soundFinished( eventId, NoSoundFile );
449 bool external = d->useExternal && !d->externalPlayer.isEmpty();
451 TQString soundFile(sound);
452 if ( TQFileInfo(sound).isRelative() )
454 TQString search = TQString(
"%1/sounds/%2").arg(appname).arg(sound);
456 if ( soundFile.isEmpty() )
457 soundFile =
locate(
"sound", sound );
459 if ( soundFile.isEmpty() || isPlaying( soundFile ) )
461 soundFinished( eventId, soundFile.isEmpty() ? NoSoundFile : FileAlreadyPlaying );
473 soundFinished( eventId, NoSoundSupport );
479 while( d->playObjects.count()>5 )
480 abortFirstPlayObject();
483 if( d->audioManager )
487 KDE::PlayObject *playObject = factory.createPlayObject(soundURL,
false);
491 soundFinished( eventId, NoSoundSupport );
496 if ( d->volume != 100 )
500 Arts::StereoVolumeControl volumeControl = Arts::DynamicCast(soundServer->
server().createObject(
"Arts::StereoVolumeControl"));
501 Arts::PlayObject player = playObject->
object();
502 Arts::Synth_AMAN_PLAY ap = d->audioManager->amanPlay();
503 if( ! volumeControl.isNull() && ! player.isNull() && ! ap.isNull() )
505 volumeControl.scaleFactor( d->volume/100.0 );
508 Arts::disconnect( player,
"left", ap,
"left" );
509 Arts::disconnect( player,
"right", ap,
"right" );
512 volumeControl.start();
514 Arts::connect(player,
"left",volumeControl,
"inleft");
515 Arts::connect(player,
"right",volumeControl,
"inright");
517 Arts::connect(volumeControl,
"outleft",ap,
"left");
518 Arts::connect(volumeControl,
"outright",ap,
"right");
520 player._addChild( volumeControl,
"volume" );
525 d->playObjects.append( playObject );
526 d->playObjectEventMap.insert( playObject, eventId );
530 d->playTimer =
new TQTimer(
this );
531 connect( d->playTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( playTimeout() ) );
533 if ( !d->playTimer->isActive() )
534 d->playTimer->start( 1000 );
538 }
else if(!d->externalPlayer.isEmpty()) {
540 KProcess *proc = d->externalPlayerProc;
543 proc = d->externalPlayerProc =
new KProcess;
544 connect( proc, TQT_SIGNAL( processExited(
KProcess * )),
545 TQT_SLOT( slotPlayerProcessExited(
KProcess * )));
549 soundFinished( eventId, PlayerBusy );
553 (*proc) << d->externalPlayer << TQFile::encodeName( soundFile ).data();
554 d->externalPlayerEventId = eventId;
559 soundFinished( eventId, Unknown );
563 bool KNotify::notifyByMessagebox(
const TQString &text,
int level, WId winId)
566 if ( text.isEmpty() )
572 case KNotifyClient::Notification:
575 case KNotifyClient::Warning:
578 case KNotifyClient::Error:
581 case KNotifyClient::Catastrophe:
589 bool KNotify::notifyByPassivePopup(
const TQString &text,
590 const TQString &appName,
595 if ( eventsFile != NULL ) {
597 TQString iconName = config.readEntry(
"IconName", appName );
598 TQPixmap icon = iconLoader.loadIcon( iconName,
KIcon::Small );
599 TQString title = config.readEntry(
"Comment", appName );
602 kdError() <<
"No events for app " << appName <<
"defined!" <<
endl;
607 bool KNotify::notifyByExecute(
const TQString &command,
const TQString& event,
608 const TQString& fromApp,
const TQString& text,
609 int winId,
int eventId) {
610 if (!command.isEmpty()) {
612 TQMap<TQChar,TQString> subst;
613 subst.insert(
'e', event );
614 subst.insert(
'a', fromApp );
615 subst.insert(
's', text );
616 subst.insert(
'w', TQString::number( winId ));
617 subst.insert(
'i', TQString::number( eventId ));
619 if ( execLine.isEmpty() )
632 bool KNotify::notifyByLogfile(
const TQString &text,
const TQString &file)
635 if ( text.isEmpty() )
639 TQFile logFile(file);
640 if ( !logFile.open(IO_WriteOnly | IO_Append) )
644 TQTextStream strm( &logFile );
645 strm <<
"- KNotify " << TQDateTime::currentDateTime().toString() <<
": ";
646 strm << text <<
endl;
653 bool KNotify::notifyByStderr(
const TQString &text)
656 if ( text.isEmpty() )
660 TQTextStream strm( stderr, IO_WriteOnly );
663 strm <<
"KNotify " << TQDateTime::currentDateTime().toString() <<
": ";
664 strm << text <<
endl;
669 bool KNotify::notifyByTaskbar( WId win )
677 bool KNotify::isGlobal(
const TQString &eventname)
679 return d->globalEvents->hasGroup( eventname );
682 void KNotify::setVolume(
int volume )
684 if ( volume<0 ) volume=0;
685 if ( volume>=100 ) volume=100;
689 void KNotify::playTimeout()
692 for ( TQPtrListIterator< KDE::PlayObject > it(d->playObjects); *it;)
694 TQPtrListIterator< KDE::PlayObject > current = it;
696 if ( (*current)->state() != Arts::posPlaying )
698 TQMap<KDE::PlayObject*,int>::Iterator eit = d->playObjectEventMap.find( *current );
699 if ( eit != d->playObjectEventMap.end() )
701 soundFinished( *eit, PlayedOK );
702 d->playObjectEventMap.remove( eit );
704 d->playObjects.remove( current );
707 if ( !d->playObjects.count() )
708 d->playTimer->stop();
712 bool KNotify::isPlaying(
const TQString& soundFile )
const
715 for ( TQPtrListIterator< KDE::PlayObject > it(d->playObjects); *it; ++it)
717 if ( (*it)->mediaName() == soundFile )
724 void KNotify::slotPlayerProcessExited(
KProcess *proc )
726 soundFinished( d->externalPlayerEventId,
730 void KNotify::abortFirstPlayObject()
733 TQMap<KDE::PlayObject*,int>::Iterator it = d->playObjectEventMap.find( d->playObjects.getFirst() );
734 if ( it != d->playObjectEventMap.end() )
736 soundFinished( it.data(), Aborted );
737 d->playObjectEventMap.remove( it );
739 d->playObjects.removeFirst();
743 void KNotify::soundFinished(
int eventId, PlayingFinishedStatus reason )
746 TQDataStream stream( data, IO_WriteOnly );
747 stream << eventId << (int) reason;
749 DCOPClient::mainClient()->emitDCOPSignal(
"KNotify",
"playingFinished(int,int)", data );
752 WId KNotify::checkWinId(
const TQString &appName, WId senderWinId )
754 if ( senderWinId == 0 )
756 TQCString senderId = kapp->dcopClient()->senderId();
757 TQCString compare = (appName +
"-mainwindow").latin1();
758 int len = compare.length();
761 QCStringList objs = kapp->dcopClient()->remoteObjects( senderId );
762 for (QCStringList::ConstIterator it = objs.begin(); it != objs.end(); ++it ) {
763 TQCString obj( *it );
764 if ( obj.left(len) == compare) {
767 TQByteArray data, replyData;
769 if ( kapp->dcopClient()->call(senderId, obj,
"getWinID()", data, replyType, replyData) ) {
770 TQDataStream answer(replyData, IO_ReadOnly);
771 if (replyType ==
"int") {
772 answer >> senderWinId;
783 void KNotify::restartedArtsd()
786 delete d->audioManager;
788 d->audioManager->setTitle( i18n(
"Trinity System Notifications" ) );
789 d->audioManager->setAutoRestoreID(
"KNotify Aman Play" );
793 void KNotify::sessionReady()
795 if( d->inStartup && !d->startupEvents.isEmpty())
796 kdDebug() <<
"There were knotify events while startup:" << d->startupEvents <<
endl;
797 d->inStartup =
false;