~ubuntu-branches/ubuntu/natty/empathy/natty-updates

« back to all changes in this revision

Viewing changes to tools/glib-ginterface-gen.py

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons
  • Date: 2008-03-10 16:39:07 UTC
  • mfrom: (1.1.13 upstream)
  • Revision ID: james.westby@ubuntu.com-20080310163907-tv41g2zmf0qqgi85
Tags: 0.22.0-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 libglibcodegen import Signature, type_to_gtype, cmp_by_name, \
 
30
        camelcase_to_lower, NS_TP, dbus_gutils_wincaps_to_uscore, \
 
31
        signal_to_marshal_name, method_to_glue_marshal_name
 
32
 
 
33
 
 
34
NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
 
35
 
 
36
class Generator(object):
 
37
 
 
38
    def __init__(self, dom, prefix, basename, signal_marshal_prefix,
 
39
                 headers, end_headers, not_implemented_func,
 
40
                 allow_havoc):
 
41
        self.dom = dom
 
42
        self.__header = []
 
43
        self.__body = []
 
44
 
 
45
        assert prefix.endswith('_')
 
46
        assert not signal_marshal_prefix.endswith('_')
 
47
 
 
48
        # The main_prefix, sub_prefix thing is to get:
 
49
        # FOO_ -> (FOO_, _)
 
50
        # FOO_SVC_ -> (FOO_, _SVC_)
 
51
        # but
 
52
        # FOO_BAR/ -> (FOO_BAR_, _)
 
53
        # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_)
 
54
 
 
55
        if '/' in prefix:
 
56
            main_prefix, sub_prefix = prefix.upper().split('/', 1)
 
57
            prefix = prefix.replace('/', '_')
 
58
        else:
 
59
            main_prefix, sub_prefix = prefix.upper().split('_', 1)
 
60
 
 
61
        self.MAIN_PREFIX_ = main_prefix + '_'
 
62
        self._SUB_PREFIX_ = '_' + sub_prefix
 
63
 
 
64
        self.Prefix_ = prefix
 
65
        self.Prefix = prefix.replace('_', '')
 
66
        self.prefix_ = prefix.lower()
 
67
        self.PREFIX_ = prefix.upper()
 
68
 
 
69
        self.signal_marshal_prefix = signal_marshal_prefix
 
70
        self.headers = headers
 
71
        self.end_headers = end_headers
 
72
        self.not_implemented_func = not_implemented_func
 
73
        self.allow_havoc = allow_havoc
 
74
 
 
75
    def h(self, s):
 
76
        self.__header.append(s)
 
77
 
 
78
    def b(self, s):
 
79
        self.__body.append(s)
 
80
 
 
81
    def do_node(self, node):
 
82
        node_name = node.getAttribute('name').replace('/', '')
 
83
        node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
 
84
        node_name_lc = self.node_name_lc = node_name.lower()
 
85
        node_name_uc = self.node_name_uc = node_name.upper()
 
86
 
 
87
        interfaces = node.getElementsByTagName('interface')
 
88
        assert len(interfaces) == 1, interfaces
 
89
        interface = interfaces[0]
 
90
        self.iface_name = interface.getAttribute('name')
 
91
 
 
92
        tmp = interface.getAttribute('tp:implement-service')
 
93
        if tmp == "no":
 
94
            return
 
95
 
 
96
        tmp = interface.getAttribute('tp:causes-havoc')
 
97
        if tmp and not self.allow_havoc:
 
98
            raise AssertionError('%s is %s' % (self.iface_name, tmp))
 
99
 
 
100
        self.b('static const DBusGObjectInfo _%s%s_object_info;'
 
101
               % (self.prefix_, node_name_lc))
 
102
        self.b('')
 
103
 
 
104
        methods = interface.getElementsByTagName('method')
 
105
        signals = interface.getElementsByTagName('signal')
 
106
        properties = interface.getElementsByTagName('property')
 
107
        # Don't put properties in dbus-glib glue
 
108
        glue_properties = []
 
109
 
 
110
        self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
 
111
        self.b('    GTypeInterface parent_class;')
 
112
        for method in methods:
 
113
            self.b('    %s %s;' % self.get_method_impl_names(method))
 
114
        self.b('};')
 
