add pcr handling
[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
9 eSocketNotifier::eSocketNotifier(eMainloop *context, int fd, int requested, bool startnow): context(*context), fd(fd), state(0), requested(requested)
10 {
11         if (startnow)   
12                 start();
13 }
14
15 eSocketNotifier::~eSocketNotifier()
16 {
17         stop();
18 }
19
20 void eSocketNotifier::start()
21 {
22         if (state)
23                 stop();
24
25         context.addSocketNotifier(this);
26         state=1;
27 }
28
29 void eSocketNotifier::stop()
30 {
31         if (state)
32                 context.removeSocketNotifier(this);
33
34         state=0;
35 }
36
37                                         // timer
38 void eTimer::start(long msek, bool singleShot)
39 {
40         if (bActive)
41                 stop();
42
43         bActive = true;
44         bSingleShot = singleShot;
45         interval = msek;
46         gettimeofday(&nextActivation, 0);               
47 //      eDebug("this = %p\nnow sec = %d, usec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_usec, msek);
48         nextActivation += (msek<0 ? 0 : msek);
49 //      eDebug("next Activation sec = %d, usec = %d", nextActivation.tv_sec, nextActivation.tv_usec );
50         context.addTimer(this);
51 }
52
53 void eTimer::stop()
54 {       
55         if (bActive)
56         {
57                 bActive=false;
58                 context.removeTimer(this);
59         }
60 }
61
62 void eTimer::changeInterval(long msek)
63 {
64         if (bActive)  // Timer is running?
65         {
66                 context.removeTimer(this);       // then stop
67                 nextActivation -= interval;  // sub old interval
68         }
69         else
70                 bActive=true;   // then activate Timer
71
72         interval = msek;                                                // set new Interval
73         nextActivation += interval;             // calc nextActivation
74
75         context.addTimer(this);                         // add Timer to context TimerList
76 }
77
78 void eTimer::activate()   // Internal Function... called from eApplication
79 {
80         timeval now;
81         gettimeofday(&now, 0);
82 //      eDebug("this = %p\nnow sec = %d, usec = %d\nnextActivation sec = %d, usec = %d", this, now.tv_sec, now.tv_usec, nextActivation.tv_sec, nextActivation.tv_usec );
83 //      eDebug("Timer emitted");
84         context.removeTimer(this);
85
86         if (!bSingleShot)
87         {
88                 nextActivation += interval;
89                 context.addTimer(this);
90         }
91         else
92                 bActive=false;
93
94         /*emit*/ timeout();
95 }
96
97 // mainloop
98
99 void eMainloop::addSocketNotifier(eSocketNotifier *sn)
100 {
101         notifiers.insert(std::pair<int,eSocketNotifier*> (sn->getFD(), sn));
102 }
103
104 void eMainloop::removeSocketNotifier(eSocketNotifier *sn)
105 {
106         notifiers.erase(sn->getFD());
107 }
108
109 void eMainloop::processOneEvent()
110 {
111                 /* notes:
112                   - we should use epoll(4)
113                   - timer are checked twice. there was a strong reason for it, but i can't remember. (FIXME)
114                   - for each time, we gettimeofday() and check wether the timer should fire.
115                     we should do this all better - we know how long the poll last, so we know which
116                     timers should fire. Problem is that a timer handler could have required so
117                     much time that another timer fired.
118                     
119                     A probably structure could look
120                     
121                     while (1)
122                     {
123                             time = gettimeofday()
124                             timeout = calculate_pending_timers(time);
125                     
126                       doPoll(timeout or infinite);
127                         
128                         if (poll_had_results)
129                                 handle_poll_handler();
130                         else
131                                     fire_timers(time + timeout)
132                           }
133                           
134                           the gettimeofday() call is required because fire_timers could last more
135                           than nothing.
136                           
137                           when poll did no timeout, we don't handle timers, as this will be done
138                           in the next iteration (without adding overhead - we had to get the new
139                           time anyway
140                 */
141
142                 // first, process pending timers...
143         long usec=0;
144
145         while (TimerList && (usec = timeout_usec( TimerList.begin()->getNextActivation() ) ) <= 0 )
146                 TimerList.begin()->activate();
147
148                 // build the poll aray
149         int fdAnz = notifiers.size();
150         pollfd* pfd = new pollfd[fdAnz];  // make new pollfd array
151
152         std::map<int,eSocketNotifier*>::iterator it(notifiers.begin());
153         for (int i=0; i < fdAnz; i++, it++)
154         {
155                 pfd[i].fd = it->first;
156                 pfd[i].events = it->second->getRequested();
157         }
158
159                 // to the poll. When there are no timers, we have an infinite timeout
160         int ret=poll(pfd, fdAnz, TimerList ? usec / 1000 : -1);  // convert to us
161
162         if (ret>0)
163         {
164                 for (int i=0; i < fdAnz ; i++)
165                 {
166                         if( notifiers.find(pfd[i].fd) == notifiers.end())
167                                 continue;
168
169                         int req = notifiers[pfd[i].fd]->getRequested();
170
171                         if ( pfd[i].revents & req )
172                         {
173                                 notifiers[pfd[i].fd]->activate(pfd[i].revents);
174
175                                 if (!--ret)
176                                         break;
177                         } else if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
178                                 eFatal("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d) -> FIX YOUR CODE", pfd[i].fd,pfd[i].revents);
179                 }
180         } else if (ret<0)
181         {
182                         /* when we got a signal, we get EINTR. we do not care, 
183                            because we check current time in timers anyway. */
184                 if (errno != EINTR)
185                         eDebug("poll made error (%m)");
186         } 
187
188                 // check timer...
189         while ( TimerList && timeout_usec( TimerList.begin()->getNextActivation() ) <= 0 )
190                 TimerList.begin()->activate();
191
192         delete [] pfd;
193 }
194
195
196 int eMainloop::exec()
197 {
198         if (!loop_level)
199         {
200                 app_quit_now = false;
201                 enter_loop();
202         }
203         return retval;
204 }
205
206         /* use with care! better: don't use it anymore. it was used for gui stuff, but 
207                  doesn't allow multiple paths (or active dialogs, if you want it that way.) */
208 void eMainloop::enter_loop()
209 {
210         loop_level++;
211
212         // Status der vorhandenen Loop merken
213         bool old_exit_loop = app_exit_loop;
214         
215         app_exit_loop = false;
216
217         while (!app_exit_loop && !app_quit_now)
218         {
219                 processOneEvent();
220         }
221
222         // wiederherstellen der vorherigen app_exit_loop
223         app_exit_loop = old_exit_loop;
224
225         loop_level--;
226
227         if (!loop_level)
228         {
229                         // do something here on exit the last loop
230         }
231 }
232
233 void eMainloop::exit_loop()  // call this to leave the current loop
234 {
235         app_exit_loop = true;   
236 }
237
238 void eMainloop::quit( int ret )   // call this to leave all loops
239 {
240         retval=ret;
241         app_quit_now = true;
242 }
243
244 eApplication* eApp = 0;