- add looping for ts playbacks (hack for prodrive)
[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         nextActivation.tv_sec -= context.getTimerOffset();
49         nextActivation += (msek<0 ? 0 : msek);
50 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek);
51 //      eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec );
52         context.addTimer(this);
53 }
54
55 void eTimer::startLongTimer( int seconds )
56 {
57         if (bActive)
58                 stop();
59
60         bActive = bSingleShot = true;
61         interval = 0;
62         gettimeofday(&nextActivation, 0);
63         nextActivation.tv_sec -= context.getTimerOffset();
64 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek);
65         if ( seconds > 0 )
66                 nextActivation.tv_sec += seconds;
67 //      eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec );
68         context.addTimer(this);
69 }
70
71 void eTimer::stop()
72 {
73         if (bActive)
74         {
75                 bActive=false;
76                 context.removeTimer(this);
77         }
78 }
79
80 void eTimer::changeInterval(long msek)
81 {
82         if (bActive)  // Timer is running?
83         {
84                 context.removeTimer(this);       // then stop
85                 nextActivation -= interval;  // sub old interval
86         }
87         else
88                 bActive=true; // then activate Timer
89
90         interval = msek;                                                // set new Interval
91         nextActivation += interval;             // calc nextActivation
92
93         context.addTimer(this);                         // add Timer to context TimerList
94 }
95
96 void eTimer::activate()   // Internal Funktion... called from eApplication
97 {
98         context.removeTimer(this);
99
100         if (!bSingleShot)
101         {
102                 nextActivation += interval;
103                 context.addTimer(this);
104         }
105         else
106                 bActive=false;
107
108         /*emit*/ timeout();
109 }
110
111 inline void eTimer::recalc( int offset )
112 {
113         nextActivation.tv_sec += offset;
114 }
115
116 // mainloop
117 ePtrList<eMainloop> eMainloop::existing_loops;
118
119 void eMainloop::setTimerOffset( int difference )
120 {
121         singleLock s(recalcLock);
122         if (!TimerList)
123                 timer_offset=0;
124         else
125         {
126                 if ( timer_offset )
127                         eDebug("time_offset %d avail.. add new offset %d than new is %d",
128                         timer_offset, difference, timer_offset+difference);
129                 timer_offset+=difference;
130         }
131 }
132
133 void eMainloop::addSocketNotifier(eSocketNotifier *sn)
134 {
135         notifiers.insert(std::pair<int,eSocketNotifier*> (sn->getFD(), sn));
136 }
137
138 void eMainloop::removeSocketNotifier(eSocketNotifier *sn)
139 {
140         notifiers.erase(sn->getFD());
141 }
142
143 void eMainloop::processOneEvent()
144 {
145                 /* notes:
146                   - we should use epoll(4)
147                   - timer are checked twice. there was a strong reason for it, but i can't remember. (FIXME)
148                   - for each time, we gettimeofday() and check wether the timer should fire.
149                     we should do this all better - we know how long the poll last, so we know which
150                     timers should fire. Problem is that a timer handler could have required so
151                     much time that another timer fired.
152
153                     A probably structure could look
154
155                     while (1)
156                     {
157                             time = gettimeofday()
158                             timeout = calculate_pending_timers(time);
159
160                       doPoll(timeout or infinite);
161
162                         if (poll_had_results)
163                                 handle_poll_handler();
164                         else
165                                     fire_timers(time + timeout)
166                           }
167
168                           the gettimeofday() call is required because fire_timers could last more
169                           than nothing.
170
171                           when poll did no timeout, we don't handle timers, as this will be done
172                           in the next iteration (without adding overhead - we had to get the new
173                           time anyway
174                 */
175
176                 // first, process pending timers...
177         long usec=0;
178
179         if ( TimerList )
180                 doRecalcTimers();
181         while (TimerList && (usec = timeout_usec( TimerList.begin()->getNextActivation() ) ) <= 0 )
182         {
183                 TimerList.begin()->activate();
184                 doRecalcTimers();
185         }
186
187         int fdAnz = notifiers.size();
188         pollfd pfd[fdAnz];
189
190 // fill pfd array
191         std::map<int,eSocketNotifier*>::iterator it(notifiers.begin());
192         for (int i=0; i < fdAnz; i++, it++)
193         {
194                 pfd[i].fd = it->first;
195                 pfd[i].events = it->second->getRequested();
196         }
197
198                 // to the poll. When there are no timers, we have an infinite timeout
199         int ret=poll(pfd, fdAnz, TimerList ? usec / 1000 : -1);  // convert to ms
200
201         if (ret>0)
202         {
203         //              eDebug("bin aussem poll raus und da war was");
204                 for (int i=0; i < fdAnz ; i++)
205                 {
206                         if( notifiers.find(pfd[i].fd) == notifiers.end())
207                                 continue;
208
209                         int req = notifiers[pfd[i].fd]->getRequested();
210
211                         if ( pfd[i].revents & req )
212                         {
213                                 notifiers[pfd[i].fd]->activate(pfd[i].revents);
214                                 if (!--ret)
215                                         break;
216                         }
217                         else if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
218                                 eDebug("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d)", pfd[i].fd,pfd[i].revents);
219                 }
220         }
221         else if (ret<0)
222         {
223                         /* when we got a signal, we get EINTR. we do not care,
224                            because we check current time in timers anyway. */
225                 if (errno != EINTR)
226                         eDebug("poll made error");
227         }
228 }
229
230 int eMainloop::exec()
231 {
232         if (!loop_level)
233         {
234                 app_quit_now = false;
235                 app_exit_loop = false;
236                 enter_loop();
237         }
238         return retval;
239 }
240
241 void eMainloop::enter_loop()
242 {
243         loop_level++;
244         // Status der vorhandenen Loop merken
245         bool old_exit_loop = app_exit_loop;
246
247         app_exit_loop = false;
248
249         while (!app_exit_loop && !app_quit_now)
250                 processOneEvent();
251
252         // wiederherstellen der vorherigen app_exit_loop
253         app_exit_loop = old_exit_loop;
254
255         --loop_level;
256
257         if (!loop_level)
258         {
259                 // do something here on exit the last loop
260         }
261 }
262
263 void eMainloop::exit_loop()  // call this to leave the current loop
264 {
265         app_exit_loop = true;
266 }
267
268 void eMainloop::quit( int ret )   // call this to leave all loops
269 {
270         retval=ret;
271         app_quit_now = true;
272 }
273
274 inline void eMainloop::doRecalcTimers()
275 {
276         singleLock s(recalcLock);
277         if ( timer_offset )
278         {
279                 for (ePtrList<eTimer>::iterator it(TimerList); it != TimerList.end(); ++it )
280                         it->recalc( timer_offset );
281                 timer_offset=0;
282         }
283 }
284
285 eApplication* eApp = 0;