]> git.0d.be Git - empathy.git/blob - tools/glib-ginterface-gen.py
Updated kn translation
[empathy.git] / tools / glib-ginterface-gen.py
1 #!/usr/bin/python
2
3 # glib-ginterface-gen.py: service-side interface generator
4 #
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.
8 #
9 # Copyright (C) 2006, 2007 Collabora Limited
10 #
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.
15 #
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.
20 #
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
24
25 import sys
26 import os.path
27 import xml.dom.minidom
28
29 from libtpcodegen import file_set_contents
30 from libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
31         NS_TP, dbus_gutils_wincaps_to_uscore
32
33
34 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
35
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
45
46 class Generator(object):
47
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 = []
55
56         assert prefix.endswith('_')
57         assert not signal_marshal_prefix.endswith('_')
58
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_)
65
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)
71
72         self.MAIN_PREFIX_ = main_prefix + '_'
73         self._SUB_PREFIX_ = '_' + sub_prefix
74
75         self.Prefix_ = prefix
76         self.Prefix = prefix.replace('_', '')
77         self.prefix_ = prefix.lower()
78         self.PREFIX_ = prefix.upper()
79
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
86
87     def h(self, s):
88         if isinstance(s, unicode):
89             s = s.encode('utf-8')
90         self.__header.append(s)
91
92     def b(self, s):
93         if isinstance(s, unicode):
94             s = s.encode('utf-8')
95         self.__body.append(s)
96
97     def d(self, s):
98         if isinstance(s, unicode):
99             s = s.encode('utf-8')
100         self.__docs.append(s)
101
102     def do_node(self, node):
103         node_name = node.getAttribute('name').replace('/', '')
104         node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
105         node_name_lc = self.node_name_lc = node_name.lower()
106         node_name_uc = self.node_name_uc = node_name.upper()
107
108         interfaces = node.getElementsByTagName('interface')
109         assert len(interfaces) == 1, interfaces
110         interface = interfaces[0]
111         self.iface_name = interface.getAttribute('name')
112
113         tmp = interface.getAttribute('tp:implement-service')
114         if tmp == "no":
115             return
116
117         tmp = interface.getAttribute('tp:causes-havoc')
118         if tmp and not self.allow_havoc:
119             raise AssertionError('%s is %s' % (self.iface_name, tmp))
120
121         iface_emits_changed = get_emits_changed(interface)
122
123         self.b('static const DBusGObjectInfo _%s%s_object_info;'
124                % (self.prefix_, node_name_lc))
125         self.b('')
126
127         methods = interface.getElementsByTagName('method')
128         signals = interface.getElementsByTagName('signal')
129         properties = interface.getElementsByTagName('property')
130         # Don't put properties in dbus-glib glue
131         glue_properties = []
132
133         self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
134         self.b('    GTypeInterface parent_class;')
135         for method in methods:
136             self.b('    %s %s;' % self.get_method_impl_names(method))
137         self.b('};')
138         self.b('')
139
140         if signals:
141             self.b('enum {')
142             for signal in signals:
143                 self.b('    %s,' % self.get_signal_const_entry(signal))
144             self.b('    N_%s_SIGNALS' % node_name_uc)
145             self.b('};')
146             self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
147                    % (node_name_lc, node_name_uc))
148             self.b('')
149
150         self.b('static void %s%s_base_init (gpointer klass);'
151                % (self.prefix_, node_name_lc))
152         self.b('')
153
154         self.b('GType')
155         self.b('%s%s_get_type (void)'
156                % (self.prefix_, node_name_lc))
157         self.b('{')
158         self.b('  static GType type = 0;')
159         self.b('')
160         self.b('  if (G_UNLIKELY (type == 0))')
161         self.b('    {')
162         self.b('      static const GTypeInfo info = {')
163         self.b('        sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
164         self.b('        %s%s_base_init, /* base_init */'
165                % (self.prefix_, node_name_lc))
166         self.b('        NULL, /* base_finalize */')
167         self.b('        NULL, /* class_init */')
168         self.b('        NULL, /* class_finalize */')
169         self.b('        NULL, /* class_data */')
170         self.b('        0,')
171         self.b('        0, /* n_preallocs */')
172         self.b('        NULL /* instance_init */')
173         self.b('      };')
174         self.b('')
175         self.b('      type = g_type_register_static (G_TYPE_INTERFACE,')
176         self.b('          "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
177         self.b('    }')
178         self.b('')
179         self.b('  return type;')
180         self.b('}')
181         self.b('')
182
183         self.d('/**')
184         self.d(' * %s%s:' % (self.Prefix, node_name_mixed))
185         self.d(' *')
186         self.d(' * Dummy typedef representing any implementation of this '
187                'interface.')
188         self.d(' */')
189
190         self.h('typedef struct _%s%s %s%s;'
191                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
192         self.h('')
193
194         self.d('/**')
195         self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed))
196         self.d(' *')
197         self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
198
199         if methods:
200             self.d(' *')
201             self.d(' * In a full implementation of this interface (i.e. all')
202             self.d(' * methods implemented), the interface initialization')
203             self.d(' * function used in G_IMPLEMENT_INTERFACE() would')
204             self.d(' * typically look like this:')
205             self.d(' *')
206             self.d(' * <programlisting>')
207             self.d(' * static void')
208             self.d(' * implement_%s (gpointer klass,' % self.node_name_lc)
209             self.d(' *     gpointer unused G_GNUC_UNUSED)')
210             self.d(' * {')
211             self.d(' * #define IMPLEMENT(x) %s%s_implement_&num;&num;x (\\'
212                    % (self.prefix_, self.node_name_lc))
213             self.d(' *   klass, my_object_&num;&num;x)')
214
215             for method in methods:
216                 class_member_name = method.getAttribute('tp:name-for-bindings')
217                 class_member_name = class_member_name.lower()
218                 self.d(' *   IMPLEMENT (%s);' % class_member_name)
219
220             self.d(' * #undef IMPLEMENT')
221             self.d(' * }')
222             self.d(' * </programlisting>')
223         else:
224             self.d(' * This interface has no D-Bus methods, so an')
225             self.d(' * implementation can typically pass %NULL to')
226             self.d(' * G_IMPLEMENT_INTERFACE() as the interface')
227             self.d(' * initialization function.')
228
229         self.d(' */')
230         self.d('')
231
232         self.h('typedef struct _%s%sClass %s%sClass;'
233                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
234         self.h('')
235         self.h('GType %s%s_get_type (void);'
236                % (self.prefix_, node_name_lc))
237
238         gtype = self.current_gtype = \
239                 self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
240         classname = self.Prefix + node_name_mixed
241
242         self.h('#define %s \\\n  (%s%s_get_type ())'
243                % (gtype, self.prefix_, node_name_lc))
244         self.h('#define %s%s(obj) \\\n'
245                '  (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
246                % (self.PREFIX_, node_name_uc, gtype, classname))
247         self.h('#define %sIS%s%s(obj) \\\n'
248                '  (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
249                % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
250         self.h('#define %s%s_GET_CLASS(obj) \\\n'
251                '  (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
252                % (self.PREFIX_, node_name_uc, gtype, classname))
253         self.h('')
254         self.h('')
255
256         base_init_code = []
257
258         for method in methods:
259             self.do_method(method)
260
261         for signal in signals:
262             base_init_code.extend(self.do_signal(signal))
263
264         self.b('static inline void')
265         self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)'
266                % (self.prefix_, node_name_lc))
267         self.b('{')
268
269         if properties:
270             self.b('  static TpDBusPropertiesMixinPropInfo properties[%d] = {'
271                    % (len(properties) + 1))
272
273             for m in properties:
274                 access = m.getAttribute('access')
275                 assert access in ('read', 'write', 'readwrite')
276
277                 if access == 'read':
278                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
279                 elif access == 'write':
280                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
281                 else:
282                     flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
283                              'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
284
285                 prop_emits_changed = get_emits_changed(m)
286
287                 if prop_emits_changed is None:
288                     prop_emits_changed = iface_emits_changed
289
290                 if prop_emits_changed == 'true':
291                     flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED'
292                 elif prop_emits_changed == 'invalidates':
293                     flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED'
294
295                 self.b('      { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
296                        % (flags, m.getAttribute('type'), m.getAttribute('name')))
297
298             self.b('      { 0, 0, NULL, 0, NULL, NULL }')
299             self.b('  };')
300             self.b('  static TpDBusPropertiesMixinIfaceInfo interface =')
301             self.b('      { 0, properties, NULL, NULL };')
302             self.b('')
303
304
305         self.b('  dbus_g_object_type_install_info (%s%s_get_type (),'
306                % (self.prefix_, node_name_lc))
307         self.b('      &_%s%s_object_info);'
308                % (self.prefix_, node_name_lc))
309         self.b('')
310
311         if properties:
312             self.b('  interface.dbus_interface = g_quark_from_static_string '
313                    '("%s");' % self.iface_name)
314
315             for i, m in enumerate(properties):
316                 self.b('  properties[%d].name = g_quark_from_static_string ("%s");'
317                        % (i, m.getAttribute('name')))
318                 self.b('  properties[%d].type = %s;'
319                            % (i, type_to_gtype(m.getAttribute('type'))[1]))
320
321             self.b('  tp_svc_interface_set_dbus_properties_info (%s, &interface);'
322                    % self.current_gtype)
323
324             self.b('')
325
326         for s in base_init_code:
327             self.b(s)
328         self.b('}')
329
330         self.b('static void')
331         self.b('%s%s_base_init (gpointer klass)'
332                % (self.prefix_, node_name_lc))
333         self.b('{')
334         self.b('  static gboolean initialized = FALSE;')
335         self.b('')
336         self.b('  if (!initialized)')
337         self.b('    {')
338         self.b('      initialized = TRUE;')
339         self.b('      %s%s_base_init_once (klass);'
340                % (self.prefix_, node_name_lc))
341         self.b('    }')
342         # insert anything we need to do per implementation here
343         self.b('}')
344
345         self.h('')
346
347         self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
348                % (self.prefix_, node_name_lc))
349
350         method_blob, offsets = self.get_method_glue(methods)
351
352         for method, offset in zip(methods, offsets):
353             self.do_method_glue(method, offset)
354
355         if len(methods) == 0:
356             # empty arrays are a gcc extension, so put in a dummy member
357             self.b("  { NULL, NULL, 0 }")
358
359         self.b('};')
360         self.b('')
361
362         self.b('static const DBusGObjectInfo _%s%s_object_info = {'
363                % (self.prefix_, node_name_lc))
364         self.b('  0,')  # version
365         self.b('  _%s%s_methods,' % (self.prefix_, node_name_lc))
366         self.b('  %d,' % len(methods))
367         self.b('"' + method_blob.replace('\0', '\\0') + '",')
368         self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
369         self.b('"' +
370                self.get_property_glue(glue_properties).replace('\0', '\\0') +
371                '",')
372         self.b('};')
373         self.b('')
374
375         self.node_name_mixed = None
376         self.node_name_lc = None
377         self.node_name_uc = None
378
379     def get_method_glue(self, methods):
380         info = []
381         offsets = []
382
383         for method in methods:
384             offsets.append(len(''.join(info)))
385
386             info.append(self.iface_name + '\0')
387             info.append(method.getAttribute('name') + '\0')
388
389             info.append('A\0')    # async
390
391             counter = 0
392             for arg in method.getElementsByTagName('arg'):
393                 out = arg.getAttribute('direction') == 'out'
394
395                 name = arg.getAttribute('name')
396                 if not name:
397                     assert out
398                     name = 'arg%u' % counter
399                 counter += 1
400
401                 info.append(name + '\0')
402
403                 if out:
404                     info.append('O\0')
405                 else:
406                     info.append('I\0')
407
408                 if out:
409                     info.append('F\0')    # not const
410                     info.append('N\0')    # not error or return
411                 info.append(arg.getAttribute('type') + '\0')
412
413             info.append('\0')
414
415         return ''.join(info) + '\0', offsets
416
417     def do_method_glue(self, method, offset):
418         lc_name = method.getAttribute('tp:name-for-bindings')
419         if method.getAttribute('name') != lc_name.replace('_', ''):
420             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
421                     'not match' % (method.getAttribute('name'), lc_name))
422         lc_name = lc_name.lower()
423
424         marshaller = 'g_cclosure_marshal_generic'
425         wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
426
427         self.b("  { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
428
429     def get_signal_glue(self, signals):
430         info = []
431
432         for signal in signals:
433             info.append(self.iface_name)
434             info.append(signal.getAttribute('name'))
435
436         return '\0'.join(info) + '\0\0'
437
438     # the implementation can be the same
439     get_property_glue = get_signal_glue
440
441     def get_method_impl_names(self, method):
442         dbus_method_name = method.getAttribute('name')
443
444         class_member_name = method.getAttribute('tp:name-for-bindings')
445         if dbus_method_name != class_member_name.replace('_', ''):
446             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
447                     'not match' % (dbus_method_name, class_member_name))
448         class_member_name = class_member_name.lower()
449
450         stub_name = (self.prefix_ + self.node_name_lc + '_' +
451                      class_member_name)
452         return (stub_name + '_impl', class_member_name + '_cb')
453
454     def do_method(self, method):
455         assert self.node_name_mixed is not None
456
457         in_class = []
458
459         # Examples refer to Thing.DoStuff (su) -> ii
460
461         # DoStuff
462         dbus_method_name = method.getAttribute('name')
463         # do_stuff
464         class_member_name = method.getAttribute('tp:name-for-bindings')
465         if dbus_method_name != class_member_name.replace('_', ''):
466             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
467                     'not match' % (dbus_method_name, class_member_name))
468         class_member_name = class_member_name.lower()
469
470         # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
471         #   DBusGMethodInvocation *);
472         stub_name = (self.prefix_ + self.node_name_lc + '_' +
473                      class_member_name)
474         # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *,
475         #   const char *, guint, DBusGMethodInvocation);
476         impl_name = stub_name + '_impl'
477         # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *,
478         #   gint, gint);
479         ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' +
480                     class_member_name)
481
482         # Gather arguments
483         in_args = []
484         out_args = []
485         for i in method.getElementsByTagName('arg'):
486             name = i.getAttribute('name')
487             direction = i.getAttribute('direction') or 'in'
488             dtype = i.getAttribute('type')
489
490             assert direction in ('in', 'out')
491
492             if name:
493                 name = direction + '_' + name
494             elif direction == 'in':
495                 name = direction + str(len(in_args))
496             else:
497                 name = direction + str(len(out_args))
498
499             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
500
501             if pointer:
502                 ctype = 'const ' + ctype
503
504             struct = (ctype, name)
505
506             if direction == 'in':
507                 in_args.append(struct)
508             else:
509                 out_args.append(struct)
510
511         # Implementation type declaration (in header, docs separated)
512         self.d('/**')
513         self.d(' * %s:' % impl_name)
514         self.d(' * @self: The object implementing this interface')
515         for (ctype, name) in in_args:
516             self.d(' * @%s: %s (FIXME, generate documentation)'
517                    % (name, ctype))
518         self.d(' * @context: Used to return values or throw an error')
519         self.d(' *')
520         self.d(' * The signature of an implementation of the D-Bus method')
521         self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
522         self.d(' */')
523
524         self.h('typedef void (*%s) (%s%s *self,'
525           % (impl_name, self.Prefix, self.node_name_mixed))
526         for (ctype, name) in in_args:
527             self.h('    %s%s,' % (ctype, name))
528         self.h('    DBusGMethodInvocation *context);')
529
530         # Class member (in class definition)
531         in_class.append('    %s %s;' % (impl_name, class_member_name))
532
533         # Stub definition (in body only - it's static)
534         self.b('static void')
535         self.b('%s (%s%s *self,'
536            % (stub_name, self.Prefix, self.node_name_mixed))
537         for (ctype, name) in in_args:
538             self.b('    %s%s,' % (ctype, name))
539         self.b('    DBusGMethodInvocation *context)')
540         self.b('{')
541         self.b('  %s impl = (%s%s_GET_CLASS (self)->%s_cb);'
542           % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
543         self.b('')
544         self.b('  if (impl != NULL)')
545         tmp = ['self'] + [name for (ctype, name) in in_args] + ['context']
546         self.b('    {')
547         self.b('      (impl) (%s);' % ',\n        '.join(tmp))
548         self.b('    }')
549         self.b('  else')
550         self.b('    {')
551         if self.not_implemented_func:
552             self.b('      %s (context);' % self.not_implemented_func)
553         else:
554             self.b('      GError e = { DBUS_GERROR, ')
555             self.b('           DBUS_GERROR_UNKNOWN_METHOD,')
556             self.b('           "Method not implemented" };')
557             self.b('')
558             self.b('      dbus_g_method_return_error (context, &e);')
559         self.b('    }')
560         self.b('}')
561         self.b('')
562
563         # Implementation registration (in both header and body)
564         self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);'
565                % (self.prefix_, self.node_name_lc, class_member_name,
566                   self.Prefix, self.node_name_mixed, impl_name))
567
568         self.d('/**')
569         self.d(' * %s%s_implement_%s:'
570                % (self.prefix_, self.node_name_lc, class_member_name))
571         self.d(' * @klass: A class whose instances implement this interface')
572         self.d(' * @impl: A callback used to implement the %s D-Bus method'
573                % dbus_method_name)
574         self.d(' *')
575         self.d(' * Register an implementation for the %s method in the vtable'
576                % dbus_method_name)
577         self.d(' * of an implementation of this interface. To be called from')
578         self.d(' * the interface init function.')
579         self.d(' */')
580
581         self.b('void')
582         self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
583                % (self.prefix_, self.node_name_lc, class_member_name,
584                   self.Prefix, self.node_name_mixed, impl_name))
585         self.b('{')
586         self.b('  klass->%s_cb = impl;' % class_member_name)
587         self.b('}')
588         self.b('')
589
590         # Return convenience function (static inline, in header)
591         self.d('/**')
592         self.d(' * %s:' % ret_name)
593         self.d(' * @context: The D-Bus method invocation context')
594         for (ctype, name) in out_args:
595             self.d(' * @%s: %s (FIXME, generate documentation)'
596                    % (name, ctype))
597         self.d(' *')
598         self.d(' * Return successfully by calling dbus_g_method_return().')
599         self.d(' * This inline function exists only to provide type-safety.')
600         self.d(' */')
601         self.d('')
602
603         tmp = (['DBusGMethodInvocation *context'] +
604                [ctype + name for (ctype, name) in out_args])
605         self.h('static inline')
606         self.h('/* this comment is to stop gtkdoc realising this is static */')
607         self.h(('void %s (' % ret_name) + (',\n    '.join(tmp)) + ');')
608         self.h('static inline void')
609         self.h(('%s (' % ret_name) + (',\n    '.join(tmp)) + ')')
610         self.h('{')
611         tmp = ['context'] + [name for (ctype, name) in out_args]
612         self.h('  dbus_g_method_return (' + ',\n      '.join(tmp) + ');')
613         self.h('}')
614         self.h('')
615
616         return in_class
617
618     def get_signal_const_entry(self, signal):
619         assert self.node_name_uc is not None
620         return ('SIGNAL_%s_%s'
621                 % (self.node_name_uc, signal.getAttribute('name')))
622
623     def do_signal(self, signal):
624         assert self.node_name_mixed is not None
625
626         in_base_init = []
627
628         # for signal: Thing::StuffHappened (s, u)
629         # we want to emit:
630         # void tp_svc_thing_emit_stuff_happened (gpointer instance,
631         #    const char *arg0, guint arg1);
632
633         dbus_name = signal.getAttribute('name')
634
635         ugly_name = signal.getAttribute('tp:name-for-bindings')
636         if dbus_name != ugly_name.replace('_', ''):
637             raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
638                     'not match' % (dbus_name, ugly_name))
639
640         stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
641                      ugly_name.lower())
642
643         const_name = self.get_signal_const_entry(signal)
644
645         # Gather arguments
646         args = []
647         for i in signal.getElementsByTagName('arg'):
648             name = i.getAttribute('name')
649             dtype = i.getAttribute('type')
650             tp_type = i.getAttribute('tp:type')
651
652             if name:
653                 name = 'arg_' + name
654             else:
655                 name = 'arg' + str(len(args))
656
657             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
658
659             if pointer:
660                 ctype = 'const ' + ctype
661
662             struct = (ctype, name, gtype)
663             args.append(struct)
664
665         tmp = (['gpointer instance'] +
666                [ctype + name for (ctype, name, gtype) in args])
667
668         self.h(('void %s (' % stub_name) + (',\n    '.join(tmp)) + ');')
669
670         # FIXME: emit docs
671
672         self.d('/**')
673         self.d(' * %s:' % stub_name)
674         self.d(' * @instance: The object implementing this interface')
675         for (ctype, name, gtype) in args:
676             self.d(' * @%s: %s (FIXME, generate documentation)'
677                    % (name, ctype))
678         self.d(' *')
679         self.d(' * Type-safe wrapper around g_signal_emit to emit the')
680         self.d(' * %s signal on interface %s.'
681                % (dbus_name, self.iface_name))
682         self.d(' */')
683
684         self.b('void')
685         self.b(('%s (' % stub_name) + (',\n    '.join(tmp)) + ')')
686         self.b('{')
687         self.b('  g_assert (instance != NULL);')
688         self.b('  g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));'
689                % (self.current_gtype))
690         tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name),
691                 '0'] + [name for (ctype, name, gtype) in args])
692         self.b('  g_signal_emit (' + ',\n      '.join(tmp) + ');')
693         self.b('}')
694         self.b('')
695
696         signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_',
697                 '-')
698
699         self.d('/**')
700         self.d(' * %s%s::%s:'
701                 % (self.Prefix, self.node_name_mixed, signal_name))
702         self.d(' * @self: an object')
703         for (ctype, name, gtype) in args:
704             self.d(' * @%s: %s (FIXME, generate documentation)'
705                    % (name, ctype))
706         self.d(' *')
707         self.d(' * The %s D-Bus signal is emitted whenever '
708                 'this GObject signal is.' % dbus_name)
709         self.d(' */')
710         self.d('')
711
712         in_base_init.append('  %s_signals[%s] ='
713                             % (self.node_name_lc, const_name))
714         in_base_init.append('  g_signal_new ("%s",' % signal_name)
715         in_base_init.append('      G_OBJECT_CLASS_TYPE (klass),')
716         in_base_init.append('      G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
717         in_base_init.append('      0,')
718         in_base_init.append('      NULL, NULL,')
719         in_base_init.append('      g_cclosure_marshal_generic,')
720         in_base_init.append('      G_TYPE_NONE,')
721         tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
722         in_base_init.append('      %s);' % ',\n      '.join(tmp))
723         in_base_init.append('')
724
725         return in_base_init
726
727     def have_properties(self, nodes):
728         for node in nodes:
729             interface =  node.getElementsByTagName('interface')[0]
730             if interface.getElementsByTagName('property'):
731                 return True
732         return False
733
734     def __call__(self):
735         nodes = self.dom.getElementsByTagName('node')
736         nodes.sort(cmp_by_name)
737
738         self.h('#include <glib-object.h>')
739         self.h('#include <dbus/dbus-glib.h>')
740
741         for header in self.headers:
742             self.h('#include %s' % header)
743         self.h('')
744
745         self.h('')
746         self.h('G_BEGIN_DECLS')
747         self.h('')
748
749         self.b('#include "%s.h"' % self.basename)
750         self.b('')
751
752         for node in nodes:
753             self.do_node(node)
754
755         self.h('')
756         self.h('G_END_DECLS')
757
758         self.b('')
759         for header in self.end_headers:
760             self.b('#include %s' % header)
761
762         self.h('')
763         self.b('')
764         file_set_contents(self.basename + '.h', '\n'.join(self.__header))
765         file_set_contents(self.basename + '.c', '\n'.join(self.__body))
766         file_set_contents(self.basename + '-gtk-doc.h', '\n'.join(self.__docs))
767
768 def cmdline_error():
769     print """\
770 usage:
771     gen-ginterface [OPTIONS] xmlfile Prefix_
772 options:
773     --include='<header.h>' (may be repeated)
774     --include='"header.h"' (ditto)
775     --include-end='"header.h"' (ditto)
776         Include extra headers in the generated .c file
777     --signal-marshal-prefix='prefix'
778         Use the given prefix on generated signal marshallers (default is
779         prefix.lower()).
780     --filename='BASENAME'
781         Set the basename for the output files (default is prefix.lower()
782         + 'ginterfaces')
783     --not-implemented-func='symbol'
784         Set action when methods not implemented in the interface vtable are
785         called. symbol must have signature
786             void symbol (DBusGMethodInvocation *context)
787         and return some sort of "not implemented" error via
788             dbus_g_method_return_error (context, ...)
789 """
790     sys.exit(1)
791
792
793 if __name__ == '__main__':
794     from getopt import gnu_getopt
795
796     options, argv = gnu_getopt(sys.argv[1:], '',
797                                ['filename=', 'signal-marshal-prefix=',
798                                 'include=', 'include-end=',
799                                 'allow-unstable',
800                                 'not-implemented-func='])
801
802     try:
803         prefix = argv[1]
804     except IndexError:
805         cmdline_error()
806
807     basename = prefix.lower() + 'ginterfaces'
808     signal_marshal_prefix = prefix.lower().rstrip('_')
809     headers = []
810     end_headers = []
811     not_implemented_func = ''
812     allow_havoc = False
813
814     for option, value in options:
815         if option == '--filename':
816             basename = value
817         elif option == '--signal-marshal-prefix':
818             signal_marshal_prefix = value
819         elif option == '--include':
820             if value[0] not in '<"':
821                 value = '"%s"' % value
822             headers.append(value)
823         elif option == '--include-end':
824             if value[0] not in '<"':
825                 value = '"%s"' % value
826             end_headers.append(value)
827         elif option == '--not-implemented-func':
828             not_implemented_func = value
829         elif option == '--allow-unstable':
830             allow_havoc = True
831
832     try:
833         dom = xml.dom.minidom.parse(argv[0])
834     except IndexError:
835         cmdline_error()
836
837     Generator(dom, prefix, basename, signal_marshal_prefix, headers,
838               end_headers, not_implemented_func, allow_havoc)()