115
        self.b('')
 
116
 
 
117
        if signals:
 
118
            self.b('enum {')
 
119
            for signal in signals:
 
120
                self.b('    %s,' % self.get_signal_const_entry(signal))
 
121
            self.b('    N_%s_SIGNALS' % node_name_uc)
 
122
            self.b('};')
 
123
            self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
 
124
                   % (node_name_lc, node_name_uc))
 
125
            self.b('')
 
126
 
 
127
        self.b('static void %s%s_base_init (gpointer klass);'
 
128
               % (self.prefix_, node_name_lc))
 
129
        self.b('')
 
130
 
 
131
        self.b('GType')
 
132
        self.b('%s%s_get_type (void)'
 
133
               % (self.prefix_, node_name_lc))
 
134
        self.b('{')
 
135
        self.b('  static GType type = 0;')
 
136
        self.b('')
 
137
        self.b('  if (G_UNLIKELY (type == 0))')
 
138
        self.b('    {')
 
139
        self.b('      static const GTypeInfo info = {')
 
140
        self.b('        sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
 
141
        self.b('        %s%s_base_init, /* base_init */'
 
142
               % (self.prefix_, node_name_lc))
 
143
        self.b('        NULL, /* base_finalize */')
 
144
        self.b('        NULL, /* class_init */')
 
145
        self.b('        NULL, /* class_finalize */')
 
146
        self.b('        NULL, /* class_data */')
 
147
        self.b('        0,')
 
148
        self.b('        0, /* n_preallocs */')
 
149
        self.b('        NULL /* instance_init */')
 
150
        self.b('      };')
 
151
        self.b('')
 
152
        self.b('      type = g_type_register_static (G_TYPE_INTERFACE,')
 
153
        self.b('          "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
 
154
        self.b('    }')
 
155
        self.b('')
 
156
        self.b('  return type;')
 
157
        self.b('}')
 
158
        self.b('')
 
159
 
 
160
        self.h('/**')
 
161
        self.h(' * %s%s:' % (self.Prefix, node_name_mixed))
 
162
        self.h(' *')
 
163
        self.h(' * Dummy typedef representing any implementation of this '
 
164
               'interface.')
 
165
        self.h(' */')
 
166
        self.h('typedef struct _%s%s %s%s;'
 
167
               % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
 
168
        self.h('')
 
169
        self.h('/**')
 
170
        self.h(' * %s%sClass:' % (self.Prefix, node_name_mixed))
 
171
        self.h(' *')
 
172
        self.h(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
 
173
        self.h(' */')
 
174
        self.h('typedef struct _%s%sClass %s%sClass;'
 
175
               % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
 
176
        self.h('')
 
177
        self.h('GType %s%s_get_type (void);'
 
178
               % (self.prefix_, node_name_lc))
 
179
 
 
180
        gtype = self.current_gtype = \
 
181
                self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
 
182
        classname = self.Prefix + node_name_mixed
 
183
 
 
184
        self.h('#define %s \\\n  (%s%s_get_type ())'
 
185
               % (gtype, self.prefix_, node_name_lc))
 
186
        self.h('#define %s%s(obj) \\\n'
 
187
               '  (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
 
188
               % (self.PREFIX_, node_name_uc, gtype, classname))
 
189
        self.h('#define %sIS%s%s(obj) \\\n'
 
190
               '  (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
 
191
               % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
 
192
        self.h('#define %s%s_GET_CLASS(obj) \\\n'
 
193
               '  (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
 
194
               % (self.PREFIX_, node_name_uc, gtype, classname))
 
195
        self.h('')
 
196
        self.h('')
 
197
 
 
198
        base_init_code = []
 
199
 
 
200
        for method in methods:
 
201
            self.do_method(method)
 
202
 
 
203
        for signal in signals:
 
204
            base_init_code.extend(self.do_signal(signal))
 
205
 
 
206
        self.b('static inline void')
 
207
        self.b('%s%s_base_init_once (gpointer klass)'
 
208
               % (self.prefix_, node_name_lc))
 
209
        self.b('{')
 
210
        self.b('  static TpDBusPropertiesMixinPropInfo properties[%d] = {'
 
211
               % (len(properties) + 1))
 
212
 
 
213
        for m in properties:
 
214
            access = m.getAttribute('access')
 
215
            assert access in ('read', 'write', 'readwrite')
 
216
 
 
217
            if access == 'read':
 
218
                flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
 
219
            elif access == 'write':
 
220
                flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
 
221
            else:
 
222
                flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
 
223
                         'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
 
224
 
 
225
            self.b('      { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
 
226
                   % (flags, m.getAttribute('type'), m.getAttribute('name')))
 
227
 
 
228
        self.b('      { 0, 0, NULL, 0, NULL, NULL }')
 
229
        self.b('  };')
 
230
        self.b('  static TpDBusPropertiesMixinIfaceInfo interface =')
 
231
        self.b('      { 0, properties, NULL, NULL };')
 
232
        self.b('')
 
233
        self.b('  interface.dbus_interface = g_quark_from_static_string '
 
234
               '("%s");' % self.iface_name)
 
235
 
 
236
        for i, m in enumerate(properties):
 
237
            self.b('  properties[%d].name = g_quark_from_static_string ("%s");'
 
238
                   % (i, m.getAttribute('name')))
 
239
            self.b('  properties[%d].type = %s;'
 
240
                   % (i, type_to_gtype(m.getAttribute('type'))[1]))
 
241
 
 
242
        self.b('  tp_svc_interface_set_dbus_properties_info (%s, &interface);'
 
243
               % self.current_gtype)
 
244
 
 
245
        self.b('')
 
246
        for s in base_init_code:
 
247
            self.b(s)
 
248
        self.b('  dbus_g_object_type_install_info (%s%s_get_type (),'
 
249
               % (self.prefix_, node_name_lc))
 
250
        self.b('      &_%s%s_object_info);'
 
251
               % (self.prefix_, node_name_lc))
 
252
        self.b('}')
 
253
 
 
254
        self.b('static void')
 
255
        self.b('%s%s_base_init (gpointer klass)'
 
256
               % (self.prefix_, node_name_lc))
 
257
        self.b('{')
 
258
        self.b('  static gboolean initialized = FALSE;')
 
259
        self.b('')
 
260
        self.b('  if (!initialized)')
 
261
        self.b('    {')
 
262
        self.b('      initialized = TRUE;')
 
263
        self.b('      %s%s_base_init_once (klass);'
 
264
               % (self.prefix_, node_name_lc))
 
265
        self.b('    }')
 
266
        # insert anything we need to do per implementation here
 
267
        self.b('}')
 
268
 
 
269
        self.h('')
 
270
 
 
271
        self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
 
272
               % (self.prefix_, node_name_lc))
 
273
 
 
274
        method_blob, offsets = self.get_method_glue(methods)
 
275
 
 
276
        for method, offset in zip(methods, offsets):
 
277
            self.do_method_glue(method, offset)
 
278
 
 
279
        self.b('};')
 
280
        self.b('')
 
281
 
 
282
        self.b('static const DBusGObjectInfo _%s%s_object_info = {'
 
283
               % (self.prefix_, node_name_lc))
 
284
        self.b('  0,')  # version
 
285
        self.b('  _%s%s_methods,' % (self.prefix_, node_name_lc))
 
286
        self.b('  %d,' % len(methods))
 
287
        self.b('"' + method_blob.replace('\0', '\\0') + '",')
 
288
        self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
 
289
        self.b('"' +
 
290
               self.get_property_glue(glue_properties).replace('\0', '\\0') +
 
291
               '",')
 
292
        self.b('};')
 
293
        self.b('')
 
294
 
 
295
        self.node_name_mixed = None
 
296
        self.node_name_lc = None
 
297
        self.node_name_uc = None
 
298
 
 
299
    def get_method_glue(self, methods):
 
300
        info = []
 
301
        offsets = []
 
302
 
 
303
        for method in methods:
 
304
            offsets.append(len(''.join(info)))
 
305
 
 
306
            info.append(self.iface_name + '\0')
 
307
            info.append(method.getAttribute('name') + '\0')
 
308
 
 
309
            info.append('A\0')    # async
 
310
 
 
311
            counter = 0
 
312
            for arg in method.getElementsByTagName('arg'):
 
313
                out = arg.getAttribute('direction') == 'out'
 
314
 
 
315
                name = arg.getAttribute('name')
 
316
                if not name:
 
317
                    assert out
 
318
                    name = 'arg%u' % counter
 
319
                counter += 1
 
320
 
 
321
                info.append(name + '\0')
 
322
 
 
323
                if out:
 
324
                    info.append('O\0')
 
325
                else:
 
326
                    info.append('I\0')
 
327
 
 
328
                if out:
 
329
                    info.append('F\0')    # not const
 
330
                    info.append('N\0')    # not error or return
 
331
                info.append(arg.getAttribute('type') + '\0')
 
332
 
 
333
            info.append('\0')
 
334
 
 
335
        return ''.join(info) + '\0', offsets
 
336
 
 
337
    def do_method_glue(self, method, offset):
 
338
        lc_name = camelcase_to_lower(method.getAttribute('name'))
 
339
 
 
340
        marshaller = method_to_glue_marshal_name(method,
 
341
                self.signal_marshal_prefix)
 
342
        wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
 
343
 
 
344
        self.b("  { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
 
345
 
 
346
    def get_signal_glue(self, signals):
 
347
        info = []
 
348
 
 
349
        for signal in signals:
 
350
            info.append(self.iface_name)
 
351
            info.append(signal.getAttribute('name'))
 
352
 
 
353
        return '\0'.join(info) + '\0\0'
 
354
 
 
355
    # the implementation can be the same
 
356
    get_property_glue = get_signal_glue
 
357
 
 
358
    def get_method_impl_names(self, method):
 
359
        dbus_method_name = method.getAttribute('name')
 
360
        class_member_name = camelcase_to_lower(dbus_method_name)
 
361
        stub_name = (self.prefix_ + self.node_name_lc + '_' +
 
362
                     class_member_name)
 
363
        return (stub_name + '_impl', class_member_name)
 
364
 
 
365
    def do_method(self, method):
 
366
        assert self.node_name_mixed is not None
 
367
 
 
368
        in_class = []
 
369
 
 
370
        # Examples refer to Thing.DoStuff (su) -> ii
 
371
 
 
372
        # DoStuff
 
373
        dbus_method_name = method.getAttribute('name')
 
374
        # do_stuff
 
375
        class_member_name = camelcase_to_lower(dbus_method_name)
 
376
        # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
 
377
        #   DBusGMethodInvocation *);
 
378
        stub_name = (self.prefix_ + self.node_name_lc + '_' +
 
379
                     class_member_name)
 
380
        # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *,
 
381
        #   const char *, guint, DBusGMethodInvocation);
 
382
        impl_name = stub_name + '_impl'
 
383
        # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *,
 
384
        #   gint, gint);
 
385
        ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' +
 
386
                    class_member_name)
 
387
 
 
388
        # Gather arguments
 
389
        in_args = []
 
390
        out_args = []
 
391
        for i in method.getElementsByTagName('arg'):
 
392
            name = i.getAttribute('name')
 
393
            direction = i.getAttribute('direction') or 'in'
 
394
            dtype = i.getAttribute('type')
 
395
 
 
396
            assert direction in ('in', 'out')
 
397
 
 
398
            if name:
 
399
                name = direction + '_' + name
 
400
            elif direction == 'in':
 
401
                name = direction + str(len(in_args))
 
402
            else:
 
403
                name = direction + str(len(out_args))
 
404
 
 
405
            ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
 
406
 
 
407
            if pointer:
 
408
                ctype = 'const ' + ctype
 
409
 
 
410
            struct = (ctype, name)
 
411
 
 
412
            if direction == 'in':
 
413
                in_args.append(struct)
 
414
            else:
 
415
                out_args.append(struct)
 
416
 
 
417
        # Implementation type declaration (in header, docs in body)
 
418
        self.b('/**')
 
419
        self.b(' * %s:' % impl_name)
 
420
        self.b(' * @self: The object implementing this interface')
 
421
        for (ctype, name) in in_args:
 
422
            self.b(' * @%s: %s (FIXME, generate documentation)'
 
423
                   % (name, ctype))
 
424
        self.b(' * @context: Used to return values or throw an error')
 
425
        self.b(' *')
 
426
        self.b(' * The signature of an implementation of the D-Bus method')
 
427
        self.b(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
 
428
        self.b(' */')
 
429
        self.h('typedef void (*%s) (%s%s *self,'
 
430
          % (impl_name, self.Prefix, self.node_name_mixed))
 
431
        for (ctype, name) in in_args:
 
432
            self.h('    %s%s,' % (ctype, name))
 
433
        self.h('    DBusGMethodInvocation *context);')
 
434
 
 
435
        # Class member (in class definition)
 
436
        in_class.append('    %s %s;' % (impl_name, class_member_name))
 
437
 
 
438
        # Stub definition (in body only - it's static)
 
439
        self.b('static void')
 
440
        self.b('%s (%s%s *self,'
 
441
           % (stub_name, self.Prefix, self.node_name_mixed))
 
442
        for (ctype, name) in in_args:
 
443
            self.b('    %s%s,' % (ctype, name))
 
444
        self.b('    DBusGMethodInvocation *context)')
 
445
        self.b('{')
 
446
        self.b('  %s impl = (%s%s_GET_CLASS (self)->%s);'
 
447
          % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
 
448
        self.b('')
 
449
        self.b('  if (impl != NULL)')
 
450
        tmp = ['self'] + [name for (ctype, name) in in_args] + ['context']
 
451
        self.b('    {')
 
452
        self.b('      (impl) (%s);' % ',\n        '.join(tmp))
 
453
        self.b('    }')
 
454
        self.b('  else')
 
455
        self.b('    {')
 
456
        if self.not_implemented_func:
 
457
            self.b('      %s (context);' % self.not_implemented_func)
 
458
        else:
 
459
            self.b('      GError e = { DBUS_GERROR, ')
 
460
            self.b('           DBUS_GERROR_UNKNOWN_METHOD,')
 
461
            self.b('           "Method not implemented" };')
 
462
            self.b('')
 
463
            self.b('      dbus_g_method_return_error (context, &e);')
 
464
        self.b('    }')
 
465
        self.b('}')
 
466
        self.b('')
 
467
 
 
468
        # Implementation registration (in both header and body)
 
469
        self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);'
 
470
               % (self.prefix_, self.node_name_lc, class_member_name,
 
471
                  self.Prefix, self.node_name_mixed, impl_name))
 
472
 
 
473
        self.b('/**')
 
474
        self.b(' * %s%s_implement_%s:'
 
475
               % (self.prefix_, self.node_name_lc, class_member_name))
 
476
        self.b(' * @klass: A class whose instances implement this interface')
 
477
        self.b(' * @impl: A callback used to implement the %s D-Bus method'
 
478
               % dbus_method_name)
 
479
        self.b(' *')
 
480
        self.b(' * Register an implementation for the %s method in the vtable'
 
481
               % dbus_method_name)
 
482
        self.b(' * of an implementation of this interface. To be called from')
 
483
        self.b(' * the interface init function.')
 
484
        self.b(' */')
 
485
        self.b('void')
 
486
        self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
 
487
               % (self.prefix_, self.node_name_lc, class_member_name,
 
488
                  self.Prefix, self.node_name_mixed, impl_name))
 
489
        self.b('{')
 
490
        self.b('  klass->%s = impl;' % class_member_name)
 
491
        self.b('}')
 
492
        self.b('')
 
493
 
 
494
        # Return convenience function (static inline, in header)
 
495
        self.h('/**')
 
496
        self.h(' * %s:' % ret_name)
 
497
        self.h(' * @context: The D-Bus method invocation context')
 
498
        for (ctype, name) in out_args:
 
499
            self.h(' * @%s: %s (FIXME, generate documentation)'
 
500
                   % (name, ctype))
 
501
        self.h(' *')
 
502
        self.h(' * Return successfully by calling dbus_g_method_return().')
 
503
        self.h(' * This inline function exists only to provide type-safety.')
 
504
        self.h(' */')
 
505
        tmp = (['DBusGMethodInvocation *context'] +
 
506
               [ctype + name for (ctype, name) in out_args])
 
507
        self.h('static inline')
 
508
        self.h('/* this comment is to stop gtkdoc realising this is static */')
 
509
        self.h(('void %s (' % ret_name) + (',\n    '.join(tmp)) + ');')
 
510
        self.h('static inline void')
 
511
        self.h(('%s (' % ret_name) + (',\n    '.join(tmp)) + ')')
 
512
        self.h('{')
 
513
        tmp = ['context'] + [name for (ctype, name) in out_args]
 
514
        self.h('  dbus_g_method_return (' + ',\n      '.join(tmp) + ');')
 
515
        self.h('}')
 
516
        self.h('')
 
517
 
 
518
        return in_class
 
519
 
 
520
    def get_signal_const_entry(self, signal):
 
521
        assert self.node_name_uc is not None
 
522
        return ('SIGNAL_%s_%s'
 
523
                % (self.node_name_uc, signal.getAttribute('name')))
 
524
 
 
525
    def do_signal(self, signal):
 
526
        assert self.node_name_mixed is not None
 
527
 
 
528
        in_base_init = []
 
529
 
 
530
        # for signal: Thing::StuffHappened (s, u)
 
531
        # we want to emit:
 
532
        # void tp_svc_thing_emit_stuff_happened (gpointer instance,
 
533
        #    const char *arg0, guint arg1);
 
534
 
 
535
        dbus_name = signal.getAttribute('name')
 
536
        stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
 
537
                     camelcase_to_lower(dbus_name))
 
538
        const_name = self.get_signal_const_entry(signal)
 
539
 
 
540
        # Gather arguments
 
541
        args = []
 
542
        for i in signal.getElementsByTagName('arg'):
 
543
            name = i.getAttribute('name')
 
544
            dtype = i.getAttribute('type')
 
545
            tp_type = i.getAttribute('tp:type')
 
546
 
 
547
            if name:
 
548
                name = 'arg_' + name
 
549
            else:
 
550
                name = 'arg' + str(len(args))
 
551
 
 
552
            ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
 
553
 
 
554
            if pointer:
 
555
                ctype = 'const ' + ctype
 
556
 
 
557
            struct = (ctype, name, gtype)
 
558
            args.append(struct)
 
559
 
 
560
        tmp = (['gpointer instance'] +
 
561
               [ctype + name for (ctype, name, gtype) in args])
 
562
 
 
563
        self.h(('void %s (' % stub_name) + (',\n    '.join(tmp)) + ');')
 
564
 
 
565
        # FIXME: emit docs
 
566
 
 
567
        self.b('/**')
 
568
        self.b(' * %s:' % stub_name)
 
569
        self.b(' * @instance: The object implementing this interface')
 
570
        for (ctype, name, gtype) in args:
 
571
            self.b(' * @%s: %s (FIXME, generate documentation)'
 
572
                   % (name, ctype))
 
573
        self.b(' *')
 
574
        self.b(' * Type-safe wrapper around g_signal_emit to emit the')
 
575
        self.b(' * %s signal on interface %s.'
 
576
               % (dbus_name, self.iface_name))
 
577
        self.b(' */')
 
578
 
 
579
        self.b('void')
 
580
        self.b(('%s (' % stub_name) + (',\n    '.join(tmp)) + ')')
 
581
        self.b('{')
 
582
        self.b('  g_assert (instance != NULL);')
 
583
        self.b('  g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));'
 
584
               % (self.current_gtype))
 
585
        tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name),
 
586
                '0'] + [name for (ctype, name, gtype) in args])
 
