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_##x (\\' 0206 % (self.prefix_, self.node_name_lc)) 0207 self.d(' * klass, my_object_##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)()