fix dvd hotplug: stop assuming dvd for every other medium. in case of multiple ISO...
[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 #include <lib/gdi/grc.h>
10
11 eSocketNotifier::eSocketNotifier(eMainloop *context, int fd, int requested, bool startnow): context(*context), fd(fd), state(0), requested(requested)
12 {
13         if (startnow)   
14                 start();
15 }
16
17 eSocketNotifier::~eSocketNotifier()
18 {
19         stop();
20 }
21
22 void eSocketNotifier::start()
23 {
24         if (state)
25                 stop();
26
27         context.addSocketNotifier(this);
28         state=2;  // running but not in poll yet
29 }
30
31 void eSocketNotifier::stop()
32 {
33         if (state)
34                 context.removeSocketNotifier(this);
35
36         state=0;
37 }
38
39                                         // timer
40 void eTimer::start(long msek, bool singleShot)
41 {
42         if (bActive)
43                 stop();
44
45         bActive = true;
46         bSingleShot = singleShot;
47         interval = msek;
48         clock_gettime(CLOCK_MONOTONIC, &nextActivation);
49 //      eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d msec", this, nextActivation.tv_sec, nextActivation.tv_nsec, msek);
50         nextActivation += (msek<0 ? 0 : msek);
51 //      eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec );
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         clock_gettime(CLOCK_MONOTONIC, &nextActivation);
63 //      eDebug("this = %p\nnow sec = %d, nsec = %d\nadd %d sec", this, nextActivation.tv_sec, nextActivation.tv_nsec, seconds);
64         if ( seconds > 0 )
65                 nextActivation.tv_sec += seconds;
66 //      eDebug("next Activation sec = %d, nsec = %d", nextActivation.tv_sec, nextActivation.tv_nsec );
67         context.addTimer(this);
68 }
69
70 void eTimer::stop()
71 {
72         if (bActive)
73         {
74                 bActive=false;
75                 context.removeTimer(this);
76         }
77 }
78
79 void eTimer::changeInterval(long msek)
80 {
81         if (bActive)  // Timer is running?
82         {
83                 context.removeTimer(this);       // then stop
84                 nextActivation -= interval;  // sub old interval
85         }
86         else
87                 bActive=true; // then activate Timer
88
89         interval = msek;                                                // set new Interval
90         nextActivation += interval;             // calc nextActivation
91
92         context.addTimer(this);                         // add Timer to context TimerList
93 }
94
95 void eTimer::activate()   // Internal Funktion... called from eApplication
96 {
97         context.removeTimer(this);
98
99         if (!bSingleShot)
100         {
101                 nextActivation += interval;
102                 context.addTimer(this);
103         }
104         else
105                 bActive=false;
106
107         /*emit*/ timeout();
108 }
109
110 // mainloop
111 ePtrList<eMainloop> eMainloop::existing_loops;
112
113 eMainloop::~eMainloop()
114 {
115         existing_loops.remove(this);
116         for (std::map<int, eSocketNotifier*>::iterator it(notifiers.begin());it != notifiers.end();++it)
117                 it->second->stop();
118         while(m_timer_list.begin() != m_timer_list.end())
119                 m_timer_list.begin()->stop();
120 }
121
122 void eMainloop::addSocketNotifier(eSocketNotifier *sn)
123 {
124         int fd = sn->getFD();
125         ASSERT(notifiers.find(fd) == notifiers.end());
126         notifiers[fd]=sn;
127 }
128
129 void eMainloop::removeSocketNotifier(eSocketNotifier *sn)
130 {
131         int fd = sn->getFD();
132         std::map<int,eSocketNotifier*>::iterator i(notifiers.find(fd));
133         if (i != notifiers.end())
134                 return notifiers.erase(i);
135         eFatal("removed socket notifier which is not present");
136 }
137
138 int eMainloop::processOneEvent(unsigned int twisted_timeout, PyObject **res, ePyObject additional)
139 {
140         int return_reason = 0;
141                 /* get current time */
142
143         if (additional && !PyDict_Check(additional))
144                 eFatal("additional, but it's not dict");
145
146         if (additional && !res)
147                 eFatal("additional, but no res");
148
149         long poll_timeout = -1; /* infinite in case of empty timer list */
150
151         if (!m_timer_list.empty())
152         {
153                 /* process all timers which are ready. first remove them out of the list. */
154                 while (!m_timer_list.empty() && (poll_timeout = timeout_usec( m_timer_list.begin()->getNextActivation() ) ) <= 0 )
155                         m_timer_list.begin()->activate();
156                 if (poll_timeout < 0)
157                         poll_timeout = 0;
158                 else /* convert us to ms */
159                         poll_timeout /= 1000;
160         }
161
162         if ((twisted_timeout > 0) && (poll_timeout > 0) && ((unsigned int)poll_timeout > twisted_timeout))
163         {
164                 poll_timeout = twisted_timeout;
165                 return_reason = 1;
166         }
167
168         int nativecount=notifiers.size(),
169                 fdcount=nativecount,
170                 ret=0;
171
172         if (additional)
173                 fdcount += PyDict_Size(additional);
174
175                 // build the poll aray
176         pollfd pfd[fdcount];  // make new pollfd array
177         std::map<int,eSocketNotifier*>::iterator it = notifiers.begin();
178         int i=0;
179         for (; i < nativecount; ++i, ++it)
180         {
181                 it->second->state = 1; // running and in poll
182                 pfd[i].fd = it->first;
183                 pfd[i].events = it->second->getRequested();
184         }
185
186         if (additional)
187         {
188 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
189                 typedef int Py_ssize_t;
190 # define PY_SSIZE_T_MAX INT_MAX
191 # define PY_SSIZE_T_MIN INT_MIN
192 #endif
193                 PyObject *key, *val;
194                 Py_ssize_t pos=0;
195                 while (PyDict_Next(additional, &pos, &key, &val)) {
196                         pfd[i].fd = PyObject_AsFileDescriptor(key);
197                         pfd[i++].events = PyInt_AsLong(val);
198                 }
199         }
200
201         m_is_idle = 1;
202
203         if (this == eApp)
204         {
205                 gOpcode op;
206                 op.dc = 0;
207                 op.opcode = gOpcode::flush;
208                 gRC::getInstance()->submit(op);
209                 Py_BEGIN_ALLOW_THREADS
210                 ret = ::poll(pfd, fdcount, poll_timeout);
211                 Py_END_ALLOW_THREADS
212                 
213         } else
214                 ret = ::poll(pfd, fdcount, poll_timeout);
215
216         m_is_idle = 0;
217
218                         /* ret > 0 means that there are some active poll entries. */
219         if (ret > 0)
220         {
221                 int i=0;
222                 return_reason = 0;
223                 for (; i < nativecount; ++i)
224                 {
225                         if (pfd[i].revents)
226                         {
227                                 it = notifiers.find(pfd[i].fd);
228                                 if (it != notifiers.end()
229                                         && it->second->state == 1) // added and in poll
230                                 {
231                                         int req = it->second->getRequested();
232                                         if (pfd[i].revents & req)
233                                                 it->second->activate(pfd[i].revents & req);
234                                         pfd[i].revents &= ~req;
235                                 }
236                                 if (pfd[i].revents & (POLLERR|POLLHUP|POLLNVAL))
237                                         eDebug("poll: unhandled POLLERR/HUP/NVAL for fd %d(%d)", pfd[i].fd, pfd[i].revents);
238                         }
239                 }
240                 for (; i < fdcount; ++i)
241                 {
242                         if (pfd[i].revents)
243                         {
244                                 if (!*res)
245                                         *res = PyList_New(0);
246                                 ePyObject it = PyTuple_New(2);
247                                 PyTuple_SET_ITEM(it, 0, PyInt_FromLong(pfd[i].fd));
248                                 PyTuple_SET_ITEM(it, 1, PyInt_FromLong(pfd[i].revents));
249                                 PyList_Append(*res, it);
250                                 Py_DECREF(it);
251                         }
252                 }
253         }
254         else if (ret < 0)
255         {
256                         /* when we got a signal, we get EINTR. */
257                 if (errno != EINTR)
258                         eDebug("poll made error (%m)");
259                 else
260                         return_reason = 2; /* don't assume the timeout has passed when we got a signal */
261         }
262
263         return return_reason;
264 }
265
266 void eMainloop::addTimer(eTimer* e)
267 {
268         m_timer_list.insert_in_order(e);
269 }
270
271 void eMainloop::removeTimer(eTimer* e)
272 {
273         m_timer_list.remove(e);
274 }
275
276 int eMainloop::iterate(unsigned int twisted_timeout, PyObject **res, ePyObject dict)
277 {
278         int ret = 0;
279
280         if (twisted_timeout)
281         {
282                 clock_gettime(CLOCK_MONOTONIC, &m_twisted_timer);
283                 m_twisted_timer += twisted_timeout;
284         }
285
286                 /* TODO: this code just became ugly. fix that. */
287         do
288         {
289                 if (m_interrupt_requested)
290                 {
291                         m_interrupt_requested = 0;
292                         return 0;
293                 }
294
295                 if (app_quit_now)
296                         return -1;
297
298                 int to = 0;
299                 if (twisted_timeout)
300                 {
301                         timespec now, timeout;
302                         clock_gettime(CLOCK_MONOTONIC, &now);
303                         if (m_twisted_timer<=now) // timeout
304                                 return 0;
305                         timeout = m_twisted_timer - now;
306                         to = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000;
307                 }
308                 ret = processOneEvent(to, res, dict);
309         } while ( !ret && !(res && *res) );
310
311         return ret;
312 }
313
314 int eMainloop::runLoop()
315 {
316         while (!app_quit_now)
317                 iterate();
318         return retval;
319 }
320
321 void eMainloop::reset()
322 {
323         app_quit_now=false;
324 }
325
326 PyObject *eMainloop::poll(ePyObject timeout, ePyObject dict)
327 {
328         PyObject *res=0;
329
330         if (app_quit_now)
331                 Py_RETURN_NONE;
332
333         int twisted_timeout = (timeout == Py_None) ? 0 : PyInt_AsLong(timeout);
334
335         iterate(twisted_timeout, &res, dict);
336         if (res)
337                 return res;
338
339         return PyList_New(0); /* return empty list on timeout */
340 }
341
342 void eMainloop::interruptPoll()
343 {
344         m_interrupt_requested = 1;
345 }
346
347 void eMainloop::quit(int ret)
348 {
349         retval = ret;
350         app_quit_now = true;
351 }
352
353 eApplication* eApp = 0;
354
355 #include "structmember.h"
356
357 extern "C" {
358
359 // eTimer replacement
360
361 struct eTimerPy
362 {
363         PyObject_HEAD
364         eTimer *tm;
365         PyObject *in_weakreflist; /* List of weak references */
366 };
367
368 static int
369 eTimerPy_traverse(eTimerPy *self, visitproc visit, void *arg)
370 {
371         PyObject *obj = self->tm->timeout.get();
372         Py_VISIT(obj);
373         return 0;
374 }
375
376 static int
377 eTimerPy_clear(eTimerPy *self)
378 {
379         PyObject *obj = self->tm->timeout.get();
380         Py_CLEAR(obj);
381         return 0;
382 }
383
384 static void
385 eTimerPy_dealloc(eTimerPy* self)
386 {
387         if (self->in_weakreflist != NULL)
388                 PyObject_ClearWeakRefs((PyObject *) self);
389         eTimerPy_clear(self);
390         delete self->tm;
391         self->ob_type->tp_free((PyObject*)self);
392 }
393
394 static PyObject *
395 eTimerPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
396 {
397         eTimerPy *self = (eTimerPy *)type->tp_alloc(type, 0);
398         self->tm = new eTimer(eApp);
399         self->in_weakreflist = NULL;
400         return (PyObject *)self;
401 }
402
403 static PyObject *
404 eTimerPy_is_active(eTimerPy* self)
405 {
406         PyObject *ret = NULL;
407         ret = self->tm->isActive() ? Py_True : Py_False;
408         Org_Py_INCREF(ret);
409         return ret;
410 }
411
412 static PyObject *
413 eTimerPy_start(eTimerPy* self, PyObject *args)
414 {
415         long v=0;
416         long singleShot=0;
417         if (PyTuple_Size(args) > 1)
418         {
419                 if (!PyArg_ParseTuple(args, "ll", &v, &singleShot)) // when 2nd arg is a value
420                 {
421                         PyObject *obj=0;
422                         if (!PyArg_ParseTuple(args, "lO", &v, &obj)) // get 2nd arg as python object
423                                 return NULL;
424                         else if (obj == Py_True)
425                                 singleShot=1;
426                         else if (obj != Py_False)
427                                 return NULL;
428                 }
429         }
430         else if (!PyArg_ParseTuple(args, "l", &v))
431                 return NULL;
432         self->tm->start(v, singleShot);
433         Py_RETURN_NONE;
434 }
435
436 static PyObject *
437 eTimerPy_start_long(eTimerPy* self, PyObject *args)
438 {
439         long v=0;
440         if (!PyArg_ParseTuple(args, "l", &v)) {
441                 return NULL;
442         }
443         self->tm->startLongTimer(v);
444         Py_RETURN_NONE;
445 }
446
447 static PyObject *
448 eTimerPy_change_interval(eTimerPy* self, PyObject *args)
449 {
450         long v=0;
451         if (!PyArg_ParseTuple(args, "l", &v)) {
452                 return NULL;
453         }
454         self->tm->changeInterval(v);
455         Py_RETURN_NONE;
456 }
457
458 static PyObject *
459 eTimerPy_stop(eTimerPy* self)
460 {
461         self->tm->stop();
462         Py_RETURN_NONE;
463 }
464
465 static PyObject *
466 eTimerPy_get_callback_list(eTimerPy *self)
467 { //used for compatibilty with the old eTimer
468         return self->tm->timeout.get();
469 }
470
471 static PyMethodDef eTimerPy_methods[] = {
472         {"isActive", (PyCFunction)eTimerPy_is_active, METH_NOARGS,
473          "returns the timer state"
474         },
475         {"start", (PyCFunction)eTimerPy_start, METH_VARARGS,
476          "start timer with interval in msecs"
477         },
478         {"startLongTimer", (PyCFunction)eTimerPy_start_long, METH_VARARGS,
479          "start timer with interval in secs"
480         },
481         {"changeInterval", (PyCFunction)eTimerPy_change_interval, METH_VARARGS,
482          "change interval of a timer (in msecs)"
483         },
484         {"stop", (PyCFunction)eTimerPy_stop, METH_NOARGS,
485          "stops the timer"
486         },
487         //used for compatibilty with the old eTimer
488         {"get", (PyCFunction)eTimerPy_get_callback_list, METH_NOARGS,
489          "get timeout callback list"
490         },
491         {NULL}  /* Sentinel */
492 };
493
494 static PyObject *
495 eTimerPy_get_cb_list(eTimerPy *self, void *closure)
496 {
497         return self->tm->timeout.get();
498 }
499
500 static PyObject *
501 eTimerPy_timeout(eTimerPy *self, void *closure) 
502 { //used for compatibilty with the old eTimer
503         Org_Py_INCREF((PyObject*)self);
504         return (PyObject*)self;
505 }
506
507 static PyGetSetDef eTimerPy_getseters[] = {
508         {"callback",
509          (getter)eTimerPy_get_cb_list, (setter)0,
510          "returns the callback python list",
511          NULL},
512
513         {"timeout", //used for compatibilty with the old eTimer
514          (getter)eTimerPy_timeout, (setter)0,
515          "synonym for our self",
516          NULL},
517
518         {NULL} /* Sentinel */
519 };
520
521 static PyTypeObject eTimerPyType = {
522         PyObject_HEAD_INIT(NULL)
523         0, /*ob_size*/
524         "eBaseImpl.eTimer", /*tp_name*/
525         sizeof(eTimerPy), /*tp_basicsize*/
526         0, /*tp_itemsize*/
527         (destructor)eTimerPy_dealloc, /*tp_dealloc*/
528         0, /*tp_print*/
529         0, /*tp_getattr*/
530         0, /*tp_setattr*/
531         0, /*tp_compare*/
532         0, /*tp_repr*/
533         0, /*tp_as_number*/
534         0, /*tp_as_sequence*/
535         0, /*tp_as_mapping*/
536         0, /*tp_hash */
537         0, /*tp_call*/
538         0, /*tp_str*/
539         0, /*tp_getattro*/
540         0, /*tp_setattro*/
541         0, /*tp_as_buffer*/
542         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
543         "eTimer objects", /* tp_doc */
544         (traverseproc)eTimerPy_traverse, /* tp_traverse */
545         (inquiry)eTimerPy_clear, /* tp_clear */
546         0, /* tp_richcompare */
547         offsetof(eTimerPy, in_weakreflist), /* tp_weaklistoffset */
548         0, /* tp_iter */
549         0, /* tp_iternext */
550         eTimerPy_methods, /* tp_methods */
551         0, /* tp_members */
552         eTimerPy_getseters, /* tp_getset */
553         0, /* tp_base */
554         0, /* tp_dict */
555         0, /* tp_descr_get */
556         0, /* tp_descr_set */
557         0, /* tp_dictoffset */
558         0, /* tp_init */
559         0, /* tp_alloc */
560         eTimerPy_new, /* tp_new */
561 };
562
563 // eSocketNotifier replacement
564
565 struct eSocketNotifierPy
566 {
567         PyObject_HEAD
568         eSocketNotifier *sn;
569         PyObject *in_weakreflist; /* List of weak references */
570 };
571
572 static int
573 eSocketNotifierPy_traverse(eSocketNotifierPy *self, visitproc visit, void *arg)
574 {
575         PyObject *obj = self->sn->activated.get();
576         Py_VISIT(obj);
577         return 0;
578 }
579
580 static int
581 eSocketNotifierPy_clear(eSocketNotifierPy *self)
582 {
583         PyObject *obj = self->sn->activated.get();
584         Py_CLEAR(obj);
585         return 0;
586 }
587
588 static void
589 eSocketNotifierPy_dealloc(eSocketNotifierPy* self)
590 {
591         if (self->in_weakreflist != NULL)
592                 PyObject_ClearWeakRefs((PyObject *) self);
593         eSocketNotifierPy_clear(self);
594         delete self->sn;
595         self->ob_type->tp_free((PyObject*)self);
596 }
597
598 static PyObject *
599 eSocketNotifierPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
600 {
601         eSocketNotifierPy *self = (eSocketNotifierPy *)type->tp_alloc(type, 0);
602         int fd, req, immediate_start = 1, size = PyTuple_Size(args);
603         if (size > 2)
604         {
605                 if (!PyArg_ParseTuple(args, "iii", &fd, &req, &immediate_start))
606                 {
607                         PyObject *obj = NULL;
608                         if (!PyArg_ParseTuple(args, "iiO", &fd, &req, &immediate_start))
609                                 return NULL;
610                         if (obj == Py_False)
611                                 immediate_start = 0;
612                         else if (obj != Py_True)
613                                 return NULL;
614                 }
615         }
616         else if (size < 2 || !PyArg_ParseTuple(args, "ii", &fd, &req))
617                 return NULL;
618         self->sn = new eSocketNotifier(eApp, fd, req, immediate_start);
619         self->in_weakreflist = NULL;
620         return (PyObject *)self;
621 }
622
623 static PyObject *
624 eSocketNotifierPy_is_running(eSocketNotifierPy* self)
625 {
626         PyObject *ret = self->sn->isRunning() ? Py_True : Py_False;
627         Org_Py_INCREF(ret);
628         return ret;
629 }
630
631 static PyObject *
632 eSocketNotifierPy_start(eSocketNotifierPy* self)
633 {
634         self->sn->start();
635         Py_RETURN_NONE;
636 }
637
638 static PyObject *
639 eSocketNotifierPy_stop(eSocketNotifierPy* self)
640 {
641         self->sn->stop();
642         Py_RETURN_NONE;
643 }
644
645 static PyObject *
646 eSocketNotifierPy_get_fd(eSocketNotifierPy* self)
647 {
648         return PyInt_FromLong(self->sn->getFD());
649 }
650
651 static PyObject *
652 eSocketNotifierPy_get_requested(eSocketNotifierPy* self)
653 {
654         return PyInt_FromLong(self->sn->getRequested());
655 }
656
657 static PyObject *
658 eSocketNotifierPy_set_requested(eSocketNotifierPy* self, PyObject *args)
659 {
660         int req;
661         if (PyTuple_Size(args) != 1 || !PyArg_ParseTuple(args, "i", &req))
662                 return NULL;
663         self->sn->setRequested(req);
664         Py_RETURN_NONE;
665 }
666
667 static PyMethodDef eSocketNotifierPy_methods[] = {
668         {"isRunning", (PyCFunction)eSocketNotifierPy_is_running, METH_NOARGS,
669          "returns the running state"
670         },
671         {"start", (PyCFunction)eSocketNotifierPy_start, METH_NOARGS,
672          "start the sn"
673         },
674         {"stop", (PyCFunction)eSocketNotifierPy_stop, METH_NOARGS,
675          "stops the sn"
676         },
677         {"getFD", (PyCFunction)eSocketNotifierPy_get_fd, METH_NOARGS,
678          "get file descriptor"
679         },
680         {"getRequested", (PyCFunction)eSocketNotifierPy_get_requested, METH_NOARGS,
681          "get requested"
682         },
683         {"setRequested", (PyCFunction)eSocketNotifierPy_set_requested, METH_VARARGS,
684          "set requested"
685         },
686         {NULL}  /* Sentinel */
687 };
688
689 static PyObject *
690 eSocketNotifierPy_get_cb_list(eSocketNotifierPy *self, void *closure)
691 {
692         return self->sn->activated.get();
693 }
694
695 static PyGetSetDef eSocketNotifierPy_getseters[] = {
696         {"callback",
697          (getter)eSocketNotifierPy_get_cb_list, (setter)0,
698          "returns the callback python list",
699          NULL},
700         {NULL} /* Sentinel */
701 };
702
703 static PyTypeObject eSocketNotifierPyType = {
704         PyObject_HEAD_INIT(NULL)
705         0, /*ob_size*/
706         "eBaseImpl.eSocketNotifier", /*tp_name*/
707         sizeof(eSocketNotifierPy), /*tp_basicsize*/
708         0, /*tp_itemsize*/
709         (destructor)eSocketNotifierPy_dealloc, /*tp_dealloc*/
710         0, /*tp_print*/
711         0, /*tp_getattr*/
712         0, /*tp_setattr*/
713         0, /*tp_compare*/
714         0, /*tp_repr*/
715         0, /*tp_as_number*/
716         0, /*tp_as_sequence*/
717         0, /*tp_as_mapping*/
718         0, /*tp_hash */
719         0, /*tp_call*/
720         0, /*tp_str*/
721         0, /*tp_getattro*/
722         0, /*tp_setattro*/
723         0, /*tp_as_buffer*/
724         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
725         "eTimer objects", /* tp_doc */
726         (traverseproc)eSocketNotifierPy_traverse, /* tp_traverse */
727         (inquiry)eSocketNotifierPy_clear, /* tp_clear */
728         0, /* tp_richcompare */
729         offsetof(eSocketNotifierPy, in_weakreflist), /* tp_weaklistoffset */
730         0, /* tp_iter */
731         0, /* tp_iternext */
732         eSocketNotifierPy_methods, /* tp_methods */
733         0, /* tp_members */
734         eSocketNotifierPy_getseters, /* tp_getset */
735         0, /* tp_base */
736         0, /* tp_dict */
737         0, /* tp_descr_get */
738         0, /* tp_descr_set */
739         0, /* tp_dictoffset */
740         0, /* tp_init */
741         0, /* tp_alloc */
742         eSocketNotifierPy_new, /* tp_new */
743 };
744
745 static PyMethodDef module_methods[] = {
746         {NULL}  /* Sentinel */
747 };
748
749 void eBaseInit(void)
750 {
751         PyObject* m;
752
753         m = Py_InitModule3("eBaseImpl", module_methods,
754                 "Module that implements some enigma classes with working cyclic garbage collection.");
755
756         if (m == NULL)
757                 return;
758
759         if (!PyType_Ready(&eTimerPyType))
760         {
761                 Org_Py_INCREF((PyObject*)&eTimerPyType);
762                 PyModule_AddObject(m, "eTimer", (PyObject*)&eTimerPyType);
763         }
764         if (!PyType_Ready(&eSocketNotifierPyType))
765         {
766                 Org_Py_INCREF((PyObject*)&eSocketNotifierPyType);
767                 PyModule_AddObject(m, "eSocketNotifier", (PyObject*)&eSocketNotifierPyType);
768         }
769 }
770 }