File indexing completed on 2024-04-28 12:45:42

0001 #!/usr/bin/python
0002 
0003 # glib-ginterface-gen.py: service-side interface generator
0004 #
0005 # Generate dbus-glib 0.x service GInterfaces from the Telepathy specification.
0006 # The master copy of this program is in the telepathy-glib repository -
0007 # please make any changes there.
0008 #
0009 # Copyright (C) 2006, 2007 Collabora Limited
0010 #
0011 # This library is free software; you can redistribute it and/or
0012 # modify it under the terms of the GNU Lesser General Public
0013 # License as published by the Free Software Foundation; either
0014 # version 2.1 of the License, or (at your option) any later version.
0015 #
0016 # This library is distributed in the hope that it will be useful,
0017 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0018 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0019 # Lesser General Public License for more details.
0020 #
0021 # You should have received a copy of the GNU Lesser General Public
0022 # License along with this library; if not, write to the Free Software
0023 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
0024 
0025 import sys
0026 import os.path
0027 import xml.dom.minidom
0028 
0029 from libtpcodegen import file_set_contents, key_by_name, u
0030 from libglibcodegen import Signature, type_to_gtype, \
0031         NS_TP, dbus_gutils_wincaps_to_uscore
0032 
0033 
0034 NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0"
0035 
0036 def get_emits_changed(node):
0037     try:
0038         return [
0039             annotation.getAttribute('value')
0040             for annotation in node.getElementsByTagName('annotation')
0041             if annotation.getAttribute('name') == 'org.freedesktop.DBus.Property.EmitsChangedSignal'
0042             ][0]
0043     except IndexError:
0044         return None
0045 
0046 class Generator(object):
0047 
0048     def __init__(self, dom, prefix, basename, signal_marshal_prefix,
0049                  headers, end_headers, not_implemented_func,
0050                  allow_havoc):
0051         self.dom = dom
0052         self.__header = []
0053         self.__body = []
0054         self.__docs = []
0055 
0056         assert prefix.endswith('_')
0057         assert not signal_marshal_prefix.endswith('_')
0058 
0059         # The main_prefix, sub_prefix thing is to get:
0060         # FOO_ -> (FOO_, _)
0061         # FOO_SVC_ -> (FOO_, _SVC_)
0062         # but
0063         # FOO_BAR/ -> (FOO_BAR_, _)
0064         # FOO_BAR/SVC_ -> (FOO_BAR_, _SVC_)
0065 
0066         if '/' in prefix:
0067             main_prefix, sub_prefix = prefix.upper().split('/', 1)
0068             prefix = prefix.replace('/', '_')
0069         else:
0070             main_prefix, sub_prefix = prefix.upper().split('_', 1)
0071 
0072         self.MAIN_PREFIX_ = main_prefix + '_'
0073         self._SUB_PREFIX_ = '_' + sub_prefix
0074 
0075         self.Prefix_ = prefix
0076         self.Prefix = prefix.replace('_', '')
0077         self.prefix_ = prefix.lower()
0078         self.PREFIX_ = prefix.upper()
0079 
0080         self.basename = basename
0081         self.signal_marshal_prefix = signal_marshal_prefix
0082         self.headers = headers
0083         self.end_headers = end_headers
0084         self.not_implemented_func = not_implemented_func
0085         self.allow_havoc = allow_havoc
0086 
0087     def h(self, s):
0088         self.__header.append(s)
0089 
0090     def b(self, s):
0091         self.__body.append(s)
0092 
0093     def d(self, s):
0094         self.__docs.append(s)
0095 
0096     def do_node(self, node):
0097         node_name = node.getAttribute('name').replace('/', '')
0098         node_name_mixed = self.node_name_mixed = node_name.replace('_', '')
0099         node_name_lc = self.node_name_lc = node_name.lower()
0100         node_name_uc = self.node_name_uc = node_name.upper()
0101 
0102         interfaces = node.getElementsByTagName('interface')
0103         assert len(interfaces) == 1, interfaces
0104         interface = interfaces[0]
0105         self.iface_name = interface.getAttribute('name')
0106 
0107         tmp = interface.getAttribute('tp:implement-service')
0108         if tmp == "no":
0109             return
0110 
0111         tmp = interface.getAttribute('tp:causes-havoc')
0112         if tmp and not self.allow_havoc:
0113             raise AssertionError('%s is %s' % (self.iface_name, tmp))
0114 
0115         iface_emits_changed = get_emits_changed(interface)
0116 
0117         self.b('static const DBusGObjectInfo _%s%s_object_info;'
0118                % (self.prefix_, node_name_lc))
0119         self.b('')
0120 
0121         methods = interface.getElementsByTagName('method')
0122         signals = interface.getElementsByTagName('signal')
0123         properties = interface.getElementsByTagName('property')
0124         # Don't put properties in dbus-glib glue
0125         glue_properties = []
0126 
0127         self.b('struct _%s%sClass {' % (self.Prefix, node_name_mixed))
0128         self.b('    GTypeInterface parent_class;')
0129         for method in methods:
0130             self.b('    %s %s;' % self.get_method_impl_names(method))
0131         self.b('};')
0132         self.b('')
0133 
0134         if signals:
0135             self.b('enum {')
0136             for signal in signals:
0137                 self.b('    %s,' % self.get_signal_const_entry(signal))
0138             self.b('    N_%s_SIGNALS' % node_name_uc)
0139             self.b('};')
0140             self.b('static guint %s_signals[N_%s_SIGNALS] = {0};'
0141                    % (node_name_lc, node_name_uc))
0142             self.b('')
0143 
0144         self.b('static void %s%s_base_init (gpointer klass);'
0145                % (self.prefix_, node_name_lc))
0146         self.b('')
0147 
0148         self.b('GType')
0149         self.b('%s%s_get_type (void)'
0150                % (self.prefix_, node_name_lc))
0151         self.b('{')
0152         self.b('  static GType type = 0;')
0153         self.b('')
0154         self.b('  if (G_UNLIKELY (type == 0))')
0155         self.b('    {')
0156         self.b('      static const GTypeInfo info = {')
0157         self.b('        sizeof (%s%sClass),' % (self.Prefix, node_name_mixed))
0158         self.b('        %s%s_base_init, /* base_init */'
0159                % (self.prefix_, node_name_lc))
0160         self.b('        NULL, /* base_finalize */')
0161         self.b('        NULL, /* class_init */')
0162         self.b('        NULL, /* class_finalize */')
0163         self.b('        NULL, /* class_data */')
0164         self.b('        0,')
0165         self.b('        0, /* n_preallocs */')
0166         self.b('        NULL /* instance_init */')
0167         self.b('      };')
0168         self.b('')
0169         self.b('      type = g_type_register_static (G_TYPE_INTERFACE,')
0170         self.b('          "%s%s", &info, 0);' % (self.Prefix, node_name_mixed))
0171         self.b('    }')
0172         self.b('')
0173         self.b('  return type;')
0174         self.b('}')
0175         self.b('')
0176 
0177         self.d('/**')
0178         self.d(' * %s%s:' % (self.Prefix, node_name_mixed))
0179         self.d(' *')
0180         self.d(' * Dummy typedef representing any implementation of this '
0181                'interface.')
0182         self.d(' */')
0183 
0184         self.h('typedef struct _%s%s %s%s;'
0185                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
0186         self.h('')
0187 
0188         self.d('/**')
0189         self.d(' * %s%sClass:' % (self.Prefix, node_name_mixed))
0190         self.d(' *')
0191         self.d(' * The class of %s%s.' % (self.Prefix, node_name_mixed))
0192 
0193         if methods:
0194             self.d(' *')
0195             self.d(' * In a full implementation of this interface (i.e. all')
0196             self.d(' * methods implemented), the interface initialization')
0197             self.d(' * function used in G_IMPLEMENT_INTERFACE() would')
0198             self.d(' * typically look like this:')
0199             self.d(' *')
0200             self.d(' * <programlisting>')
0201             self.d(' * static void')
0202             self.d(' * implement_%s (gpointer klass,' % self.node_name_lc)
0203             self.d(' *     gpointer unused G_GNUC_UNUSED)')
0204             self.d(' * {')
0205             self.d(' * #define IMPLEMENT(x) %s%s_implement_&num;&num;x (\\'
0206                    % (self.prefix_, self.node_name_lc))
0207             self.d(' *   klass, my_object_&num;&num;x)')
0208 
0209             for method in methods:
0210                 class_member_name = method.getAttribute('tp:name-for-bindings')
0211                 class_member_name = class_member_name.lower()
0212                 self.d(' *   IMPLEMENT (%s);' % class_member_name)
0213 
0214             self.d(' * #undef IMPLEMENT')
0215             self.d(' * }')
0216             self.d(' * </programlisting>')
0217         else:
0218             self.d(' * This interface has no D-Bus methods, so an')
0219             self.d(' * implementation can typically pass %NULL to')
0220             self.d(' * G_IMPLEMENT_INTERFACE() as the interface')
0221             self.d(' * initialization function.')
0222 
0223         self.d(' */')
0224         self.d('')
0225 
0226         self.h('typedef struct _%s%sClass %s%sClass;'
0227                % (self.Prefix, node_name_mixed, self.Prefix, node_name_mixed))
0228         self.h('')
0229         self.h('GType %s%s_get_type (void);'
0230                % (self.prefix_, node_name_lc))
0231 
0232         gtype = self.current_gtype = \
0233                 self.MAIN_PREFIX_ + 'TYPE' + self._SUB_PREFIX_ + node_name_uc
0234         classname = self.Prefix + node_name_mixed
0235 
0236         self.h('#define %s \\\n  (%s%s_get_type ())'
0237                % (gtype, self.prefix_, node_name_lc))
0238         self.h('#define %s%s(obj) \\\n'
0239                '  (G_TYPE_CHECK_INSTANCE_CAST((obj), %s, %s))'
0240                % (self.PREFIX_, node_name_uc, gtype, classname))
0241         self.h('#define %sIS%s%s(obj) \\\n'
0242                '  (G_TYPE_CHECK_INSTANCE_TYPE((obj), %s))'
0243                % (self.MAIN_PREFIX_, self._SUB_PREFIX_, node_name_uc, gtype))
0244         self.h('#define %s%s_GET_CLASS(obj) \\\n'
0245                '  (G_TYPE_INSTANCE_GET_INTERFACE((obj), %s, %sClass))'
0246                % (self.PREFIX_, node_name_uc, gtype, classname))
0247         self.h('')
0248         self.h('')
0249 
0250         base_init_code = []
0251 
0252         for method in methods:
0253             self.do_method(method)
0254 
0255         for signal in signals:
0256             base_init_code.extend(self.do_signal(signal))
0257 
0258         self.b('static inline void')
0259         self.b('%s%s_base_init_once (gpointer klass G_GNUC_UNUSED)'
0260                % (self.prefix_, node_name_lc))
0261         self.b('{')
0262 
0263         if properties:
0264             self.b('  static TpDBusPropertiesMixinPropInfo properties[%d] = {'
0265                    % (len(properties) + 1))
0266 
0267             for m in properties:
0268                 access = m.getAttribute('access')
0269                 assert access in ('read', 'write', 'readwrite')
0270 
0271                 if access == 'read':
0272                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_READ'
0273                 elif access == 'write':
0274                     flags = 'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE'
0275                 else:
0276                     flags = ('TP_DBUS_PROPERTIES_MIXIN_FLAG_READ | '
0277                              'TP_DBUS_PROPERTIES_MIXIN_FLAG_WRITE')
0278 
0279                 prop_emits_changed = get_emits_changed(m)
0280 
0281                 if prop_emits_changed is None:
0282                     prop_emits_changed = iface_emits_changed
0283 
0284                 if prop_emits_changed == 'true':
0285                     flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_CHANGED'
0286                 elif prop_emits_changed == 'invalidates':
0287                     flags += ' | TP_DBUS_PROPERTIES_MIXIN_FLAG_EMITS_INVALIDATED'
0288 
0289                 self.b('      { 0, %s, "%s", 0, NULL, NULL }, /* %s */'
0290                        % (flags, m.getAttribute('type'), m.getAttribute('name')))
0291 
0292             self.b('      { 0, 0, NULL, 0, NULL, NULL }')
0293             self.b('  };')
0294             self.b('  static TpDBusPropertiesMixinIfaceInfo interface =')
0295             self.b('      { 0, properties, NULL, NULL };')
0296             self.b('')
0297 
0298 
0299         self.b('  dbus_g_object_type_install_info (%s%s_get_type (),'
0300                % (self.prefix_, node_name_lc))
0301         self.b('      &_%s%s_object_info);'
0302                % (self.prefix_, node_name_lc))
0303         self.b('')
0304 
0305         if properties:
0306             self.b('  interface.dbus_interface = g_quark_from_static_string '
0307                    '("%s");' % self.iface_name)
0308 
0309             for i, m in enumerate(properties):
0310                 self.b('  properties[%d].name = g_quark_from_static_string ("%s");'
0311                        % (i, m.getAttribute('name')))
0312                 self.b('  properties[%d].type = %s;'
0313                            % (i, type_to_gtype(m.getAttribute('type'))[1]))
0314 
0315             self.b('  tp_svc_interface_set_dbus_properties_info (%s, &interface);'
0316                    % self.current_gtype)
0317 
0318             self.b('')
0319 
0320         for s in base_init_code:
0321             self.b(s)
0322         self.b('}')
0323 
0324         self.b('static void')
0325         self.b('%s%s_base_init (gpointer klass)'
0326                % (self.prefix_, node_name_lc))
0327         self.b('{')
0328         self.b('  static gboolean initialized = FALSE;')
0329         self.b('')
0330         self.b('  if (!initialized)')
0331         self.b('    {')
0332         self.b('      initialized = TRUE;')
0333         self.b('      %s%s_base_init_once (klass);'
0334                % (self.prefix_, node_name_lc))
0335         self.b('    }')
0336         # insert anything we need to do per implementation here
0337         self.b('}')
0338 
0339         self.h('')
0340 
0341         self.b('static const DBusGMethodInfo _%s%s_methods[] = {'
0342                % (self.prefix_, node_name_lc))
0343 
0344         method_blob, offsets = self.get_method_glue(methods)
0345 
0346         for method, offset in zip(methods, offsets):
0347             self.do_method_glue(method, offset)
0348 
0349         if len(methods) == 0:
0350             # empty arrays are a gcc extension, so put in a dummy member
0351             self.b("  { NULL, NULL, 0 }")
0352 
0353         self.b('};')
0354         self.b('')
0355 
0356         self.b('static const DBusGObjectInfo _%s%s_object_info = {'
0357                % (self.prefix_, node_name_lc))
0358         self.b('  0,')  # version
0359         self.b('  _%s%s_methods,' % (self.prefix_, node_name_lc))
0360         self.b('  %d,' % len(methods))
0361         self.b('"' + method_blob.replace('\0', '\\0') + '",')
0362         self.b('"' + self.get_signal_glue(signals).replace('\0', '\\0') + '",')
0363         self.b('"' +
0364                self.get_property_glue(glue_properties).replace('\0', '\\0') +
0365                '",')
0366         self.b('};')
0367         self.b('')
0368 
0369         self.node_name_mixed = None
0370         self.node_name_lc = None
0371         self.node_name_uc = None
0372 
0373     def get_method_glue(self, methods):
0374         info = []
0375         offsets = []
0376 
0377         for method in methods:
0378             offsets.append(len(''.join(info)))
0379 
0380             info.append(self.iface_name + '\0')
0381             info.append(method.getAttribute('name') + '\0')
0382 
0383             info.append('A\0')    # async
0384 
0385             counter = 0
0386             for arg in method.getElementsByTagName('arg'):
0387                 out = arg.getAttribute('direction') == 'out'
0388 
0389                 name = arg.getAttribute('name')
0390                 if not name:
0391                     assert out
0392                     name = 'arg%u' % counter
0393                 counter += 1
0394 
0395                 info.append(name + '\0')
0396 
0397                 if out:
0398                     info.append('O\0')
0399                 else:
0400                     info.append('I\0')
0401 
0402                 if out:
0403                     info.append('F\0')    # not const
0404                     info.append('N\0')    # not error or return
0405                 info.append(arg.getAttribute('type') + '\0')
0406 
0407             info.append('\0')
0408 
0409         return ''.join(info) + '\0', offsets
0410 
0411     def do_method_glue(self, method, offset):
0412         lc_name = method.getAttribute('tp:name-for-bindings')
0413         if method.getAttribute('name') != lc_name.replace('_', ''):
0414             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
0415                     'not match' % (method.getAttribute('name'), lc_name))
0416         lc_name = lc_name.lower()
0417 
0418         marshaller = 'g_cclosure_marshal_generic'
0419         wrapper = self.prefix_ + self.node_name_lc + '_' + lc_name
0420 
0421         self.b("  { (GCallback) %s, %s, %d }," % (wrapper, marshaller, offset))
0422 
0423     def get_signal_glue(self, signals):
0424         info = []
0425 
0426         for signal in signals:
0427             info.append(self.iface_name)
0428             info.append(signal.getAttribute('name'))
0429 
0430         return '\0'.join(info) + '\0\0'
0431 
0432     # the implementation can be the same
0433     get_property_glue = get_signal_glue
0434 
0435     def get_method_impl_names(self, method):
0436         dbus_method_name = method.getAttribute('name')
0437 
0438         class_member_name = method.getAttribute('tp:name-for-bindings')
0439         if dbus_method_name != class_member_name.replace('_', ''):
0440             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
0441                     'not match' % (dbus_method_name, class_member_name))
0442         class_member_name = class_member_name.lower()
0443 
0444         stub_name = (self.prefix_ + self.node_name_lc + '_' +
0445                      class_member_name)
0446         return (stub_name + '_impl', class_member_name + '_cb')
0447 
0448     def do_method(self, method):
0449         assert self.node_name_mixed is not None
0450 
0451         in_class = []
0452 
0453         # Examples refer to Thing.DoStuff (su) -> ii
0454 
0455         # DoStuff
0456         dbus_method_name = method.getAttribute('name')
0457         # do_stuff
0458         class_member_name = method.getAttribute('tp:name-for-bindings')
0459         if dbus_method_name != class_member_name.replace('_', ''):
0460             raise AssertionError('Method %s tp:name-for-bindings (%s) does '
0461                     'not match' % (dbus_method_name, class_member_name))
0462         class_member_name = class_member_name.lower()
0463 
0464         # void tp_svc_thing_do_stuff (TpSvcThing *, const char *, guint,
0465         #   DBusGMethodInvocation *);
0466         stub_name = (self.prefix_ + self.node_name_lc + '_' +
0467                      class_member_name)
0468         # typedef void (*tp_svc_thing_do_stuff_impl) (TpSvcThing *,
0469         #   const char *, guint, DBusGMethodInvocation);
0470         impl_name = stub_name + '_impl'
0471         # void tp_svc_thing_return_from_do_stuff (DBusGMethodInvocation *,
0472         #   gint, gint);
0473         ret_name = (self.prefix_ + self.node_name_lc + '_return_from_' +
0474                     class_member_name)
0475 
0476         # Gather arguments
0477         in_args = []
0478         out_args = []
0479         for i in method.getElementsByTagName('arg'):
0480             name = i.getAttribute('name')
0481             direction = i.getAttribute('direction') or 'in'
0482             dtype = i.getAttribute('type')
0483 
0484             assert direction in ('in', 'out')
0485 
0486             if name:
0487                 name = direction + '_' + name
0488             elif direction == 'in':
0489                 name = direction + str(len(in_args))
0490             else:
0491                 name = direction + str(len(out_args))
0492 
0493             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
0494 
0495             if pointer:
0496                 ctype = 'const ' + ctype
0497 
0498             struct = (ctype, name)
0499 
0500             if direction == 'in':
0501                 in_args.append(struct)
0502             else:
0503                 out_args.append(struct)
0504 
0505         # Implementation type declaration (in header, docs separated)
0506         self.d('/**')
0507         self.d(' * %s:' % impl_name)
0508         self.d(' * @self: The object implementing this interface')
0509         for (ctype, name) in in_args:
0510             self.d(' * @%s: %s (FIXME, generate documentation)'
0511                    % (name, ctype))
0512         self.d(' * @context: Used to return values or throw an error')
0513         self.d(' *')
0514         self.d(' * The signature of an implementation of the D-Bus method')
0515         self.d(' * %s on interface %s.' % (dbus_method_name, self.iface_name))
0516         self.d(' */')
0517 
0518         self.h('typedef void (*%s) (%s%s *self,'
0519           % (impl_name, self.Prefix, self.node_name_mixed))
0520         for (ctype, name) in in_args:
0521             self.h('    %s%s,' % (ctype, name))
0522         self.h('    DBusGMethodInvocation *context);')
0523 
0524         # Class member (in class definition)
0525         in_class.append('    %s %s;' % (impl_name, class_member_name))
0526 
0527         # Stub definition (in body only - it's static)
0528         self.b('static void')
0529         self.b('%s (%s%s *self,'
0530            % (stub_name, self.Prefix, self.node_name_mixed))
0531         for (ctype, name) in in_args:
0532             self.b('    %s%s,' % (ctype, name))
0533         self.b('    DBusGMethodInvocation *context)')
0534         self.b('{')
0535         self.b('  %s impl = (%s%s_GET_CLASS (self)->%s_cb);'
0536           % (impl_name, self.PREFIX_, self.node_name_uc, class_member_name))
0537         self.b('')
0538         self.b('  if (impl != NULL)')
0539         tmp = ['self'] + [name for (ctype, name) in in_args] + ['context']
0540         self.b('    {')
0541         self.b('      (impl) (%s);' % ',\n        '.join(tmp))
0542         self.b('    }')
0543         self.b('  else')
0544         self.b('    {')
0545         if self.not_implemented_func:
0546             self.b('      %s (context);' % self.not_implemented_func)
0547         else:
0548             self.b('      GError e = { DBUS_GERROR, ')
0549             self.b('           DBUS_GERROR_UNKNOWN_METHOD,')
0550             self.b('           "Method not implemented" };')
0551             self.b('')
0552             self.b('      dbus_g_method_return_error (context, &e);')
0553         self.b('    }')
0554         self.b('}')
0555         self.b('')
0556 
0557         # Implementation registration (in both header and body)
0558         self.h('void %s%s_implement_%s (%s%sClass *klass, %s impl);'
0559                % (self.prefix_, self.node_name_lc, class_member_name,
0560                   self.Prefix, self.node_name_mixed, impl_name))
0561 
0562         self.d('/**')
0563         self.d(' * %s%s_implement_%s:'
0564                % (self.prefix_, self.node_name_lc, class_member_name))
0565         self.d(' * @klass: A class whose instances implement this interface')
0566         self.d(' * @impl: A callback used to implement the %s D-Bus method'
0567                % dbus_method_name)
0568         self.d(' *')
0569         self.d(' * Register an implementation for the %s method in the vtable'
0570                % dbus_method_name)
0571         self.d(' * of an implementation of this interface. To be called from')
0572         self.d(' * the interface init function.')
0573         self.d(' */')
0574 
0575         self.b('void')
0576         self.b('%s%s_implement_%s (%s%sClass *klass, %s impl)'
0577                % (self.prefix_, self.node_name_lc, class_member_name,
0578                   self.Prefix, self.node_name_mixed, impl_name))
0579         self.b('{')
0580         self.b('  klass->%s_cb = impl;' % class_member_name)
0581         self.b('}')
0582         self.b('')
0583 
0584         # Return convenience function (static inline, in header)
0585         self.d('/**')
0586         self.d(' * %s:' % ret_name)
0587         self.d(' * @context: The D-Bus method invocation context')
0588         for (ctype, name) in out_args:
0589             self.d(' * @%s: %s (FIXME, generate documentation)'
0590                    % (name, ctype))
0591         self.d(' *')
0592         self.d(' * Return successfully by calling dbus_g_method_return().')
0593         self.d(' * This inline function exists only to provide type-safety.')
0594         self.d(' */')
0595         self.d('')
0596 
0597         tmp = (['DBusGMethodInvocation *context'] +
0598                [ctype + name for (ctype, name) in out_args])
0599         self.h('static inline')
0600         self.h('/* this comment is to stop gtkdoc realising this is static */')
0601         self.h(('void %s (' % ret_name) + (',\n    '.join(tmp)) + ');')
0602         self.h('static inline void')
0603         self.h(('%s (' % ret_name) + (',\n    '.join(tmp)) + ')')
0604         self.h('{')
0605         tmp = ['context'] + [name for (ctype, name) in out_args]
0606         self.h('  dbus_g_method_return (' + ',\n      '.join(tmp) + ');')
0607         self.h('}')
0608         self.h('')
0609 
0610         return in_class
0611 
0612     def get_signal_const_entry(self, signal):
0613         assert self.node_name_uc is not None
0614         return ('SIGNAL_%s_%s'
0615                 % (self.node_name_uc, signal.getAttribute('name')))
0616 
0617     def do_signal(self, signal):
0618         assert self.node_name_mixed is not None
0619 
0620         in_base_init = []
0621 
0622         # for signal: Thing::StuffHappened (s, u)
0623         # we want to emit:
0624         # void tp_svc_thing_emit_stuff_happened (gpointer instance,
0625         #    const char *arg0, guint arg1);
0626 
0627         dbus_name = signal.getAttribute('name')
0628 
0629         ugly_name = signal.getAttribute('tp:name-for-bindings')
0630         if dbus_name != ugly_name.replace('_', ''):
0631             raise AssertionError('Signal %s tp:name-for-bindings (%s) does '
0632                     'not match' % (dbus_name, ugly_name))
0633 
0634         stub_name = (self.prefix_ + self.node_name_lc + '_emit_' +
0635                      ugly_name.lower())
0636 
0637         const_name = self.get_signal_const_entry(signal)
0638 
0639         # Gather arguments
0640         args = []
0641         for i in signal.getElementsByTagName('arg'):
0642             name = i.getAttribute('name')
0643             dtype = i.getAttribute('type')
0644             tp_type = i.getAttribute('tp:type')
0645 
0646             if name:
0647                 name = 'arg_' + name
0648             else:
0649                 name = 'arg' + str(len(args))
0650 
0651             ctype, gtype, marshaller, pointer = type_to_gtype(dtype)
0652 
0653             if pointer:
0654                 ctype = 'const ' + ctype
0655 
0656             struct = (ctype, name, gtype)
0657             args.append(struct)
0658 
0659         tmp = (['gpointer instance'] +
0660                [ctype + name for (ctype, name, gtype) in args])
0661 
0662         self.h(('void %s (' % stub_name) + (',\n    '.join(tmp)) + ');')
0663 
0664         # FIXME: emit docs
0665 
0666         self.d('/**')
0667         self.d(' * %s:' % stub_name)
0668         self.d(' * @instance: The object implementing this interface')
0669         for (ctype, name, gtype) in args:
0670             self.d(' * @%s: %s (FIXME, generate documentation)'
0671                    % (name, ctype))
0672         self.d(' *')
0673         self.d(' * Type-safe wrapper around g_signal_emit to emit the')
0674         self.d(' * %s signal on interface %s.'
0675                % (dbus_name, self.iface_name))
0676         self.d(' */')
0677 
0678         self.b('void')
0679         self.b(('%s (' % stub_name) + (',\n    '.join(tmp)) + ')')
0680         self.b('{')
0681         self.b('  g_assert (instance != NULL);')
0682         self.b('  g_assert (G_TYPE_CHECK_INSTANCE_TYPE (instance, %s));'
0683                % (self.current_gtype))
0684         tmp = (['instance', '%s_signals[%s]' % (self.node_name_lc, const_name),
0685                 '0'] + [name for (ctype, name, gtype) in args])
0686         self.b('  g_signal_emit (' + ',\n      '.join(tmp) + ');')
0687         self.b('}')
0688         self.b('')
0689 
0690         signal_name = dbus_gutils_wincaps_to_uscore(dbus_name).replace('_',
0691                 '-')
0692 
0693         self.d('/**')
0694         self.d(' * %s%s::%s:'
0695                 % (self.Prefix, self.node_name_mixed, signal_name))
0696         self.d(' * @self: an object')
0697         for (ctype, name, gtype) in args:
0698             self.d(' * @%s: %s (FIXME, generate documentation)'
0699                    % (name, ctype))
0700         self.d(' *')
0701         self.d(' * The %s D-Bus signal is emitted whenever '
0702                 'this GObject signal is.' % dbus_name)
0703         self.d(' */')
0704         self.d('')
0705 
0706         in_base_init.append('  %s_signals[%s] ='
0707                             % (self.node_name_lc, const_name))
0708         in_base_init.append('  g_signal_new ("%s",' % signal_name)
0709         in_base_init.append('      G_OBJECT_CLASS_TYPE (klass),')
0710         in_base_init.append('      G_SIGNAL_RUN_LAST|G_SIGNAL_DETAILED,')
0711         in_base_init.append('      0,')
0712         in_base_init.append('      NULL, NULL,')
0713         in_base_init.append('      g_cclosure_marshal_generic,')
0714         in_base_init.append('      G_TYPE_NONE,')
0715         tmp = ['%d' % len(args)] + [gtype for (ctype, name, gtype) in args]
0716         in_base_init.append('      %s);' % ',\n      '.join(tmp))
0717         in_base_init.append('')
0718 
0719         return in_base_init
0720 
0721     def have_properties(self, nodes):
0722         for node in nodes:
0723             interface =  node.getElementsByTagName('interface')[0]
0724             if interface.getElementsByTagName('property'):
0725                 return True
0726         return False
0727 
0728     def __call__(self):
0729         nodes = self.dom.getElementsByTagName('node')
0730         nodes.sort(key=key_by_name)
0731 
0732         self.h('#include <glib-object.h>')
0733         self.h('#include <dbus/dbus-glib.h>')
0734 
0735         for header in self.headers:
0736             self.h('#include %s' % header)
0737         self.h('')
0738 
0739         self.h('')
0740         self.h('G_BEGIN_DECLS')
0741         self.h('')
0742 
0743         self.b('#include "%s.h"' % self.basename)
0744         self.b('')
0745 
0746         for node in nodes:
0747             self.do_node(node)
0748 
0749         self.h('')
0750         self.h('G_END_DECLS')
0751 
0752         self.b('')
0753         for header in self.end_headers:
0754             self.b('#include %s' % header)
0755 
0756         self.h('')
0757         self.b('')
0758         file_set_contents(self.basename + '.h', u('\n').join(self.__header).encode('utf-8'))
0759         file_set_contents(self.basename + '.c', u('\n').join(self.__body).encode('utf-8'))
0760         file_set_contents(self.basename + '-gtk-doc.h', u('\n').join(self.__docs).encode('utf-8'))
0761 
0762 def cmdline_error():
0763     print("""\
0764 usage:
0765     gen-ginterface [OPTIONS] xmlfile Prefix_
0766 options:
0767     --include='<header.h>' (may be repeated)
0768     --include='"header.h"' (ditto)
0769     --include-end='"header.h"' (ditto)
0770         Include extra headers in the generated .c file
0771     --signal-marshal-prefix='prefix'
0772         Use the given prefix on generated signal marshallers (default is
0773         prefix.lower()).
0774     --filename='BASENAME'
0775         Set the basename for the output files (default is prefix.lower()
0776         + 'ginterfaces')
0777     --not-implemented-func='symbol'
0778         Set action when methods not implemented in the interface vtable are
0779         called. symbol must have signature
0780             void symbol (DBusGMethodInvocation *context)
0781         and return some sort of "not implemented" error via
0782             dbus_g_method_return_error (context, ...)
0783 """)
0784     sys.exit(1)
0785 
0786 
0787 if __name__ == '__main__':
0788     from getopt import gnu_getopt
0789 
0790     options, argv = gnu_getopt(sys.argv[1:], '',
0791                                ['filename=', 'signal-marshal-prefix=',
0792                                 'include=', 'include-end=',
0793                                 'allow-unstable',
0794                                 'not-implemented-func='])
0795 
0796     try:
0797         prefix = argv[1]
0798     except IndexError:
0799         cmdline_error()
0800 
0801     basename = prefix.lower() + 'ginterfaces'
0802     signal_marshal_prefix = prefix.lower().rstrip('_')
0803     headers = []
0804     end_headers = []
0805     not_implemented_func = ''
0806     allow_havoc = False
0807 
0808     for option, value in options:
0809         if option == '--filename':
0810             basename = value
0811         elif option == '--signal-marshal-prefix':
0812             signal_marshal_prefix = value
0813         elif option == '--include':
0814             if value[0] not in '<"':
0815                 value = '"%s"' % value
0816             headers.append(value)
0817         elif option == '--include-end':
0818             if value[0] not in '<"':
0819                 value = '"%s"' % value
0820             end_headers.append(value)
0821         elif option == '--not-implemented-func':
0822             not_implemented_func = value
0823         elif option == '--allow-unstable':
0824             allow_havoc = True
0825 
0826     try:
0827         dom = xml.dom.minidom.parse(argv[0])
0828     except IndexError:
0829         cmdline_error()
0830 
0831     Generator(dom, prefix, basename, signal_marshal_prefix, headers,
0832               end_headers, not_implemented_func, allow_havoc)()