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