587
        self.b('  g_signal_emit (' + ',\n      '.join(tmp) + ');')
 
588
        self.b('}')
 
589
        self.b('')
 
590
 
 
591
        in_base_init.append('  %s_signals[%s] ='
 
592
                            % (self.node_name_lc, const_name))
 
593
        in_base_init.append('  g_signal_new ("%s",'
 
594
                % (dbus_gutils_wincaps_to_uscore(dbus_name).replace('_', '-')))
 
595
        in_base_init.append('      G_OBJECT_CLASS_TYPE (klass),')
 
596
        in_base_init.append('      G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
 
597
        in_base_init.append('      0,')
 
598
        in_base_init.append('      NULL, NULL,')
 
599
        in_base_init.append('      %s,'
 
600
                % signal_to_marshal_name(signal, self.signal_marshal_prefix))
 
601
        in_base_init.append('      G_TYPE_NONE,')
 
602
        tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
 
603
        in_base_init.append('      %s);' % ',\n      '.join(tmp))
 
604
        in_base_init.append('')
 
605
 
 
606
        return in_base_init
 
607
 
 
608
    def __call__(self):
 
609
        self.h('#include <glib-object.h>')
 
610
        self.h('#include <dbus/dbus-glib.h>')
 
611
        self.h('#include <telepathy-glib/dbus-properties-mixin.h>')
 
612
        self.h('')
 
613
        self.h('G_BEGIN_DECLS')
 
614
        self.h('')
 
615
 
 
616
        self.b('#include "%s.h"' % basename)
 
617
        self.b('')
 
618
        for header in self.headers:
 
619
            self.b('#include %s' % header)
 
620
        self.b('')
 
621
 
 
622
        nodes = self.dom.getElementsByTagName('node')
 
623
        nodes.sort(cmp_by_name)
 
624
 
 
625
        for node in nodes:
 
626
            self.do_node(node)
 
627
 
 
628
        self.h('')
 
629
        self.h('G_END_DECLS')
 
630
 
 
631
        self.b('')
 
632
        for header in self.end_headers:
 
633
            self.b('#include %s' % header)
 
634
 
 
635
        self.h('')
 
636
        self.b('')
 
637
        open(basename + '.h', 'w').write('\n'.join(self.__header))
 
638
        open(basename + '.c', 'w').write('\n'.join(self.__body))
 
639
 
 
640
 
 
641
def cmdline_error():
 
642
    print """\
 
643
usage:
 
644
    gen-ginterface [OPTIONS] xmlfile Prefix_
 
645
options:
 
646
    --include='<header.h>' (may be repeated)
 
647
    --include='"header.h"' (ditto)
 
648
    --include-end='"header.h"' (ditto)
 
649
        Include extra headers in the generated .c file
 
650
    --signal-marshal-prefix='prefix'
 
651
        Use the given prefix on generated signal marshallers (default is
 
652
        prefix.lower()).
 
653
    --filename='BASENAME'
 
654
        Set the basename for the output files (default is prefix.lower()
 
655
        + 'ginterfaces')
 
656
    --not-implemented-func='symbol'
 
657
        Set action when methods not implemented in the interface vtable are
 
658
        called. symbol must have signature
 
659
            void symbol (DBusGMethodInvocation *context)
 
660
        and return some sort of "not implemented" error via
 
661
            dbus_g_method_return_error (context, ...)
 
662
"""
 
663
    sys.exit(1)
 
664
 
 
665
 
 
666
if __name__ == '__main__':
 
667
    from getopt import gnu_getopt
 
668
 
 
669
    options, argv = gnu_getopt(sys.argv[1:], '',
 
670
                               ['filename=', 'signal-marshal-prefix=',
 
671
                                'include=', 'include-end=',
 
672
                                'allow-unstable',
 
673
                                'not-implemented-func='])
 
674
 
 
675
    try:
 
676
        prefix = argv[1]
 
677
    except IndexError:
 
678
        cmdline_error()
 
679
 
 
680
    basename = prefix.lower() + 'ginterfaces'
 
681
    signal_marshal_prefix = prefix.lower().rstrip('_')
 
682
    headers = []
 
683
    end_headers = []
 
684
    not_implemented_func = ''
 
685
    allow_havoc = False
 
686
 
 
687
    for option, value in options:
 
688
        if option == '--filename':
 
689
            basename = value
 
690
        elif option == '--signal-marshal-prefix':
 
691
            signal_marshal_prefix = value
 
692
        elif option == '--include':
 
693
            if value[0] not in '<"':
 
694
                value = '"%s"' % value
 
695
            headers.append(value)
 
696
        elif option == '--include-end':
 
697
            if value[0] not in '<"':
 
698
                value = '"%s"' % value
 
699
            end_headers.append(value)
 
700
        elif option == '--not-implemented-func':
 
701
            not_implemented_func = value
 
702
        elif option == '--allow-unstable':
 
703
            allow_havoc = True
 
704
 
 
705
    try:
 
706
        dom = xml.dom.minidom.parse(argv[0])
 
707
    except IndexError:
 
708
        cmdline_error()
 
709
 
 
710
    Generator(dom, prefix, basename, signal_marshal_prefix, headers,
 
711
              end_headers, not_implemented_func, allow_havoc)()