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;
TQString locate(const char *type, const TQString &filename, const KInstance *instance=KGlobal::instance())
static int questionYesNo(TQWidget *parent, const TQString &text, const TQString &caption=TQString::null, const KGuiItem &buttonYes=KStdGuiItem::yes(), const KGuiItem &buttonNo=KStdGuiItem::no(), const TQString &dontAskAgainName=TQString::null, int options=Notify)
KArtsServer is a wrapper to conveniently get a reference to a SoundServer, and restart artsd when nec...
Arts::SoundServerV2 server(void)
Get a verified reference to the SoundServerV2, (re)starting artsd using the kcontrol-specified settin...
KArtsDispatcher ensures that an instance of Arts::Dispatcher using an Arts::QIOManager exists...
static void informationWId(WId parent_id, const TQString &text, const TQString &caption=TQString::null, const TQString &dontShowAgainName=TQString::null, int options=Notify)
void setUseShell(bool useShell, const char *shell=0)
kdbgstream kdError(int area=0)
TQString findResource(const char *type, const TQString &filename) const
static TQString findExe(const TQString &appname, const TQString &pathstr=TQString::null, bool ignoreExecBit=false)
virtual bool start(RunMode runmode=NotifyOnExit, Communication comm=NoCommunication)
kdbgstream kdDebug(int area=0)
void disableSessionManagement()
Arts::PlayObject object()
Returns the internal Arts::PlayObject.
void setGroup(const TQString &group)
static void demandAttention(WId win, bool set=true)
TQString readPathEntry(const TQString &pKey, const TQString &aDefault=TQString::null) const
bool readBoolEntry(const TQString &pKey, bool bDefault=false) const
This class acts as a general interface to the KDE multimedia framework.
void setPath(const TQString &path)
KDE Wrapper for Arts::Synth_AMAN_PLAY.
static KInstance * instance()
static void errorWId(WId parent_id, const TQString &text, const TQString &caption=TQString::null, int options=Notify)
static void sorryWId(WId parent_id, const TQString &text, const TQString &caption=TQString::null, int options=Notify)
static void addCmdLineOptions()
static void init(int _argc, char **_argv, const char *_appname, const char *programName, const char *_description, const char *_version, bool noKApp=false)
bool isNull()
return true if this != 0.
int readNumEntry(const TQString &pKey, int nDefault=0) const
KStandardDirs * dirs() const
static DCOPClient * dcopClient()
void data(TQByteArray &data)
Data from the aRts server has arrived.
void setAudioManagerPlay(KAudioManagerPlay *amanplay)
If this is set the PlayObject doesn't create a Synth_BUS_UPLINK at all but always uses the Synth_AMAN...
void play()
causes the PlayObject to start the play back.
kndbgstream & endl(kndbgstream &s)
This class implements a factory to create KDE::PlayObjects for a given URL and mimetype.
bool hasGroup(const TQString &group) const
static KConfig * config()
TQString expandMacrosShellQuote(const TQString &str, const TQMap< TQChar, TQString > &map, TQChar c= '%')