1 /* Implementation of main-loop integration for dbus-python.
3 * Copyright (C) 2006 Collabora Ltd.
5 * Licensed under the Academic Free License version 2.1
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "dbus_bindings-internal.h"
27 #ifndef HAVE_DBUS_WATCH_GET_UNIX_FD
28 # define dbus_watch_get_unix_fd dbus_watch_get_fd
31 /* Watch ============================================================ */
33 PyDoc_STRVAR(Watch_tp_doc
,
34 "Object representing a watched file descriptor.\n"
35 "Cannot be instantiated from Python.\n"
38 static PyTypeObject Watch_Type
;
47 PyDoc_STRVAR(Watch_fileno__doc__
,
49 "Return the watched file descriptor.\n");
51 Watch_fileno(Watch
*self
, PyObject
*unused UNUSED
)
56 DBG("Attempted fileno() on broken Watch at %p", self
);
57 PyErr_SetString(PyExc_ValueError
, "FD watch is no longer valid");
60 fd
= dbus_watch_get_unix_fd(self
->watch
);
61 DBG("Watch at %p (wrapping DBusWatch at %p) has fileno %d", self
,
63 return PyInt_FromLong(fd
);
66 PyDoc_STRVAR(Watch_get_flags__doc__
,
67 "get_flags() -> int\n\n"
68 "Return 0, WATCH_READABLE, WATCH_WRITABLE or WATCH_READABLE|WATCH_WRITABLE.\n");
70 Watch_get_flags(Watch
*self
, PyObject
*unused UNUSED
)
75 DBG("Attempted get_flags() on broken Watch at %p", self
);
76 PyErr_SetString(PyExc_ValueError
, "FD watch is no longer valid");
79 flags
= dbus_watch_get_flags(self
->watch
);
80 DBG("Watch at %p (wrapping DBusWatch at %p) has flags 0x%x (%s,%s)",
81 self
, self
->watch
, flags
, flags
& DBUS_WATCH_READABLE
? "read" : ".",
82 flags
& DBUS_WATCH_WRITABLE
? "write" : ".");
83 return PyInt_FromLong((long)flags
);
86 PyDoc_STRVAR(Watch_get_enabled__doc__
,
87 "get_enabled() -> bool\n\n"
88 "Return True if this watch is currently active.\n");
90 Watch_get_enabled(Watch
*self
, PyObject
*unused UNUSED
)
95 DBG("Attempted get_enabled() on broken Watch at %p", self
);
96 PyErr_SetString(PyExc_ValueError
, "FD watch is no longer valid");
99 enabled
= dbus_watch_get_enabled(self
->watch
);
100 DBG("Watch at %p (wrapping DBusWatch at %p) is %s", self
,
101 self
->watch
, enabled
? "enabled" : "disabled");
102 return PyBool_FromLong(enabled
);
105 PyDoc_STRVAR(Watch_handle__doc__
,
107 "To be called when the file descriptor is closed or reports error,\n"
108 "or enters a readable/writable state for which notification is required\n"
109 "(according to get_flags()).");
111 Watch_handle(Watch
*self
, PyObject
*args
)
115 DBG("Attempted handle() on broken Watch at %p", self
);
116 PyErr_SetString(PyExc_ValueError
, "FD watch is no longer valid");
119 if (!PyArg_ParseTuple(args
, "i", &flags
)) {
120 DBG("Python passed junk to <Watch at %p>.handle()", self
);
123 DBG("Watch at %p (wrapping DBusWatch at %p) handle(0x%x) (%s,%s)",
124 self
, self
->watch
, flags
, flags
& DBUS_WATCH_READABLE
? "read" : ".",
125 flags
& DBUS_WATCH_WRITABLE
? "write" : ".");
126 Py_BEGIN_ALLOW_THREADS
127 dbus_watch_handle(self
->watch
, (unsigned int)flags
);
132 /* Arranges for the watch to be referenced by its corresponding DBusWatch.
133 * Returns a borrowed reference, or NULL with a Python exception.
136 Watch_BorrowFromDBusWatch(DBusWatch
*watch
, PyObject
*mainloop
)
138 Watch
*self
= (Watch
*)dbus_watch_get_data(watch
);
141 return (PyObject
*)self
;
143 PyErr_SetString(PyExc_AssertionError
,
144 "Attempted to use a non-added watch");
148 self
= PyObject_New(Watch
, &Watch_Type
);
155 dbus_watch_set_data(watch
, self
,
156 (DBusFreeFunction
)dbus_py_take_gil_and_xdecref
);
157 return (PyObject
*)self
;
160 static PyMethodDef Watch_tp_methods
[] = {
161 {"fileno", (PyCFunction
)Watch_fileno
, METH_NOARGS
,
162 Watch_fileno__doc__
},
163 {"get_flags", (PyCFunction
)Watch_get_flags
, METH_NOARGS
,
164 Watch_get_flags__doc__
},
165 {"handle", (PyCFunction
)Watch_handle
, METH_VARARGS
,
166 Watch_handle__doc__
},
167 {"get_enabled", (PyCFunction
)Watch_get_enabled
, METH_NOARGS
,
168 Watch_get_enabled__doc__
},
169 {NULL
, NULL
, 0, NULL
}
172 static void Watch_tp_dealloc(PyObject
*self
)
177 static PyTypeObject Watch_Type
= {
178 PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type
))
180 "_dbus_bindings.Watch",
183 Watch_tp_dealloc
, /* tp_dealloc */
189 0, /* tp_as_number */
190 0, /* tp_as_sequence */
191 0, /* tp_as_mapping */
192 dbus_py_tp_hash_by_pointer
, /* tp_hash */
197 0, /* tp_as_buffer */
198 Py_TPFLAGS_DEFAULT
, /* tp_flags */
199 Watch_tp_doc
, /* tp_doc */
202 dbus_py_tp_richcompare_by_pointer
, /* tp_richcompare */
203 0, /* tp_weaklistoffset */
206 Watch_tp_methods
, /* tp_methods */
211 0, /* tp_descr_get */
212 0, /* tp_descr_set */
213 0, /* tp_dictoffset */
216 /* deliberately not callable! */
220 /* Timeout ========================================================== */
222 PyDoc_STRVAR(Timeout_tp_doc
,
223 "Object representing a watched file descriptor.\n"
224 "Cannot be instantiated from Python.\n"
227 static PyTypeObject Timeout_Type
;
229 DEFINE_CHECK(Timeout
)
233 DBusTimeout
*timeout
;
236 PyDoc_STRVAR(Timeout_get_interval__doc__
,
237 "get_interval() -> float\n\n"
238 "Return the interval in seconds.\n");
240 Timeout_get_interval(Timeout
*self
, PyObject
*unused UNUSED
)
243 if (!self
->timeout
) {
244 DBG("Attempted get_interval() on broken Timeout at %p", self
);
245 PyErr_SetString(PyExc_ValueError
, "Timeout object is no longer valid");
248 interval
= ((double)dbus_timeout_get_interval(self
->timeout
)) / 1000.0;
249 DBG("Timeout at %p (wrapping DBusTimeout at %p) has interval %f s", self
,
250 self
->timeout
, interval
);
251 return PyFloat_FromDouble(interval
);
254 PyDoc_STRVAR(Timeout_get_enabled__doc__
,
255 "get_enabled() -> bool\n\n"
256 "Return True if this timeout is currently active.\n");
258 Timeout_get_enabled(Timeout
*self
, PyObject
*unused UNUSED
)
262 if (!self
->timeout
) {
263 DBG("Attempted get_enabled() on broken Timeout at %p", self
);
264 PyErr_SetString(PyExc_ValueError
, "Timeout object is no longer valid");
267 enabled
= dbus_timeout_get_enabled(self
->timeout
);
268 DBG("Timeout at %p (wrapping DBusTimeout at %p) is %s", self
,
269 self
->timeout
, enabled
? "enabled" : "disabled");
270 return PyBool_FromLong(enabled
);
273 PyDoc_STRVAR(Timeout_handle__doc__
,
275 "To be called when timeout occurs.\n");
277 Timeout_handle(Timeout
*self
, PyObject
*unused UNUSED
)
279 if (!self
->timeout
) {
280 DBG("Attempted handle() on broken Timeout at %p", self
);
281 PyErr_SetString(PyExc_ValueError
, "Timeout object is no longer valid");
284 DBG("Timeout_handle() with self->timeout=%p", self
->timeout
);
285 Py_BEGIN_ALLOW_THREADS
286 dbus_timeout_handle(self
->timeout
);
291 /* Returns a borrowed reference */
293 Timeout_BorrowFromDBusTimeout(DBusTimeout
*timeout
, PyObject
*mainloop
)
295 Timeout
*self
= (Timeout
*)dbus_timeout_get_data(timeout
);
297 return (PyObject
*)self
;
299 PyErr_SetString(PyExc_AssertionError
,
300 "Attempted to use a non-added timeout");
304 self
= PyObject_New(Timeout
, &Timeout_Type
);
308 self
->timeout
= timeout
;
311 dbus_timeout_set_data(timeout
, self
,
312 (DBusFreeFunction
)dbus_py_take_gil_and_xdecref
);
313 return (PyObject
*)self
;
316 static PyMethodDef Timeout_tp_methods
[] = {
317 {"get_interval", (PyCFunction
)Timeout_get_interval
, METH_NOARGS
,
318 Timeout_get_interval__doc__
},
319 {"handle", (PyCFunction
)Timeout_handle
, METH_NOARGS
,
320 Timeout_handle__doc__
},
321 {"get_enabled", (PyCFunction
)Timeout_get_enabled
, METH_NOARGS
,
322 Timeout_get_enabled__doc__
},
323 {NULL
, NULL
, 0, NULL
}
326 static void Timeout_tp_dealloc(PyObject
*self
)
331 static PyTypeObject Timeout_Type
= {
332 PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type
))
334 "_dbus_bindings.Timeout",
337 Timeout_tp_dealloc
, /* tp_dealloc */
343 0, /* tp_as_number */
344 0, /* tp_as_sequence */
345 0, /* tp_as_mapping */
346 dbus_py_tp_hash_by_pointer
, /* tp_hash */
351 0, /* tp_as_buffer */
352 Py_TPFLAGS_DEFAULT
, /* tp_flags */
353 Timeout_tp_doc
, /* tp_doc */
356 dbus_py_tp_richcompare_by_pointer
, /* tp_richcompare */
357 0, /* tp_weaklistoffset */
360 Timeout_tp_methods
, /* tp_methods */
365 0, /* tp_descr_get */
366 0, /* tp_descr_set */
367 0, /* tp_dictoffset */
370 /* deliberately not callable! */
374 /* Native mainloop wrapper ========================================= */
376 PyDoc_STRVAR(NativeMainLoop_tp_doc
,
377 "Object representing D-Bus main loop integration done in native code.\n"
378 "Cannot be instantiated directly.\n"
381 static PyTypeObject NativeMainLoop_Type
;
383 DEFINE_CHECK(NativeMainLoop
)
387 /* Called with the GIL held, should set a Python exception on error */
388 dbus_bool_t (*set_up_connection_cb
)(DBusConnection
*, void *);
389 dbus_bool_t (*set_up_server_cb
)(DBusServer
*, void *);
390 /* Called in a destructor. Must not touch the exception state (use
391 * PyErr_Fetch and PyErr_Restore if necessary). */
392 void (*free_cb
)(void *);
396 static void NativeMainLoop_tp_dealloc(NativeMainLoop
*self
)
398 if (self
->data
&& self
->free_cb
) {
399 (self
->free_cb
)(self
->data
);
401 PyObject_Del((PyObject
*)self
);
404 static PyTypeObject NativeMainLoop_Type
= {
405 PyObject_HEAD_INIT(DEFERRED_ADDRESS(&PyType_Type
))
407 "dbus.mainloop.NativeMainLoop",
408 sizeof(NativeMainLoop
),
410 (destructor
)NativeMainLoop_tp_dealloc
, /* tp_dealloc */
416 0, /* tp_as_number */
417 0, /* tp_as_sequence */
418 0, /* tp_as_mapping */
424 0, /* tp_as_buffer */
425 Py_TPFLAGS_DEFAULT
, /* tp_flags */
426 NativeMainLoop_tp_doc
, /* tp_doc */
429 0, /* tp_richcompare */
430 0, /* tp_weaklistoffset */
438 0, /* tp_descr_get */
439 0, /* tp_descr_set */
440 0, /* tp_dictoffset */
443 /* deliberately not callable! */
447 /* Internal C API for Connection, Bus, Server ======================= */
450 dbus_py_check_mainloop_sanity(PyObject
*mainloop
)
452 if (NativeMainLoop_Check(mainloop
)) {
455 PyErr_SetString(PyExc_TypeError
,
456 "A dbus.mainloop.NativeMainLoop instance is required");
461 dbus_py_set_up_connection(PyObject
*conn
, PyObject
*mainloop
)
463 if (NativeMainLoop_Check(mainloop
)) {
464 /* Native mainloops are allowed to do arbitrary strange things */
465 NativeMainLoop
*nml
= (NativeMainLoop
*)mainloop
;
466 DBusConnection
*dbc
= DBusPyConnection_BorrowDBusConnection(conn
);
471 return (nml
->set_up_connection_cb
)(dbc
, nml
->data
);
473 PyErr_SetString(PyExc_TypeError
,
474 "A dbus.mainloop.NativeMainLoop instance is required");
478 /* C API ============================================================ */
481 DBusPyNativeMainLoop_New4(dbus_bool_t (*conn_cb
)(DBusConnection
*, void *),
482 dbus_bool_t (*server_cb
)(DBusServer
*, void *),
483 void (*free_cb
)(void *),
486 NativeMainLoop
*self
= PyObject_New(NativeMainLoop
, &NativeMainLoop_Type
);
489 self
->free_cb
= free_cb
;
490 self
->set_up_connection_cb
= conn_cb
;
491 self
->set_up_server_cb
= server_cb
;
493 return (PyObject
*)self
;
496 /* Null mainloop implementation ===================================== */
499 noop_main_loop_cb(void *conn_or_server UNUSED
, void *data UNUSED
)
504 #define noop_conn_cb ((dbus_bool_t (*)(DBusConnection *, void *))(noop_main_loop_cb))
505 #define noop_server_cb ((dbus_bool_t (*)(DBusServer *, void *))(noop_main_loop_cb))
507 /* Initialization =================================================== */
510 dbus_py_init_mainloop(void)
512 if (PyType_Ready (&Watch_Type
) < 0) return 0;
513 if (PyType_Ready (&Timeout_Type
) < 0) return 0;
514 if (PyType_Ready (&NativeMainLoop_Type
) < 0) return 0;
516 /* placate -Wunused */
517 (void)&Watch_BorrowFromDBusWatch
;
518 (void)&Timeout_BorrowFromDBusTimeout
;
524 dbus_py_insert_mainloop_types(PyObject
*this_module
)
526 PyObject
*null_main_loop
= DBusPyNativeMainLoop_New4(noop_conn_cb
,
530 if (!null_main_loop
) return 0;
532 if (PyModule_AddObject (this_module
, "Watch",
533 (PyObject
*)&Watch_Type
) < 0) return 0;
534 if (PyModule_AddObject (this_module
, "Timeout",
535 (PyObject
*)&Timeout_Type
) < 0) return 0;
536 if (PyModule_AddObject (this_module
, "NativeMainLoop",
537 (PyObject
*)&NativeMainLoop_Type
) < 0) return 0;
538 if (PyModule_AddObject (this_module
, "NULL_MAIN_LOOP",
539 null_main_loop
) < 0) return 0;
543 /* vim:set ft=c cino< sw=4 sts=4 et: */