-rw-r--r-- | library/alarmserver.cpp | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/library/alarmserver.cpp b/library/alarmserver.cpp new file mode 100644 index 0000000..a1a7142 --- a/dev/null +++ b/library/alarmserver.cpp | |||
@@ -0,0 +1,376 @@ | |||
1 | /********************************************************************** | ||
2 | ** Copyright (C) 2000 Trolltech AS. All rights reserved. | ||
3 | ** | ||
4 | ** This file is part of Qtopia Environment. | ||
5 | ** | ||
6 | ** This file may be distributed and/or modified under the terms of the | ||
7 | ** GNU General Public License version 2 as published by the Free Software | ||
8 | ** Foundation and appearing in the file LICENSE.GPL included in the | ||
9 | ** packaging of this file. | ||
10 | ** | ||
11 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE | ||
12 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
13 | ** | ||
14 | ** See http://www.trolltech.com/gpl/ for GPL licensing information. | ||
15 | ** | ||
16 | ** Contact info@trolltech.com if any conditions of this licensing are | ||
17 | ** not clear to you. | ||
18 | ** | ||
19 | **********************************************************************/ | ||
20 | |||
21 | #include <qdir.h> | ||
22 | #include <qfile.h> | ||
23 | #include <qmessagebox.h> | ||
24 | #include <qtextstream.h> | ||
25 | |||
26 | |||
27 | #include "qpeapplication.h" | ||
28 | #include "global.h" | ||
29 | #include "resource.h" | ||
30 | |||
31 | #if defined(Q_WS_QWS) && !defined(QT_NO_COP) | ||
32 | #include "qcopenvelope_qws.h" | ||
33 | #endif | ||
34 | #include "alarmserver.h" | ||
35 | #include <qpe/timeconversion.h> | ||
36 | |||
37 | #include <sys/types.h> | ||
38 | #include <sys/stat.h> | ||
39 | |||
40 | #include <stdlib.h> | ||
41 | #include <unistd.h> | ||
42 | |||
43 | struct timerEventItem { | ||
44 | time_t UTCtime; | ||
45 | QCString channel, message; | ||
46 | int data; | ||
47 | bool operator==( const timerEventItem &right ) const | ||
48 | { | ||
49 | return ( UTCtime == right.UTCtime | ||
50 | && channel == right.channel | ||
51 | && message == right.message | ||
52 | && data == right.data ); | ||
53 | } | ||
54 | }; | ||
55 | |||
56 | class TimerReceiverObject : public QObject | ||
57 | { | ||
58 | public: | ||
59 | TimerReceiverObject() { } | ||
60 | ~TimerReceiverObject() { } | ||
61 | void resetTimer(); | ||
62 | void setTimerEventItem(); | ||
63 | void deleteTimer(); | ||
64 | protected: | ||
65 | void timerEvent( QTimerEvent *te ); | ||
66 | private: | ||
67 | QString atfilename; | ||
68 | }; | ||
69 | |||
70 | TimerReceiverObject *timerEventReceiver = NULL; | ||
71 | QList<timerEventItem> timerEventList; | ||
72 | timerEventItem *nearestTimerEvent = NULL; | ||
73 | |||
74 | |||
75 | // set the timer to go off on the next event in the list | ||
76 | void setNearestTimerEvent() | ||
77 | { | ||
78 | nearestTimerEvent = NULL; | ||
79 | QListIterator<timerEventItem> it( timerEventList ); | ||
80 | if ( *it ) | ||
81 | nearestTimerEvent = *it; | ||
82 | for ( ; *it; ++it ) | ||
83 | if ( (*it)->UTCtime < nearestTimerEvent->UTCtime ) | ||
84 | nearestTimerEvent = *it; | ||
85 | if (nearestTimerEvent) | ||
86 | timerEventReceiver->resetTimer(); | ||
87 | else | ||
88 | timerEventReceiver->deleteTimer(); | ||
89 | } | ||
90 | |||
91 | |||
92 | //store current state to file | ||
93 | //Simple implementation. Should run on a timer. | ||
94 | |||
95 | static void saveState() | ||
96 | { | ||
97 | QString savefilename = Global::applicationFileName( "AlarmServer", "saveFile" ); | ||
98 | if ( timerEventList.isEmpty() ) { | ||
99 | unlink( savefilename ); | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | QFile savefile(savefilename+".new"); | ||
104 | if ( savefile.open(IO_WriteOnly) ) { | ||
105 | QDataStream ds( &savefile ); | ||
106 | |||
107 | //save | ||
108 | |||
109 | QListIterator<timerEventItem> it( timerEventList ); | ||
110 | for ( ; *it; ++it ) { | ||
111 | ds << it.current()->UTCtime; | ||
112 | ds << it.current()->channel; | ||
113 | ds << it.current()->message; | ||
114 | ds << it.current()->data; | ||
115 | } | ||
116 | |||
117 | |||
118 | savefile.close(); | ||
119 | unlink( savefilename ); | ||
120 | QDir d; d.rename(savefilename+".new",savefilename); | ||
121 | |||
122 | } | ||
123 | } | ||
124 | |||
125 | /*! | ||
126 | Sets up the alarm server. Restoring to previous state (session management). | ||
127 | */ | ||
128 | void AlarmServer::initialize() | ||
129 | { | ||
130 | //read autosave file and put events in timerEventList | ||
131 | |||
132 | QString savefilename = Global::applicationFileName( "AlarmServer", "saveFile" ); | ||
133 | |||
134 | QFile savefile(savefilename); | ||
135 | if ( savefile.open(IO_ReadOnly) ) { | ||
136 | QDataStream ds( &savefile ); | ||
137 | while ( !ds.atEnd() ) { | ||
138 | timerEventItem *newTimerEventItem = new timerEventItem; | ||
139 | ds >> newTimerEventItem->UTCtime; | ||
140 | ds >> newTimerEventItem->channel; | ||
141 | ds >> newTimerEventItem->message; | ||
142 | ds >> newTimerEventItem->data; | ||
143 | timerEventList.append( newTimerEventItem ); | ||
144 | } | ||
145 | savefile.close(); | ||
146 | if (!timerEventReceiver) | ||
147 | timerEventReceiver = new TimerReceiverObject; | ||
148 | setNearestTimerEvent(); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | |||
153 | |||
154 | |||
155 | static const char* atdir = "/var/spool/at/"; | ||
156 | |||
157 | static bool triggerAtd( bool writeHWClock = FALSE ) | ||
158 | { | ||
159 | QFile trigger(QString(atdir) + "trigger"); | ||
160 | if ( trigger.open(IO_WriteOnly|IO_Raw) ) { | ||
161 | |||
162 | const char* data = | ||
163 | #ifdef QT_QWS_CUSTOM | ||
164 | //custom atd only writes HW Clock if we write a 'W' | ||
165 | ( writeHWClock ) ? "W\n" : | ||
166 | #endif | ||
167 | data = "\n"; | ||
168 | int len = strlen(data); | ||
169 | int total_written = trigger.writeBlock(data,len); | ||
170 | if ( total_written != len ) { | ||
171 | QMessageBox::critical( 0, QObject::tr( "Out of Space" ), | ||
172 | QObject::tr( "Unable to schedule alarm.\nFree some memory and try again." ) ); | ||
173 | trigger.close(); | ||
174 | QFile::remove( trigger.name() ); | ||
175 | return FALSE; | ||
176 | } | ||
177 | return TRUE; | ||
178 | } | ||
179 | return FALSE; | ||
180 | } | ||
181 | |||
182 | void TimerReceiverObject::deleteTimer() | ||
183 | { | ||
184 | if ( !atfilename.isEmpty() ) { | ||
185 | unlink( atfilename ); | ||
186 | atfilename = QString::null; | ||
187 | triggerAtd( FALSE ); | ||
188 | } | ||
189 | } | ||
190 | |||
191 | void TimerReceiverObject::resetTimer() | ||
192 | { | ||
193 | const int maxsecs = 2147000; | ||
194 | int total_written; | ||
195 | QDateTime nearest = TimeConversion::fromUTC(nearestTimerEvent->UTCtime); | ||
196 | QDateTime now = QDateTime::currentDateTime(); | ||
197 | int secs = TimeConversion::secsTo( now, nearest ); | ||
198 | if ( secs > maxsecs ) { | ||
199 | // too far for millisecond timing | ||
200 | secs = maxsecs; | ||
201 | } | ||
202 | |||
203 | // System timer (needed so that we wake from deep sleep), | ||
204 | // from the Epoch in seconds. | ||
205 | // | ||
206 | int at_secs = TimeConversion::toUTC(nearest); | ||
207 | // qDebug("reset timer to %d seconds from Epoch",at_secs); | ||
208 | QString fn = atdir + QString::number(at_secs) + "." | ||
209 | + QString::number(getpid()); | ||
210 | if ( fn != atfilename ) { | ||
211 | QFile atfile(fn+".new"); | ||
212 | if ( atfile.open(IO_WriteOnly|IO_Raw) ) { | ||
213 | // just wake up and delete the at file | ||
214 | QString cmd = "#!/bin/sh\nrm " + fn; | ||
215 | total_written = atfile.writeBlock(cmd.latin1(),cmd.length()); | ||
216 | if ( total_written != int(cmd.length()) ) { | ||
217 | QMessageBox::critical( 0, tr("Out of Space"), | ||
218 | tr("Unable to schedule alarm.\n" | ||
219 | "Please free up space and try again") ); | ||
220 | atfile.close(); | ||
221 | QFile::remove( atfile.name() ); | ||
222 | return; | ||
223 | } | ||
224 | atfile.close(); | ||
225 | unlink( atfilename ); | ||
226 | QDir d; d.rename(fn+".new",fn); | ||
227 | chmod(fn.latin1(),0755); | ||
228 | atfilename = fn; | ||
229 | triggerAtd( FALSE ); | ||
230 | } else { | ||
231 | qWarning("Cannot open atd file %s",fn.latin1()); | ||
232 | } | ||
233 | } | ||
234 | // Qt timers (does the actual alarm) | ||
235 | // from now in milliseconds | ||
236 | // | ||
237 | qDebug("AlarmServer waiting %d seconds",secs); | ||
238 | startTimer( 1000 * secs + 500 ); | ||
239 | } | ||
240 | |||
241 | void TimerReceiverObject::timerEvent( QTimerEvent * ) | ||
242 | { | ||
243 | bool needSave = FALSE; | ||
244 | killTimers(); | ||
245 | if (nearestTimerEvent) { | ||
246 | if ( nearestTimerEvent->UTCtime | ||
247 | <= TimeConversion::toUTC(QDateTime::currentDateTime()) ) { | ||
248 | QCopEnvelope e( nearestTimerEvent->channel, | ||
249 | nearestTimerEvent->message ); | ||
250 | e << TimeConversion::fromUTC( nearestTimerEvent->UTCtime ) | ||
251 | << nearestTimerEvent->data; | ||
252 | timerEventList.remove( nearestTimerEvent ); | ||
253 | needSave = TRUE; | ||
254 | } | ||
255 | setNearestTimerEvent(); | ||
256 | } else { | ||
257 | resetTimer(); | ||
258 | } | ||
259 | if ( needSave ) | ||
260 | saveState(); | ||
261 | } | ||
262 | |||
263 | /*! | ||
264 | \class AlarmServer alarmserver.h | ||
265 | \brief The AlarmServer class provides alarms to be scheduled. | ||
266 | |||
267 | Applications which wish to be informed when a certain time instant | ||
268 | passes use the functions of AlarmServer to request so. | ||
269 | */ | ||
270 | |||
271 | /*! | ||
272 | Schedules an alarm for \a when. Soon after this time, | ||
273 | \a message will be sent to \a channel, with \a data as | ||
274 | a parameter. \a message must be of the form "someMessage(int)". | ||
275 | |||
276 | \sa deleteAlarm() | ||
277 | */ | ||
278 | void AlarmServer::addAlarm ( QDateTime when, const QCString& channel, | ||
279 | const QCString& message, int data) | ||
280 | { | ||
281 | if ( qApp->type() == QApplication::GuiServer ) { | ||
282 | bool needSave = FALSE; | ||
283 | // Here we are the server so either it has been directly called from | ||
284 | // within the server or it has been sent to us from a client via QCop | ||
285 | if (!timerEventReceiver) | ||
286 | timerEventReceiver = new TimerReceiverObject; | ||
287 | |||
288 | timerEventItem *newTimerEventItem = new timerEventItem; | ||
289 | newTimerEventItem->UTCtime = TimeConversion::toUTC( when ); | ||
290 | newTimerEventItem->channel = channel; | ||
291 | newTimerEventItem->message = message; | ||
292 | newTimerEventItem->data = data; | ||
293 | // explore the case of already having the event in here... | ||
294 | QListIterator<timerEventItem> it( timerEventList ); | ||
295 | for ( ; *it; ++it ) | ||
296 | if ( *(*it) == *newTimerEventItem ) | ||
297 | return; | ||
298 | // if we made it here, it is okay to add the item... | ||
299 | timerEventList.append( newTimerEventItem ); | ||
300 | needSave = TRUE; | ||
301 | // quicker than using setNearestTimerEvent() | ||
302 | if ( nearestTimerEvent ) { | ||
303 | if (newTimerEventItem->UTCtime < nearestTimerEvent->UTCtime) { | ||
304 | nearestTimerEvent = newTimerEventItem; | ||
305 | timerEventReceiver->killTimers(); | ||
306 | timerEventReceiver->resetTimer(); | ||
307 | } | ||
308 | } else { | ||
309 | nearestTimerEvent = newTimerEventItem; | ||
310 | timerEventReceiver->resetTimer(); | ||
311 | } | ||
312 | if ( needSave ) | ||
313 | saveState(); | ||
314 | } else { | ||
315 | QCopEnvelope e( "QPE/System", "addAlarm(QDateTime,QCString,QCString,int)" ); | ||
316 | e << when << channel << message << data; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | /*! | ||
321 | Deletes previously scheduled alarms which match \a when, \a channel, \a message, | ||
322 | and \a data. | ||
323 | |||
324 | Passing null values for \a when, \a channel, or \a message indicates "any". | ||
325 | Passing -1 for \a data indicates "any". | ||
326 | |||
327 | \sa deleteAlarm() | ||
328 | */ | ||
329 | void AlarmServer::deleteAlarm (QDateTime when, const QCString& channel, const QCString& message, int data) | ||
330 | { | ||
331 | if ( qApp->type() == QApplication::GuiServer) { | ||
332 | bool needSave = FALSE; | ||
333 | if ( timerEventReceiver != NULL ) { | ||
334 | timerEventReceiver->killTimers(); | ||
335 | |||
336 | // iterate over the list of events | ||
337 | QListIterator<timerEventItem> it( timerEventList ); | ||
338 | time_t deleteTime = TimeConversion::toUTC( when ); | ||
339 | for ( ; *it; ++it ) { | ||
340 | // if its a match, delete it | ||
341 | if ( ( (*it)->UTCtime == deleteTime || when.isNull() ) | ||
342 | && ( channel.isNull() || (*it)->channel == channel ) | ||
343 | && ( message.isNull() || (*it)->message == message ) | ||
344 | && ( data==-1 || (*it)->data == data ) ) | ||
345 | { | ||
346 | // if it's first, then we need to update the timer | ||
347 | if ( (*it) == nearestTimerEvent ) { | ||
348 | timerEventList.remove(*it); | ||
349 | setNearestTimerEvent(); | ||
350 | } else { | ||
351 | timerEventList.remove(*it); | ||
352 | } | ||
353 | needSave = TRUE; | ||
354 | } | ||
355 | } | ||
356 | if ( nearestTimerEvent ) | ||
357 | timerEventReceiver->resetTimer(); | ||
358 | } | ||
359 | if ( needSave ) | ||
360 | saveState(); | ||
361 | } else { | ||
362 | QCopEnvelope e( "QPE/System", "deleteAlarm(QDateTime,QCString,QCString,int)" ); | ||
363 | e << when << channel << message << data; | ||
364 | } | ||
365 | } | ||
366 | |||
367 | /*! | ||
368 | Writes the system clock to the hardware clock. | ||
369 | */ | ||
370 | void Global::writeHWClock() | ||
371 | { | ||
372 | if ( !triggerAtd( TRUE ) ) { | ||
373 | // atd not running? set it ourselves | ||
374 | system("/sbin/hwclock --systohc"); // ##### UTC? | ||
375 | } | ||
376 | } | ||