knotifyclient.cpp
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Charles Samuels <charles@altair.dhs.org> 00003 2000 Malte Starostik <starosti@zedat.fu-berlin.de> 00004 2000,2003 Carsten Pfeiffer <pfeiffer@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License version 2 as published by the Free Software Foundation. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "knotifyclient.h" 00022 00023 #include <tqdatastream.h> 00024 #include <tqptrstack.h> 00025 00026 #include <tdeapplication.h> 00027 #include <kstandarddirs.h> 00028 #include <tdeapplication.h> 00029 #include <tdeconfig.h> 00030 #include <dcopclient.h> 00031 #include <kdebug.h> 00032 #include <kstaticdeleter.h> 00033 00034 #ifdef Q_WS_X11 00035 #include <X11/X.h> 00036 #include <X11/Xlib.h> 00037 #include <X11/XKBlib.h> 00038 #include <X11/keysym.h> 00039 #include <fixx11h.h> 00040 #endif 00041 00042 static const char daemonName[] = "knotify"; 00043 00044 static bool canAvoidStartupEvent( const TQString& event, const TQString& appname, int present ) 00045 { 00046 static bool checkAvoid = true; 00047 if( !checkAvoid ) 00048 return false; 00049 if(( appname != "twin" && appname != "ksmserver" ) || present > 0 ) { 00050 checkAvoid = false; 00051 return false; 00052 } 00053 // starttde event is in global events file 00054 static TDEConfig* configfile = appname != "ksmserver" 00055 ? new TDEConfig( appname + ".eventsrc", true, false ) 00056 : new TDEConfig( "knotify.eventsrc", true, false ); 00057 static TDEConfig* eventsfile = appname != "ksmserver" 00058 ? new TDEConfig( appname + "/eventsrc", true, false, "data" ) 00059 : new TDEConfig( "knotify/eventsrc", true, false, "data" ); 00060 configfile->setGroup( event ); 00061 eventsfile->setGroup( event ); 00062 int ev1 = configfile->readNumEntry( "presentation", -2 ); 00063 int ev2 = eventsfile->readNumEntry( "default_presentation", -2 ); 00064 if(( ev1 == -2 && ev2 == -2 ) // unknown 00065 || ev1 > 0 // configured to have presentation 00066 || ( ev1 == -2 && ev2 > 0 )) { // not configured, has default presentation 00067 checkAvoid = false; 00068 return false; 00069 } 00070 return true; 00071 } 00072 00073 static int sendNotifyEvent(const TQString &message, const TQString &text, 00074 int present, int level, const TQString &sound, 00075 const TQString &file, int winId ) 00076 { 00077 if (!kapp) return 0; 00078 00079 // ensure tray icon is shown and positioned before sending event to notification daemon 00080 #ifdef Q_WS_X11 00081 XFlush(tqt_xdisplay()); 00082 #endif 00083 00084 DCOPClient *client=kapp->dcopClient(); 00085 if (!client->isAttached()) 00086 { 00087 client->attach(); 00088 if (!client->isAttached()) 00089 return 0; 00090 } 00091 00092 TQString appname = KNotifyClient::instance()->instanceName(); 00093 00094 if( canAvoidStartupEvent( message, appname, present )) 00095 return -1; // done "successfully" - there will be no event presentation 00096 00097 int uniqueId = kMax( 1, kapp->random() ); // must not be 0 -- means failure! 00098 00099 // knotify daemon needs toplevel window 00100 TQWidget* widget = TQT_TQWIDGET(TQWidget::find( (WId)winId )); 00101 if( widget ) 00102 winId = (int)widget->topLevelWidget()->winId(); 00103 00104 TQByteArray data; 00105 TQDataStream ds(data, IO_WriteOnly); 00106 ds << message << appname << text << sound << file << present << level 00107 << winId << uniqueId; 00108 00109 if ( !KNotifyClient::startDaemon() ) 00110 return 0; 00111 00112 if ( client->send(daemonName, "Notify", "notify(TQString,TQString,TQString,TQString,TQString,int,int,int,int)", data) ) 00113 { 00114 return uniqueId; 00115 } 00116 00117 return 0; 00118 } 00119 00120 int KNotifyClient::event( StandardEvent type, const TQString& text ) 00121 { 00122 return event( 0, type, text ); 00123 } 00124 00125 int KNotifyClient::event(const TQString &message, const TQString &text) 00126 { 00127 return event(0, message, text); 00128 } 00129 00130 int KNotifyClient::userEvent(const TQString &text, int present, int level, 00131 const TQString &sound, const TQString &file) 00132 { 00133 return userEvent( 0, text, present, level, sound, file ); 00134 } 00135 00136 00137 int KNotifyClient::event( int winId, StandardEvent type, const TQString& text ) 00138 { 00139 TQString message; 00140 switch ( type ) { 00141 case cannotOpenFile: 00142 message = TQString::fromLatin1("cannotopenfile"); 00143 break; 00144 case warning: 00145 message = TQString::fromLatin1("warning"); 00146 break; 00147 case fatalError: 00148 message = TQString::fromLatin1("fatalerror"); 00149 break; 00150 case catastrophe: 00151 message = TQString::fromLatin1("catastrophe"); 00152 break; 00153 case notification: // fall through 00154 default: 00155 message = TQString::fromLatin1("notification"); 00156 break; 00157 } 00158 00159 return sendNotifyEvent( message, text, Default, Default, 00160 TQString::null, TQString::null, winId ); 00161 } 00162 00163 int KNotifyClient::event(int winId, const TQString &message, 00164 const TQString &text) 00165 { 00166 return sendNotifyEvent(message, text, Default, Default, TQString::null, TQString::null, winId); 00167 } 00168 00169 int KNotifyClient::userEvent(int winId, const TQString &text, int present, 00170 int level, 00171 const TQString &sound, const TQString &file) 00172 { 00173 return sendNotifyEvent(TQString::null, text, present, level, sound, file, winId); 00174 } 00175 00176 int KNotifyClient::getPresentation(const TQString &eventname) 00177 { 00178 int present; 00179 if (eventname.isEmpty()) return Default; 00180 00181 TDEConfig eventsfile( KNotifyClient::instance()->instanceName()+".eventsrc", true, false); 00182 eventsfile.setGroup(eventname); 00183 00184 present=eventsfile.readNumEntry("presentation", -1); 00185 00186 return present; 00187 } 00188 00189 TQString KNotifyClient::getFile(const TQString &eventname, int present) 00190 { 00191 if (eventname.isEmpty()) return TQString::null; 00192 00193 TDEConfig eventsfile( KNotifyClient::instance()->instanceName()+".eventsrc", true, false); 00194 eventsfile.setGroup(eventname); 00195 00196 switch (present) 00197 { 00198 case (Sound): 00199 return eventsfile.readPathEntry("soundfile"); 00200 case (Logfile): 00201 return eventsfile.readPathEntry("logfile"); 00202 } 00203 00204 return TQString::null; 00205 } 00206 00207 int KNotifyClient::getDefaultPresentation(const TQString &eventname) 00208 { 00209 int present; 00210 if (eventname.isEmpty()) return Default; 00211 00212 TDEConfig eventsfile( KNotifyClient::instance()->instanceName()+"/eventsrc", true, false, "data"); 00213 eventsfile.setGroup(eventname); 00214 00215 present=eventsfile.readNumEntry("default_presentation", -1); 00216 00217 return present; 00218 } 00219 00220 TQString KNotifyClient::getDefaultFile(const TQString &eventname, int present) 00221 { 00222 if (eventname.isEmpty()) return TQString::null; 00223 00224 TDEConfig eventsfile( KNotifyClient::instance()->instanceName()+"/eventsrc", true, false, "data"); 00225 eventsfile.setGroup(eventname); 00226 00227 switch (present) 00228 { 00229 case (Sound): 00230 return eventsfile.readPathEntry("default_sound"); 00231 case (Logfile): 00232 return eventsfile.readPathEntry("default_logfile"); 00233 } 00234 00235 return TQString::null; 00236 } 00237 00238 bool KNotifyClient::startDaemon() 00239 { 00240 static bool firstTry = true; 00241 if (!kapp->dcopClient()->isApplicationRegistered(daemonName)) { 00242 if( firstTry ) { 00243 firstTry = false; 00244 return TDEApplication::startServiceByDesktopName(daemonName) == 0; 00245 } 00246 return false; 00247 } 00248 return true; 00249 } 00250 00251 00252 void KNotifyClient::beep(const TQString& reason) 00253 { 00254 if ( !kapp || KNotifyClient::Instance::currentInstance()->useSystemBell() ) { 00255 TQApplication::beep(); 00256 return; 00257 } 00258 00259 DCOPClient *client=kapp->dcopClient(); 00260 if (!client->isAttached()) 00261 { 00262 client->attach(); 00263 if (!client->isAttached() || !client->isApplicationRegistered(daemonName)) 00264 { 00265 TQApplication::beep(); 00266 return; 00267 } 00268 } 00269 // The kaccess daemon handles visual and other audible beeps 00270 if ( client->isApplicationRegistered( "kaccess" ) ) 00271 { 00272 TQApplication::beep(); 00273 return; 00274 } 00275 00276 KNotifyClient::event(KNotifyClient::notification, reason); 00277 } 00278 00279 00280 TDEInstance * KNotifyClient::instance() { 00281 return KNotifyClient::Instance::current(); 00282 } 00283 00284 00285 class KNotifyClient::InstanceStack 00286 { 00287 public: 00288 InstanceStack() { m_defaultInstance = 0; } 00289 virtual ~InstanceStack() { delete m_defaultInstance; } 00290 void push(Instance *instance) { m_instances.push(instance); } 00291 00292 void pop(Instance *instance) 00293 { 00294 if (m_instances.top() == instance) 00295 m_instances.pop(); 00296 else if (!m_instances.isEmpty()) 00297 { 00298 kdWarning(160) << "Tried to remove an Instance that is not the current," << endl; 00299 kdWarning(160) << "Resetting to the main TDEApplication." << endl; 00300 m_instances.clear(); 00301 } 00302 else 00303 kdWarning(160) << "Tried to remove an Instance, but the stack was empty." << endl; 00304 } 00305 00306 Instance *currentInstance() 00307 { 00308 if (m_instances.isEmpty()) 00309 { 00310 m_defaultInstance = new Instance(kapp); 00311 } 00312 return m_instances.top(); 00313 } 00314 00315 private: 00316 TQPtrStack<Instance> m_instances; 00317 Instance *m_defaultInstance; 00318 }; 00319 00320 KNotifyClient::InstanceStack * KNotifyClient::Instance::s_instances = 0L; 00321 static KStaticDeleter<KNotifyClient::InstanceStack > instancesDeleter; 00322 00323 struct KNotifyClient::InstancePrivate 00324 { 00325 TDEInstance *instance; 00326 bool useSystemBell; 00327 }; 00328 00329 KNotifyClient::Instance::Instance(TDEInstance *instance) 00330 { 00331 d = new InstancePrivate; 00332 d->instance = instance; 00333 instances()->push(this); 00334 00335 TDEConfig *config = instance->config(); 00336 TDEConfigGroupSaver cs( config, "General" ); 00337 d->useSystemBell = config->readBoolEntry( "UseSystemBell", false ); 00338 } 00339 00340 KNotifyClient::Instance::~Instance() 00341 { 00342 if (s_instances) 00343 s_instances->pop(this); 00344 delete d; 00345 } 00346 00347 KNotifyClient::InstanceStack *KNotifyClient::Instance::instances() 00348 { 00349 if (!s_instances) 00350 instancesDeleter.setObject(s_instances, new InstanceStack); 00351 return s_instances; 00352 } 00353 00354 bool KNotifyClient::Instance::useSystemBell() const 00355 { 00356 return d->useSystemBell; 00357 } 00358 00359 00360 // static methods 00361 00362 // We always return a valid KNotifyClient::Instance here. If no special one 00363 // is available, we have a default-instance with kapp as TDEInstance. 00364 // We make sure to always have that default-instance in the stack, because 00365 // the stack might have gotten cleared in the destructor. 00366 // We can't use QStack::setAutoDelete( true ), because no instance besides 00367 // our default instance is owned by us. 00368 KNotifyClient::Instance * KNotifyClient::Instance::currentInstance() 00369 { 00370 return instances()->currentInstance(); 00371 } 00372 00373 TDEInstance *KNotifyClient::Instance::current() 00374 { 00375 return currentInstance()->d->instance; 00376 }