Updated Indonesian translation
[empathy-mirror.git] / tools / glib-ginterface-gen.py
blobc0ce20ddcdbcc36b8bc0ba8905351bd17247155a
1 #!/usr/bin/python
3 # glib-ginterface-gen.py: service-side interface generator
5 # Generate dbus-glib 0.x service GInterfaces from the Telepathy specification.
6 # The master copy of this program is in the telepathy-glib repository -
7 # please make any changes there.
9 # Copyright (C) 2006, 2007 Collabora Limited
11 # This library is free software; you can redistribute it and/or
12 # modify it under the terms of the GNU Lesser General Public
13 # License as published by the Free Software Foundation; either
14 # version 2.1 of the License, or (at your option) any later version.
16 # This library is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 # Lesser General Public License for more details.
21 # You should have received a copy of the GNU Lesser General Public
22 # License along with this library; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 import sys
26 import os.path
27 import xml.dom.minidom
29 from libtpcodegen import file_set_contents, key_by_name, u
30 from libglibcodegen import Signature, type_to_gtype, \
31 NS_TP, dbus_gutils_wincaps_to_uscore
34 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
36 def get_emits_changed(node):
37 try:
38 return [
39 annotation.getAttribute('value')
40 for annotation in node.getElementsByTagName('annotation')
41 if annotation.getAttribute('name') == 'org.freedesktop.DBus.Property.EmitsChangedSignal'
42 ][0]
43 except IndexError:
44 return None
46 class Generator(object):
48 def __init__(self, dom, prefix, basename, signal_marshal_prefix,
49 headers, end_headers, not_implemented_func,
50 allow_havoc):
51 self.dom = dom
52 self.__header = []
53 self.__body = []
54 self.__docs = []
56 assert prefix.endswith('_')
57 assert not signal_marshal_prefix.endswith('_')
59 # The main_prefix, sub_prefix thing is to get:
60 # FOO_ -> (FOO_, _)
61 # FOO_SVC_ -> (FOO_, _SVC_)
62 # but
63 # FOO_BAR/ -> (FOO_BAR_, _)
64 # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_)
66 if '/' in prefix:
67 main_prefix, sub_prefix = prefix.upper().split('/', 1)
68 prefix = prefix.replace('/', '_')
69 else:
70 main_prefix, sub_prefix = prefix.upper().split('_', 1)
72 self.MAIN_PREFIX_ = main_prefix + '_'
73 self._SUB_PREFIX_ = '_' + sub_prefix
75 self.Prefix_ = prefix
76 self.Prefix = prefix.replace('_', '')
77 self.prefix_ = prefix.lower()
78 self.PREFIX_ = prefix.upper()
80 self.basename = basename
81 self.signal_marshal_prefix = signal_marshal_prefix
82 self.headers = headers
83 self.end_headers = end_headers
84 self.not_implemented_func = not_implemented_func
85 self.allow_havoc = allow_havoc
87 def h(self, s):
88 self.__header.append(s)
90 def b(self, s):
91 self.__body.append(s)
93 def d(self, s):
94 self.__docs.append(s)
96 def do_node(self, node):
97 node_name = node.getAttribute('name').replace('/', '')
98 node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
99 node_name_lc = self.node_name_lc = node_name.lower()
100 node_name_uc = self.node_name_uc = node_name.upper()
102 interfaces = node.getElementsByTagName('interface')
103 assert len(interfaces) == 1, interfaces
104 interface = interfaces[0]
105 self.iface_name = interface.getAttribute('name')
107 tmp = interface.getAttribute('tp:implement-service')
108 if tmp == "no":
109 return
111 tmp = interface.getAttribute('tp:causes-havoc')
112 if tmp and not self.allow_havoc:
113 raise AssertionError('%s is %s' % (self.iface_name, tmp))
115 iface_emits_changed = get_emits_changed(interface)
117 self.b('static const DBusGObjectInfo _%s%s_object_info;'
118 % (self.prefix_, node_name_lc))
119 self.b('')
121 methods = interface.getElementsByTagName('method')
122 signals = interface.getElementsByTagName('signal')
123 properties = interface.getElementsByTagName('property')
124 # Don't put properties in dbus-glib glue
125 glue_properties = []
127 self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
128 self.b(' GTypeInterface parent_class;')
129 for method in methods:
130 self.b(' %s %s;' % self.get_method_impl_names(method))
131 self.b('};')
132 self.b('')
134 if signals:
135 self.b('enum {')
136 for signal in signals:
137 self.b(' %s,' % self.get_signal_const_entry(signal))
138 self.b(' N_%s_SIGNALS' % node_name_uc)
139 self.b('};')
140 self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
141 % (node_name_lc, node_name_uc))
142 self.b('')
144 self.b('static void %s%s_base_init (gpointer klass);'
145 % (self.prefix_, node_name_lc))
146 self.b('')
148 self.b('GType')
149 self.b('%s%s_get_type (void)'
150 % (self.prefix_, node_name_lc))
151 self.b('{')
152 self.b(' static GType type = 0;')
153 self.b('')
154 self.b(' if (G_UNLIKELY (type == 0))')
155 self.b(' {')
156 self.b(' static const GTypeInfo info = {')
157 self.b(' sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
158 self.b(' %s%s_base_init, /* base_init */'
159 % (self.prefix_, node_name_lc))
160 self.b(' NULL, /* base_finalize */')
161 self.b(' NULL, /* class_init */')
162 self.b(' NULL, /* class_finalize */')
163 self.b(' NULL, /* class_data */')
164 self.b(' 0,')
165 self.b(' 0, /* n_preallocs */')
166 self.b(' NULL /* instance_init */')
167 self.b(' };')
168 self.b('')
169 self.b(' type = g_type_register_static (G_TYPE_INTERFACE,')
170 self.b(' "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
171 self.b(' }')
172 self.b('')
173 self.b(' return type;')
174 self.b('}')
175 self.b('')
177 self.d('/**')
178 self.d(' * %s%s:' % (self.Prefix, node_name_mixed))
179 self.d(' *')
180 self.d(' * Dummy typedef representing any implementation of this '
181 'interface.')
182 self.d(' */')
184 self.h('typedef struct _%s%s %s%s;'
185 % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
186 self.h('')
188 self.d('/**')
189 self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed))
190 self.d(' *')
191 self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
193 if methods:
194 self.d(' *')
195 self.d(' * In a full implementation of this interface (i.e. all')
196 self.d(' * methods implemented), the interface initialization')
197 self.d(' * function used in G_IMPLEMENT_INTERFACE() would')
198 self.d(' * typically look like this:')
199 self.d(' *')
200 self.d(' * <programlisting>')
201 self.d(' * static void')
202 self.d(' * implement_%s (gpointer klass,' % self.node_name_lc)
203 self.d(' * gpointer unused G_GNUC_UNUSED)')
204 self.d(' * {')
205 self.d(' * #define IMPLEMENT(x) %s%s_implement_&num;&num;x (\\'
206 % (self.prefix_, self.node_name_lc))
207 self.d(' * klass, my_object_&num;&num;x)')
209 for method in methods:
210 class_member_name = method.getAttribute('tp:name-for-bindings')
211 class_member_name = class_member_name.lower()
212 self.d(' * IMPLEMENT (%s);' % class_member_name)
214 self.d(' * #undef IMPLEMENT')
215 self.d(' * }')
216 self.d(' * </programlisting>')
217 else:
218 self.d(' * This interface has no D-Bus methods, so an')
219 self.d(' * implementation can typically pass %NULL to')
220 self.d(' * G_IMPLEMENT_INTERFACE() as the interface')
221 self.d(' * initialization function.')
223 self.d(' */')
224 self.d('')
226 self.h('typedef struct _%s%sClass %s%sClass;'
227 % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
228 self.h('')
229 self.h('GType %s%s_get_type (void);'
230 % (self.prefix_, node_name_lc))
232 gtype = self.current_gtype = \
233 self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
234 classname = self.Prefix + node_name_mixed
236 self.h('#define %s \\\n (%s%s_get_type ())'
237 % (gtype, self.prefix_, node_name_lc))
238 self.h('#define %s%s(obj) \\\n'
239 ' (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
240 % (self.PREFIX_, node_name_uc, gtype, classname))
241 self.h('#define %sIS%s%s(obj) \\\n'
242 ' (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
243 % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
244 self.h('#define %s%s_GET_CLASS(obj) \\\n'
245 ' (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
246 % (self.PREFIX_, node_name_uc, gtype, classname))
247 self.h('')
248 self.h('')
250 base_init_code = []
252 for method in methods:
253 self.do_method(method)
255 for signal in signals:
256 base_init_code.extend(self.do_signal(signal))
258 self.b('static inline void')
259 self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)'
260 % (self.prefix_, node_name_lc))
261 self.b('{')
263 if properties:
264 self.b(' static TpDBusPropertiesMixinPropInfo properties[%d] = {'
265 % (len(properties) + 1))
267 for m in properties:
268 access = m.getAttribute('access')
269 assert access in ('read', 'write', 'readwrite')
271 if access == 'read':
272 flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
273 elif access == 'write':
274 flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
275 else:
276 flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
277 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
279 prop_emits_changed = get_emits_changed(m)
281 if prop_emits_changed is None:
282 prop_emits_changed = iface_emits_changed
284 if prop_emits_changed == 'true':
285 flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED'
286 elif prop_emits_changed == 'invalidates':
287 flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED'
289 self.b(' { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
290 % (flags, m.getAttribute('type'), m.getAttribute('name')))
292 self.b(' { 0, 0, NULL, 0, NULL, NULL }')
293 self.b(' };')
294 self.b(' static TpDBusPropertiesMixinIfaceInfo interface =')
295 self.b(' { 0, properties, NULL, NULL };')
296 self.b('')
299 self.b(' dbus_g_object_type_install_info (%s%s_get_type (),'
300 % (self.prefix_, node_name_lc))
301 self.b(' &_%s%s_object_info);'
302 % (self.prefix_, node_name_lc))
303 self.b('')
305 if properties:
306 self.b(' interface.dbus_interface = g_quark_from_static_string '
307 '("%s");' % self.iface_name)
309 for i, m in enumerate(properties):
310 self.b(' properties[%d].name = g_quark_from_static_string ("%s");'
311 % (i, m.getAttribute('name')))
312 self.b(' properties[%d].type = %s;'
313 % (i, type_to_gtype(m.getAttribute('type'))[1]))
315 self.b(' tp_svc_interface_set_dbus_properties_info (%s, &interface);'
316 % self.current_gtype)
318 self.b('')
320 for s in base_init_code:
321 self.b(s)
322 self.b('}')
324 self.b('static void')
325 self.b('%s%s_base_init (gpointer klass)'
326 % (self.prefix_, node_name_lc))
327 self.b('{')
328 self.b(' static gboolean initialized = FALSE;')
329 self.b('')
330 self.b(' if (!initialized)')
331 self.b(' {')
332 self.b(' initialized = TRUE;')
333 self.b(' %s%s_base_init_once (klass);'
334 % (self.prefix_, node_name_lc))
335 self.b(' }')
336 # insert anything we need to do per implementation here
337 self.b('}')
339 self.h('')
341 self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
342 % (self.prefix_, node_name_lc))
344 method_blob, offsets = self.get_method_glue(methods)
346 for method, offset in zip(methods, offsets):
347 self.do_method_glue(method, offset)
349 if len(methods) == 0:
350 # empty arrays are a gcc extension, so put in a dummy member
351 self.b(" { NULL, NULL, 0 }")
353 self.b('};')
354 self.b('')
356 self.b('static const DBusGObjectInfo _%s%s_object_info = {'
357 % (self.prefix_, node_name_lc))
358 self.b(' 0,') # version
359 self.b(' _%s%s_methods,' % (self.prefix_, node_name_lc))
360 self.b(' %d,' % len(methods))
361 self.b('"' + method_blob.replace('\0', '\\0') + '",')
362 self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
363 self.b('"' +
364 self.get_property_glue(glue_properties).replace('\0', '\\0') +
365 '",')
366 self.b('};')
367 self.b('')
369 self.node_name_mixed = None
370 self.node_name_lc = None
371 self.node_name_uc = None
373 def get_method_glue(self, methods):
374 info = []
375 offsets = []
377 for method in methods:
378 offsets.append(len(''.join(info)))
380 info.append(self.iface_name + '\0')
381 info.append(method.getAttribute('name') + '\0')
383 info.append('A\0') # async
385 counter = 0
386 for arg in method.getElementsByTagName('arg'):
387 out = arg.getAttribute('direction') == 'out'
389 name = arg.getAttribute('name')
390 if not name:
391 assert out
392 name = 'arg%u' % counter
393 counter += 1
395 info.append(name + '\0')
397 if out:
398 info.append('O\0')
399 else:
400 info.append('I\0')
402 if out:
403 info.append('F\0') # not const
404 info.append('N\0') # not error or return
405 info.append(arg.getAttribute('type') + '\0')
407 info.append('\0')
409 return ''.join(info) + '\0', offsets
411 def do_method_glue(self, method, offset):
412 lc_name = method.getAttribute('tp:name-for-bindings')
413 if method.getAttribute('name') != lc_name.replace('_', ''):
414 raise AssertionError('Method %s tp:name-for-bindings (%s) does '
415 'not match' % (method.getAttribute('name'), lc_name))
416 lc_name = lc_name.lower()
418 marshaller = 'g_cclosure_marshal_generic'
419 wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
421 self.b(" { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
423 def get_signal_glue(self, signals):
424 info = []
426 for signal in signals:
427 info.append(self.iface_name)
428 info.append(signal.getAttribute('name'))
430 return '\0'.join(info) + '\0\0'
432 # the implementation can be the same
433 get_property_glue = get_signal_glue
435 def get_method_impl_names(self, method):
436 dbus_method_name = method.getAttribute('name')
438 class_member_name = method.getAttribute('tp:name-for-bindings')
439 if dbus_method_name != class_member_name.replace('_', ''):
440 raise AssertionError('Method %s tp:name-for-bindings (%s) does '
441 'not match' % (dbus_method_name, class_member_name))
442 class_member_name = class_member_name.lower()
444 stub_name = (self.prefix_ + self.node_name_lc + '_' +
445 class_member_name)
446 return (stub_name + '_impl', class_member_name + '_cb')
448 def do_method(self, method):
449 assert self.node_name_mixed is not None
451 in_class = []
453 # Examples refer to Thing.DoStuff (su) -> ii
455 # DoStuff
456 dbus_method_name = method.getAttribute('name')
457 # do_stuff
458 class_member_name = method.getAttribute('tp:name-for-bindings')
459 if dbus_method_name != class_member_name.replace('_', ''):
460 raise AssertionError('Method %s tp:name-for-bindings (%s) does '
461 'not match' % (dbus_method_name, class_member_name))
462 class_member_name = class_member_name.lower()
464 # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
465 # DBusGMethodInvocation *);
466 stub_name = (self.prefix_ + self.node_name_lc + '_' +
467 class_member_name)
468 # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *,
469 # const char *, guint, DBusGMethodInvocation);
470 impl_name = stub_name + '_impl'
471 # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *,
472 # gint, gint);
473 ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' +
474 class_member_name)
476 # Gather arguments
477 in_args = []
478 out_args = []
479 for i in method.getElementsByTagName('arg'):
480 name = i.getAttribute('name')
481 direction = i.getAttribute('direction') or 'in'
482 dtype = i.getAttribute('type')
484 assert direction in ('in', 'out')
486 if name:
487 name = direction + '_' + name
488 elif direction == 'in':
489 name = direction + str(len(in_args))
490 else:
491 name = direction + str(len(out_args))
493 ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
495 if pointer:
496 ctype = 'const ' + ctype
498 struct = (ctype, name)
500 if direction == 'in':
501 in_args.append(struct)
502 else:
503 out_args.append(struct)
505 # Implementation type declaration (in header, docs separated)
506 self.d('/**')
507 self.d(' * %s:' % impl_name)
508 self.d(' * @self: The object implementing this interface')
509 for (ctype, name) in in_args:
510 self.d(' * @%s: %s (FIXME, generate documentation)'
511 % (name, ctype))
512 self.d(' * @context: Used to return values or throw an error')
513 self.d(' *')
514 self.d(' * The signature of an implementation of the D-Bus method')
515 self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
516 self.d(' */')
518 self.h('typedef void (*%s) (%s%s *self,'
519 % (impl_name, self.Prefix, self.node_name_mixed))
520 for (ctype, name) in in_args:
521 self.h(' %s%s,' % (ctype, name))
522 self.h(' DBusGMethodInvocation *context);')
524 # Class member (in class definition)
525 in_class.append(' %s %s;' % (impl_name, class_member_name))
527 # Stub definition (in body only - it's static)
528 self.b('static void')
529 self.b('%s (%s%s *self,'
530 % (stub_name, self.Prefix, self.node_name_mixed))
531 for (ctype, name) in in_args:
532 self.b(' %s%s,' % (ctype, name))
533 self.b(' DBusGMethodInvocation *context)')
534 self.b('{')
535 self.b(' %s impl = (%s%s_GET_CLASS (self)->%s_cb);'
536 % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
537 self.b('')
538 self.b(' if (impl != NULL)')
539 tmp = ['self'] + [name for (ctype, name) in in_args] + ['context']
540 self.b(' {')
541 self.b(' (impl) (%s);' % ',\n '.join(tmp))
542 self.b(' }')
543 self.b(' else')
544 self.b(' {')
545 if self.not_implemented_func:
546 self.b(' %s (context);' % self.not_implemented_func)
547 else:
548 self.b(' GError e = { DBUS_GERROR, ')
549 self.b(' DBUS_GERROR_UNKNOWN_METHOD,')
550 self.b(' "Method not implemented" };')
551 self.b('')
552 self.b(' dbus_g_method_return_error (context, &e);')
553 self.b(' }')
554 self.b('}')
555 self.b('')
557 # Implementation registration (in both header and body)
558 self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);'
559 % (self.prefix_, self.node_name_lc, class_member_name,
560 self.Prefix, self.node_name_mixed, impl_name))
562 self.d('/**')
563 self.d(' * %s%s_implement_%s:'
564 % (self.prefix_, self.node_name_lc, class_member_name))
565 self.d(' * @klass: A class whose instances implement this interface')
566 self.d(' * @impl: A callback used to implement the %s D-Bus method'
567 % dbus_method_name)
568 self.d(' *')
569 self.d(' * Register an implementation for the %s method in the vtable'
570 % dbus_method_name)
571 self.d(' * of an implementation of this interface. To be called from')
572 self.d(' * the interface init function.')
573 self.d(' */')
575 self.b('void')
576 self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
577 % (self.prefix_, self.node_name_lc, class_member_name,
578 self.Prefix, self.node_name_mixed, impl_name))
579 self.b('{')
580 self.b(' klass->%s_cb = impl;' % class_member_name)
581 self.b('}')
582 self.b('')
584 # Return convenience function (static inline, in header)
585 self.d('/**')
586 self.d(' * %s:' % ret_name)
587 self.d(' * @context: The D-Bus method invocation context')
588 for (ctype, name) in out_args:
589 self.d(' * @%s: %s (FIXME, generate documentation)'
590 % (name, ctype))
591 self.d(' *')
592 self.d(' * Return successfully by calling dbus_g_method_return().')
593 self.d(' * This inline function exists only to provide type-safety.')
594 self.d(' */')
595 self.d('')
597 tmp = (['DBusGMethodInvocation *context'] +
598 [ctype + name for (ctype, name) in out_args])
599 self.h('static inline')
600 self.h('/* this comment is to stop gtkdoc realising this is static */')
601 self.h(('void %s (' % ret_name) + (',\n '.join(tmp)) + ');')
602 self.h('static inline void')
603 self.h(('%s (' % ret_name) + (',\n '.join(tmp)) + ')')
604 self.h('{')
605 tmp = ['context'] + [name for (ctype, name) in out_args]
606 self.h(' dbus_g_method_return (' + ',\n '.join(tmp) + ');')
607 self.h('}')
608 self.h('')
610 return in_class
612 def get_signal_const_entry(self, signal):
613 assert self.node_name_uc is not None
614 return ('SIGNAL_%s_%s'
615 % (self.node_name_uc, signal.getAttribute('name')))
617 def do_signal(self, signal):
618 assert self.node_name_mixed is not None
620 in_base_init = []
622 # for signal: Thing::StuffHappened (s, u)
623 # we want to emit:
624 # void tp_svc_thing_emit_stuff_happened (gpointer instance,
625 # const char *arg0, guint arg1);
627 dbus_name = signal.getAttribute('name')
629 ugly_name = signal.getAttribute('tp:name-for-bindings')
630 if dbus_name != ugly_name.replace('_', ''):
631 raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
632 'not match' % (dbus_name, ugly_name))
634 stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
635 ugly_name.lower())
637 const_name = self.get_signal_const_entry(signal)
639 # Gather arguments
640 args = []
641 for i in signal.getElementsByTagName('arg'):
642 name = i.getAttribute('name')
643 dtype = i.getAttribute('type')
644 tp_type = i.getAttribute('tp:type')
646 if name:
647 name = 'arg_' + name
648 else:
649 name = 'arg' + str(len(args))
651 ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
653 if pointer:
654 ctype = 'const ' + ctype
656 struct = (ctype, name, gtype)
657 args.append(struct)
659 tmp = (['gpointer instance'] +
660 [ctype + name for (ctype, name, gtype) in args])
662 self.h(('void %s (' % stub_name) + (',\n '.join(tmp)) + ');')
664 # FIXME: emit docs
666 self.d('/**')
667 self.d(' * %s:' % stub_name)
668 self.d(' * @instance: The object implementing this interface')
669 for (ctype, name, gtype) in args:
670 self.d(' * @%s: %s (FIXME, generate documentation)'
671 % (name, ctype))
672 self.d(' *')
673 self.d(' * Type-safe wrapper around g_signal_emit to emit the')
674 self.d(' * %s signal on interface %s.'
675 % (dbus_name, self.iface_name))
676 self.d(' */')
678 self.b('void')
679 self.b(('%s (' % stub_name) + (',\n '.join(tmp)) + ')')
680 self.b('{')
681 self.b(' g_assert (instance != NULL);')
682 self.b(' g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));'
683 % (self.current_gtype))
684 tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name),
685 '0'] + [name for (ctype, name, gtype) in args])
686 self.b(' g_signal_emit (' + ',\n '.join(tmp) + ');')
687 self.b('}')
688 self.b('')
690 signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_',
691 '-')
693 self.d('/**')
694 self.d(' * %s%s::%s:'
695 % (self.Prefix, self.node_name_mixed, signal_name))
696 self.d(' * @self: an object')
697 for (ctype, name, gtype) in args:
698 self.d(' * @%s: %s (FIXME, generate documentation)'
699 % (name, ctype))
700 self.d(' *')
701 self.d(' * The %s D-Bus signal is emitted whenever '
702 'this GObject signal is.' % dbus_name)
703 self.d(' */')
704 self.d('')
706 in_base_init.append(' %s_signals[%s] ='
707 % (self.node_name_lc, const_name))
708 in_base_init.append(' g_signal_new ("%s",' % signal_name)
709 in_base_init.append(' G_OBJECT_CLASS_TYPE (klass),')
710 in_base_init.append(' G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
711 in_base_init.append(' 0,')
712 in_base_init.append(' NULL, NULL,')
713 in_base_init.append(' g_cclosure_marshal_generic,')
714 in_base_init.append(' G_TYPE_NONE,')
715 tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
716 in_base_init.append(' %s);' % ',\n '.join(tmp))
717 in_base_init.append('')
719 return in_base_init
721 def have_properties(self, nodes):
722 for node in nodes:
723 interface = node.getElementsByTagName('interface')[0]
724 if interface.getElementsByTagName('property'):
725 return True
726 return False
728 def __call__(self):
729 nodes = self.dom.getElementsByTagName('node')
730 nodes.sort(key=key_by_name)
732 self.h('#include <glib-object.h>')
733 self.h('#include <dbus/dbus-glib.h>')
735 for header in self.headers:
736 self.h('#include %s' % header)
737 self.h('')
739 self.h('')
740 self.h('G_BEGIN_DECLS')
741 self.h('')
743 self.b('#include "%s.h"' % self.basename)
744 self.b('')
746 for node in nodes:
747 self.do_node(node)
749 self.h('')
750 self.h('G_END_DECLS')
752 self.b('')
753 for header in self.end_headers:
754 self.b('#include %s' % header)
756 self.h('')
757 self.b('')
758 file_set_contents(self.basename + '.h', u('\n').join(self.__header).encode('utf-8'))
759 file_set_contents(self.basename + '.c', u('\n').join(self.__body).encode('utf-8'))
760 file_set_contents(self.basename + '-gtk-doc.h', u('\n').join(self.__docs).encode('utf-8'))
762 def cmdline_error():
763 print("""\
764 usage:
765 gen-ginterface [OPTIONS] xmlfile Prefix_
766 options:
767 --include='<header.h>' (may be repeated)
768 --include='"header.h"' (ditto)
769 --include-end='"header.h"' (ditto)
770 Include extra headers in the generated .c file
771 --signal-marshal-prefix='prefix'
772 Use the given prefix on generated signal marshallers (default is
773 prefix.lower()).
774 --filename='BASENAME'
775 Set the basename for the output files (default is prefix.lower()
776 + 'ginterfaces')
777 --not-implemented-func='symbol'
778 Set action when methods not implemented in the interface vtable are
779 called. symbol must have signature
780 void symbol (DBusGMethodInvocation *context)
781 and return some sort of "not implemented" error via
782 dbus_g_method_return_error (context, ...)
783 """)
784 sys.exit(1)
787 if __name__ == '__main__':
788 from getopt import gnu_getopt
790 options, argv = gnu_getopt(sys.argv[1:], '',
791 ['filename=', 'signal-marshal-prefix=',
792 'include=', 'include-end=',
793 'allow-unstable',
794 'not-implemented-func='])
796 try:
797 prefix = argv[1]
798 except IndexError:
799 cmdline_error()
801 basename = prefix.lower() + 'ginterfaces'
802 signal_marshal_prefix = prefix.lower().rstrip('_')
803 headers = []
804 end_headers = []
805 not_implemented_func = ''
806 allow_havoc = False
808 for option, value in options:
809 if option == '--filename':
810 basename = value
811 elif option == '--signal-marshal-prefix':
812 signal_marshal_prefix = value
813 elif option == '--include':
814 if value[0] not in '<"':
815 value = '"%s"' % value
816 headers.append(value)
817 elif option == '--include-end':
818 if value[0] not in '<"':
819 value = '"%s"' % value
820 end_headers.append(value)
821 elif option == '--not-implemented-func':
822 not_implemented_func = value
823 elif option == '--allow-unstable':
824 allow_havoc = True
826 try:
827 dom = xml.dom.minidom.parse(argv[0])
828 except IndexError:
829 cmdline_error()
831 Generator(dom, prefix, basename, signal_marshal_prefix, headers,
832 end_headers, not_implemented_func, allow_havoc)()