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