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