e7708633da3544a0655a475cae8dc0fe03c91b35
[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(true);
372         if (obj) {
373                 Py_VISIT(obj);
374         }
375         return 0;
376 }
377
378 static int
379 eTimerPy_clear(eTimerPy *self)
380 {
381         PyObject *obj = self->tm->timeout.get(true);
382         if (obj)
383                 Py_CLEAR(obj);
384         return 0;
385 }
386
387 static void
388 eTimerPy_dealloc(eTimerPy* self)
389 {
390         if (self->in_weakreflist != NULL)
391                 PyObject_ClearWeakRefs((PyObject *) self);
392         eTimerPy_clear(self);
393         delete self->tm;
394         self->ob_type->tp_free((PyObject*)self);
395 }
396
397 static PyObject *
398 eTimerPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
399 {
400         eTimerPy *self = (eTimerPy *)type->tp_alloc(type, 0);
401         self->tm = new eTimer(eApp);
402         self->in_weakreflist = NULL;
403         return (PyObject *)self;
404 }
405
406 static PyObject *
407 eTimerPy_is_active(eTimerPy* self)
408 {
409         PyObject *ret = NULL;
410         ret = self->tm->isActive() ? Py_True : Py_False;
411         Org_Py_INCREF(ret);
412         return ret;
413 }
414
415 static PyObject *
416 eTimerPy_start(eTimerPy* self, PyObject *args)
417 {
418         long v=0;
419         long singleShot=0;
420         if (PyTuple_Size(args) > 1)
421         {
422                 if (!PyArg_ParseTuple(args, "ll", &v, &singleShot)) // when 2nd arg is a value
423                 {
424                         PyObject *obj=0;
425                         if (!PyArg_ParseTuple(args, "lO", &v, &obj)) // get 2nd arg as python object
426                                 return NULL;
427                         else if (obj == Py_True)
428                                 singleShot=1;
429                         else if (obj != Py_False)
430                                 return NULL;
431                 }
432         }
433         else if (!PyArg_ParseTuple(args, "l", &v))
434                 return NULL;
435         self->tm->start(v, singleShot);
436         Py_RETURN_NONE;
437 }
438
439 static PyObject *
440 eTimerPy_start_long(eTimerPy* self, PyObject *args)
441 {
442         int v=0;
443         if (!PyArg_ParseTuple(args, "i", &v)) {
444                 return NULL;
445         }
446         self->tm->startLongTimer(v);
447         Py_RETURN_NONE;
448 }
449
450 static PyObject *
451 eTimerPy_change_interval(eTimerPy* self, PyObject *args)
452 {
453         long v=0;
454         if (!PyArg_ParseTuple(args, "l", &v)) {
455                 return NULL;
456         }
457         self->tm->changeInterval(v);
458         Py_RETURN_NONE;
459 }
460
461 static PyObject *
462 eTimerPy_stop(eTimerPy* self)
463 {
464         self->tm->stop();
465         Py_RETURN_NONE;
466 }
467
468 static PyObject *
469 eTimerPy_get_callback_list(eTimerPy *self)
470 { //used for compatibilty with the old eTimer
471         return self->tm->timeout.get();
472 }
473
474 static PyMethodDef eTimerPy_methods[] = {
475         {"isActive", (PyCFunction)eTimerPy_is_active, METH_NOARGS,
476          "returns the timer state"
477         },
478         {"start", (PyCFunction)eTimerPy_start, METH_VARARGS,
479          "start timer with interval in msecs"
480         },
481         {"startLongTimer", (PyCFunction)eTimerPy_start_long, METH_VARARGS,
482          "start timer with interval in secs"
483         },
484         {"changeInterval", (PyCFunction)eTimerPy_change_interval, METH_VARARGS,
485          "change interval of a timer (in msecs)"
486         },
487         {"stop", (PyCFunction)eTimerPy_stop, METH_NOARGS,
488          "stops the timer"
489         },
490         //used for compatibilty with the old eTimer
491         {"get", (PyCFunction)eTimerPy_get_callback_list, METH_NOARGS,
492          "get timeout callback list"
493         },
494         {NULL}  /* Sentinel */
495 };
496
497 static PyObject *
498 eTimerPy_get_cb_list(eTimerPy *self, void *closure)
499 {
500         return self->tm->timeout.get();
501 }
502
503 static PyObject *
504 eTimerPy_timeout(eTimerPy *self, void *closure) 
505 { //used for compatibilty with the old eTimer
506         Org_Py_INCREF((PyObject*)self);
507         return (PyObject*)self;
508 }
509
510 static PyGetSetDef eTimerPy_getseters[] = {
511         {"callback",
512          (getter)eTimerPy_get_cb_list, (setter)0,
513          "returns the callback python list",
514          NULL},
515
516         {"timeout", //used for compatibilty with the old eTimer
517          (getter)eTimerPy_timeout, (setter)0,
518          "synonym for our self",
519          NULL},
520
521         {NULL} /* Sentinel */
522 };
523
524 static PyTypeObject eTimerPyType = {
525         PyObject_HEAD_INIT(NULL)
526         0, /*ob_size*/
527         "eBaseImpl.eTimer", /*tp_name*/
528         sizeof(eTimerPy), /*tp_basicsize*/
529         0, /*tp_itemsize*/
530         (destructor)eTimerPy_dealloc, /*tp_dealloc*/
531         0, /*tp_print*/
532         0, /*tp_getattr*/
533         0, /*tp_setattr*/
534         0, /*tp_compare*/
535         0, /*tp_repr*/
536         0, /*tp_as_number*/
537         0, /*tp_as_sequence*/
538         0, /*tp_as_mapping*/
539         0, /*tp_hash */
540         0, /*tp_call*/
541         0, /*tp_str*/
542         0, /*tp_getattro*/
543         0, /*tp_setattro*/
544         0, /*tp_as_buffer*/
545         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
546         "eTimer objects", /* tp_doc */
547         (traverseproc)eTimerPy_traverse, /* tp_traverse */
548         (inquiry)eTimerPy_clear, /* tp_clear */
549         0, /* tp_richcompare */
550         offsetof(eTimerPy, in_weakreflist), /* tp_weaklistoffset */
551         0, /* tp_iter */
552         0, /* tp_iternext */
553         eTimerPy_methods, /* tp_methods */
554         0, /* tp_members */
555         eTimerPy_getseters, /* tp_getset */
556         0, /* tp_base */
557         0, /* tp_dict */
558         0, /* tp_descr_get */
559         0, /* tp_descr_set */
560         0, /* tp_dictoffset */
561         0, /* tp_init */
562         0, /* tp_alloc */
563         eTimerPy_new, /* tp_new */
564 };
565
566 // eSocketNotifier replacement
567
568 struct eSocketNotifierPy
569 {
570         PyObject_HEAD
571         eSocketNotifier *sn;
572         PyObject *in_weakreflist; /* List of weak references */
573 };
574
575 static int
576 eSocketNotifierPy_traverse(eSocketNotifierPy *self, visitproc visit, void *arg)
577 {
578         PyObject *obj = self->sn->activated.get(true);
579         if (obj)
580                 Py_VISIT(obj);
581         return 0;
582 }
583
584 static int
585 eSocketNotifierPy_clear(eSocketNotifierPy *self)
586 {
587         PyObject *obj = self->sn->activated.get(true);
588         if (obj)
589                 Py_CLEAR(obj);
590         return 0;
591 }
592
593 static void
594 eSocketNotifierPy_dealloc(eSocketNotifierPy* self)
595 {
596         if (self->in_weakreflist != NULL)
597                 PyObject_ClearWeakRefs((PyObject *) self);
598         eSocketNotifierPy_clear(self);
599         delete self->sn;
600         self->ob_type->tp_free((PyObject*)self);
601 }
602
603 static PyObject *
604 eSocketNotifierPy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
605 {
606         eSocketNotifierPy *self = (eSocketNotifierPy *)type->tp_alloc(type, 0);
607         int fd, req, immediate_start = 1, size = PyTuple_Size(args);
608         if (size > 2)
609         {
610                 if (!PyArg_ParseTuple(args, "iii", &fd, &req, &immediate_start))
611                 {
612                         PyObject *obj = NULL;
613                         if (!PyArg_ParseTuple(args, "iiO", &fd, &req, &immediate_start))
614                                 return NULL;
615                         if (obj == Py_False)
616                                 immediate_start = 0;
617                         else if (obj != Py_True)
618                                 return NULL;
619                 }
620         }
621         else if (size < 2 || !PyArg_ParseTuple(args, "ii", &fd, &req))
622                 return NULL;
623         self->sn = new eSocketNotifier(eApp, fd, req, immediate_start);
624         self->in_weakreflist = NULL;
625         return (PyObject *)self;
626 }
627
628 static PyObject *
629 eSocketNotifierPy_is_running(eSocketNotifierPy* self)
630 {
631         PyObject *ret = self->sn->isRunning() ? Py_True : Py_False;
632         Org_Py_INCREF(ret);
633         return ret;
634 }
635
636 static PyObject *
637 eSocketNotifierPy_start(eSocketNotifierPy* self)
638 {
639         self->sn->start();
640         Py_RETURN_NONE;
641 }
642
643 static PyObject *
644 eSocketNotifierPy_stop(eSocketNotifierPy* self)
645 {
646         self->sn->stop();
647         Py_RETURN_NONE;
648 }
649
650 static PyObject *
651 eSocketNotifierPy_get_fd(eSocketNotifierPy* self)
652 {
653         return PyInt_FromLong(self->sn->getFD());
654 }
655
656 static PyObject *
657 eSocketNotifierPy_get_requested(eSocketNotifierPy* self)
658 {
659         return PyInt_FromLong(self->sn->getRequested());
660 }
661
662 static PyObject *
663 eSocketNotifierPy_set_requested(eSocketNotifierPy* self, PyObject *args)
664 {
665         int req;
666         if (PyTuple_Size(args) != 1 || !PyArg_ParseTuple(args, "i", &req))
667                 return NULL;
668         self->sn->setRequested(req);
669         Py_RETURN_NONE;
670 }
671
672 static PyMethodDef eSocketNotifierPy_methods[] = {
673         {"isRunning", (PyCFunction)eSocketNotifierPy_is_running, METH_NOARGS,
674          "returns the running state"
675         },
676         {"start", (PyCFunction)eSocketNotifierPy_start, METH_NOARGS,
677          "start the sn"
678         },
679         {"stop", (PyCFunction)eSocketNotifierPy_stop, METH_NOARGS,
680          "stops the sn"
681         },
682         {"getFD", (PyCFunction)eSocketNotifierPy_get_fd, METH_NOARGS,
683          "get file descriptor"
684         },
685         {"getRequested", (PyCFunction)eSocketNotifierPy_get_requested, METH_NOARGS,
686          "get requested"
687         },
688         {"setRequested", (PyCFunction)eSocketNotifierPy_set_requested, METH_VARARGS,
689          "set requested"
690         },
691         {NULL}  /* Sentinel */
692 };
693
694 static PyObject *
695 eSocketNotifierPy_get_cb_list(eSocketNotifierPy *self, void *closure)
696 {
697         return self->sn->activated.get();
698 }
699
700 static PyGetSetDef eSocketNotifierPy_getseters[] = {
701         {"callback",
702          (getter)eSocketNotifierPy_get_cb_list, (setter)0,
703          "returns the callback python list",
704          NULL},
705         {NULL} /* Sentinel */
706 };
707
708 static PyTypeObject eSocketNotifierPyType = {
709         PyObject_HEAD_INIT(NULL)
710         0, /*ob_size*/
711         "eBaseImpl.eSocketNotifier", /*tp_name*/
712         sizeof(eSocketNotifierPy), /*tp_basicsize*/
713         0, /*tp_itemsize*/
714         (destructor)eSocketNotifierPy_dealloc, /*tp_dealloc*/
715         0, /*tp_print*/
716         0, /*tp_getattr*/
717         0, /*tp_setattr*/
718         0, /*tp_compare*/
719         0, /*tp_repr*/
720         0, /*tp_as_number*/
721         0, /*tp_as_sequence*/
722         0, /*tp_as_mapping*/
723         0, /*tp_hash */
724         0, /*tp_call*/
725         0, /*tp_str*/
726         0, /*tp_getattro*/
727         0, /*tp_setattro*/
728         0, /*tp_as_buffer*/
729         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /*tp_flags*/
730         "eTimer objects", /* tp_doc */
731         (traverseproc)eSocketNotifierPy_traverse, /* tp_traverse */
732         (inquiry)eSocketNotifierPy_clear, /* tp_clear */
733         0, /* tp_richcompare */
734         offsetof(eSocketNotifierPy, in_weakreflist), /* tp_weaklistoffset */
735         0, /* tp_iter */
736         0, /* tp_iternext */
737         eSocketNotifierPy_methods, /* tp_methods */
738         0, /* tp_members */
739         eSocketNotifierPy_getseters, /* tp_getset */
740         0, /* tp_base */
741         0, /* tp_dict */
742         0, /* tp_descr_get */
743         0, /* tp_descr_set */
744         0, /* tp_dictoffset */
745         0, /* tp_init */
746         0, /* tp_alloc */
747         eSocketNotifierPy_new, /* tp_new */
748 };
749
750 static PyMethodDef module_methods[] = {
751         {NULL}  /* Sentinel */
752 };
753
754 void eBaseInit(void)
755 {
756         PyObject* m = Py_InitModule3("eBaseImpl", module_methods,
757                 "Module that implements some enigma classes with working cyclic garbage collection.");
758
759         if (m == NULL)
760                 return;
761
762         if (!PyType_Ready(&eTimerPyType))
763         {
764                 Org_Py_INCREF((PyObject*)&eTimerPyType);
765                 PyModule_AddObject(m, "eTimer", (PyObject*)&eTimerPyType);
766         }
767         if (!PyType_Ready(&eSocketNotifierPyType))
768         {
769                 Org_Py_INCREF((PyObject*)&eSocketNotifierPyType);
770                 PyModule_AddObject(m, "eSocketNotifier", (PyObject*)&eSocketNotifierPyType);
771         }
772 }
773 }