add possibility to have menu entries from a plugin
[enigma2.git] / e2reactor.py
1 # enigma2 reactor: based on pollreactor, which is
2 # Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5
6 """
7 Maintainer: U{Felix Domke<mailto:tmbinc@elitedvb.net>}
8 """
9
10 # System imports
11 import select, errno, sys
12
13 # Twisted imports
14 from twisted.python import log, threadable, failure
15 from twisted.internet import main, posixbase, error
16 #from twisted.internet.pollreactor import PollReactor, poller
17
18 from enigma import getApplication
19
20 # globals
21 reads = {}
22 writes = {}
23 selectables = {}
24
25 POLL_DISCONNECTED = (select.POLLHUP | select.POLLERR | select.POLLNVAL)
26
27 class E2SharedPoll:
28         def __init__(self):
29                 self.dict = { }
30
31         def register(self, fd, eventmask = select.POLLIN | select.POLLERR | select.POLLOUT):
32                 self.dict[fd] = eventmask
33         
34         def unregister(self, fd):
35                 del self.dict[fd]
36         
37         def poll(self, timeout = None):
38                 r = getApplication().poll(timeout, self.dict)
39                 return r
40
41 poller = E2SharedPoll()
42
43 class PollReactor(posixbase.PosixReactorBase):
44         """A reactor that uses poll(2)."""
45
46         def _updateRegistration(self, fd):
47                 """Register/unregister an fd with the poller."""
48                 try:
49                         poller.unregister(fd)
50                 except KeyError:
51                         pass
52
53                 mask = 0
54                 if reads.has_key(fd): mask = mask | select.POLLIN
55                 if writes.has_key(fd): mask = mask | select.POLLOUT
56                 if mask != 0:
57                         poller.register(fd, mask)
58                 else:
59                         if selectables.has_key(fd): del selectables[fd]
60                 
61                 getApplication().interruptPoll()
62
63         def _dictRemove(self, selectable, mdict):
64                 try:
65                         # the easy way
66                         fd = selectable.fileno()
67                         # make sure the fd is actually real.  In some situations we can get
68                         # -1 here.
69                         mdict[fd]
70                 except:
71                         # the hard way: necessary because fileno() may disappear at any
72                         # moment, thanks to python's underlying sockets impl
73                         for fd, fdes in selectables.items():
74                                 if selectable is fdes:
75                                         break
76                         else:
77                                 # Hmm, maybe not the right course of action?  This method can't
78                                 # fail, because it happens inside error detection...
79                                 return
80                 if mdict.has_key(fd):
81                         del mdict[fd]
82                         self._updateRegistration(fd)
83
84         def addReader(self, reader):
85                 """Add a FileDescriptor for notification of data available to read.
86                 """
87                 fd = reader.fileno()
88                 if not reads.has_key(fd):
89                         selectables[fd] = reader
90                         reads[fd] =  1
91                         self._updateRegistration(fd)
92
93         def addWriter(self, writer, writes=writes, selectables=selectables):
94                 """Add a FileDescriptor for notification of data available to write.
95                 """
96                 fd = writer.fileno()
97                 if not writes.has_key(fd):
98                         selectables[fd] = writer
99                         writes[fd] =  1
100                         self._updateRegistration(fd)
101
102         def removeReader(self, reader, reads=reads):
103                 """Remove a Selectable for notification of data available to read.
104                 """
105                 return self._dictRemove(reader, reads)
106
107         def removeWriter(self, writer, writes=writes):
108                 """Remove a Selectable for notification of data available to write.
109                 """
110                 return self._dictRemove(writer, writes)
111
112         def removeAll(self, reads=reads, writes=writes, selectables=selectables):
113                 """Remove all selectables, and return a list of them."""
114                 if self.waker is not None:
115                         self.removeReader(self.waker)
116                 result = selectables.values()
117                 fds = selectables.keys()
118                 reads.clear()
119                 writes.clear()
120                 selectables.clear()
121                 for fd in fds:
122                         poller.unregister(fd)
123                         
124                 if self.waker is not None:
125                         self.addReader(self.waker)
126                 return result
127
128         def doPoll(self, timeout,
129                            reads=reads,
130                            writes=writes,
131                            selectables=selectables,
132                            select=select,
133                            log=log,
134                            POLLIN=select.POLLIN,
135                            POLLOUT=select.POLLOUT):
136                 """Poll the poller for new events."""
137                 
138                 if timeout is not None:
139                         timeout = int(timeout * 1000) # convert seconds to milliseconds
140
141                 try:
142                         l = poller.poll(timeout)
143                         if l is None:
144                                 if self.running:
145                                         self.stop()
146                                 l = [ ]
147                 except select.error, e:
148                         if e[0] == errno.EINTR:
149                                 return
150                         else:
151                                 raise
152                 _drdw = self._doReadOrWrite
153                 for fd, event in l:
154                         try:
155                                 selectable = selectables[fd]
156                         except KeyError:
157                                 # Handles the infrequent case where one selectable's
158                                 # handler disconnects another.
159                                 continue
160                         log.callWithLogger(selectable, _drdw, selectable, fd, event, POLLIN, POLLOUT, log)
161
162         doIteration = doPoll
163
164         def _doReadOrWrite(self, selectable, fd, event, POLLIN, POLLOUT, log, 
165                 faildict={
166                         error.ConnectionDone: failure.Failure(error.ConnectionDone()),
167                         error.ConnectionLost: failure.Failure(error.ConnectionLost())
168                 }):
169                 why = None
170                 inRead = False
171                 if event & POLL_DISCONNECTED and not (event & POLLIN):
172                         why = main.CONNECTION_LOST
173                 else:
174                         try:
175                                 if event & POLLIN:
176                                         why = selectable.doRead()
177                                         inRead = True
178                                 if not why and event & POLLOUT:
179                                         why = selectable.doWrite()
180                                         inRead = False
181                                 if not selectable.fileno() == fd:
182                                         why = error.ConnectionFdescWentAway('Filedescriptor went away')
183                                         inRead = False
184                         except:
185                                 log.deferr()
186                                 why = sys.exc_info()[1]
187                 if why:
188                         self._disconnectSelectable(selectable, why, inRead)
189
190         def callLater(self, *args, **kwargs):
191                 getApplication().interruptPoll()
192                 return posixbase.PosixReactorBase.callLater(self, *args, **kwargs)
193
194 def install():
195         """Install the poll() reactor."""
196         
197         p = PollReactor()
198         main.installReactor(p)
199
200 __all__ = ["PollReactor", "install"]