fa5cff744288a98988888e3baf23c5ce2e6006fb
[enigma2.git] / lib / base / ebase.cpp
1 #include <lib/base/ebase.h>
2
3 #include <fcntl.h>
4 #include <unistd.h>
5 #include <errno.h>
6
7 #include <lib/base/eerror.h>
8 #include <lib/base/elock.h>
9
10 eSocketNotifier::eSocketNotifier(eMainloop *context, int fd, int requested, bool startnow): context(*context), fd(fd), state(0), requested(requested)
11 {
12         if (startnow)   
13                 start();
14 }
15
16 eSocketNotifier::~eSocketNotifier()
17 {
18         stop();
19 }
20
21 void eSocketNotifier::start()
22 {
23         if (state)
24                 stop();
25
26         context.addSocketNotifier(this);
27         state=1;
28 }
29
30 void eSocketNotifier::stop()
31 {
32         if (state)
33                 context.removeSocketNotifier(this);
34
35         state=0;
36 }
37
38                                         // timer
39 void eTimer::start(long msek, bool singleShot)
40 {
41         if (bActive)
42                 stop();
43
44         bActive = true;
45         bSingleShot = singleShot;
46         interval = msek;
47         gettimeofday(&nextActivation, 0);
48 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek);
49         nextActivation += (msek<0 ? 0 : msek);
50 //      eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec );
51         context.addTimer(this);
52 }
53
54 void eTimer::startLongTimer( int seconds )
55 {
56         if (bActive)
57                 stop();
58
59         bActive = bSingleShot = true;
60         interval = 0;
61         gettimeofday(&nextActivation, 0);
62 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d sec", this, nextActivation.tv_sec, nextActivation.tv_usec, seconds);
63         if ( seconds > 0 )
64                 nextActivation.tv_sec += seconds;
65 //      eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec );
66         context.addTimer(this);
67 }
68
69 void eTimer::stop()
70 {
71         if (bActive)
72         {
73                 bActive=false;
74                 context.removeTimer(this);
75         }
76 }
77
78 void eTimer::changeInterval(long msek)
79 {
80         if (bActive)  // Timer is running?
81         {
82                 context.removeTimer(this);       // then stop
83                 nextActivation -= interval;  // sub old interval
84         }
85         else
86                 bActive=true; // then activate Timer
87
88         interval = msek;                                                // set new Interval
89         nextActivation += interval;             // calc nextActivation
90
91         context.addTimer(this);                         // add Timer to context TimerList
92 }
93
94 void eTimer::activate()   // Internal Funktion... called from eApplication
95 {
96         context.removeTimer(this);
97
98         if (!bSingleShot)
99         {
100                 nextActivation += interval;
101                 context.addTimer(this);
102         }
103         else
104                 bActive=false;
105
106         /*emit*/ timeout();
107 }
108
109 void eTimer::addTimeOffset( int offset )
110 {
111         nextActivation.tv_sec += offset;
112 }
113
114 // mainloop
115 ePtrList<eMainloop> eMainloop::existing_loops;
116
117 void eMainloop::addSocketNotifier(eSocketNotifier *sn)
118 {
119         notifiers.insert(std::pair<int,eSocketNotifier*> (sn->getFD(), sn));
120 }
121
122 void eMainloop::removeSocketNotifier(eSocketNotifier *sn)
123 {
124         notifiers.erase(sn->getFD());
125 }
126
127 void eMainloop::processOneEvent()
128 {
129                 /* get current time */
130         timeval now;
131         gettimeofday(&now, 0);
132         m_now_is_invalid = 0;
133         
134         int poll_timeout = -1; /* infinite in case of empty timer list */
135         
136         if (m_timer_list)
137         {
138                 singleLock s(recalcLock);
139                 poll_timeout = timeval_to_usec(m_timer_list.begin()->getNextActivation() - now);
140                         /* if current timer already passed, don't delay infinite. */
141                 if (poll_timeout < 0)
142                         poll_timeout = 0;
143                         
144                         /* convert us to ms */
145                 poll_timeout /= 1000;
146         }
147         
148         int ret = 0;
149
150         if (poll_timeout)
151         {
152                         // build the poll aray
153                 int fdcount = notifiers.size();
154                 pollfd* pfd = new pollfd[fdcount];  // make new pollfd array
155
156                 std::map<int,eSocketNotifier*>::iterator it(notifiers.begin());
157                 for (int i=0; i < fdcount; i++, it++)
158                 {
159                         pfd[i].fd = it->first;
160                         pfd[i].events = it->second->getRequested();
161                 }
162
163                 ret = poll(pfd, fdcount, poll_timeout);
164
165                         /* ret > 0 means that there are some active poll entries. */
166                 if (ret > 0)
167                 {
168                         for (int i=0; i < fdcount ; i++)
169                         {
170                                 if (notifiers.find(pfd[i].fd) == notifiers.end())
171                                         continue;
172                                 
173                                 int req = notifiers[pfd[i].fd]->getRequested();
174                                 
175                                 if (pfd[i].revents & req)
176                                 {
177                                         notifiers[pfd[i].fd]->activate(pfd[i].revents);
178                                 
179                                         if (!--ret)
180                                                 break;
181                                 } else if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
182                                         eFatal("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d) -> FIX YOUR CODE", pfd[i].fd,pfd[i].revents);
183                         }
184                         
185                         ret = 1; /* poll did not timeout. */
186                 } else if (ret < 0)
187                 {
188                                 /* when we got a signal, we get EINTR. */
189                         if (errno != EINTR)
190                                 eDebug("poll made error (%m)");
191                         else
192                                 ret = -1; /* don't assume the timeout has passed when we got a signal */
193                 }
194                 delete [] pfd;
195         }
196         
197                 /* when we not processed anything, check timers. */
198         if (!ret)
199         {
200                         /* we know that this time has passed. */
201                 now += poll_timeout;
202                 
203                 singleLock s(recalcLock);
204
205                         /* this will never change while we have the recalcLock */
206                         /* we can savely return here, the timer will be re-checked soon. */
207                 if (m_now_is_invalid)
208                         return;
209
210                         /* process all timers which are ready. first remove them out of the list. */
211                 while ((!m_timer_list.empty()) && (m_timer_list.begin()->getNextActivation() <= now))
212                         m_timer_list.begin()->activate();
213         }
214 }
215
216 void eMainloop::addTimer(eTimer* e)
217 {
218         m_timer_list.insert_in_order(e);
219 }
220
221 void eMainloop::removeTimer(eTimer* e)
222 {
223         m_timer_list.remove(e);
224 }
225
226 int eMainloop::exec()
227 {
228         if (!loop_level)
229         {
230                 app_quit_now = false;
231                 app_exit_loop = false;
232                 enter_loop();
233         }
234         return retval;
235 }
236
237 void eMainloop::enter_loop()
238 {
239         loop_level++;
240         // Status der vorhandenen Loop merken
241         bool old_exit_loop = app_exit_loop;
242
243         app_exit_loop = false;
244
245         while (!app_exit_loop && !app_quit_now)
246                 processOneEvent();
247
248         // wiederherstellen der vorherigen app_exit_loop
249         app_exit_loop = old_exit_loop;
250
251         --loop_level;
252
253         if (!loop_level)
254         {
255                 // do something here on exit the last loop
256         }
257 }
258
259 void eMainloop::exit_loop()  // call this to leave the current loop
260 {
261         app_exit_loop = true;
262 }
263
264 void eMainloop::quit( int ret )   // call this to leave all loops
265 {
266         retval=ret;
267         app_quit_now = true;
268 }
269
270 void eMainloop::addTimeOffset(int offset)
271 {
272         for (ePtrList<eMainloop>::iterator it(eMainloop::existing_loops)
273                 ;it != eMainloop::existing_loops.end(); ++it)
274         {
275                 singleLock s(it->recalcLock);
276                 it->m_now_is_invalid = 1;
277                 for (ePtrList<eTimer>::iterator tit = it->m_timer_list.begin(); tit != it->m_timer_list.end(); ++tit )
278                         tit->addTimeOffset(offset);
279         }
280 }
281
282 eApplication* eApp = 0;