1#!/usr/bin/env python 2 3# glib-client-gen.py: "I Can't Believe It's Not dbus-binding-tool" 4# 5# Generate GLib client wrappers 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-2008 Collabora Ltd. <http://www.collabora.co.uk/> 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 25import sys 26import os.path 27import xml.dom.minidom 28from getopt import gnu_getopt 29 30from libtpcodegen import file_set_contents, key_by_name, u 31from libglibcodegen import Signature, type_to_gtype, \ 32 get_docstring, xml_escape, get_deprecated 33 34 35NS_TP = "http://telepathy.freedesktop.org/wiki/DbusSpec#extensions-v0" 36 37class Generator(object): 38 39 def __init__(self, dom, prefix, basename, opts): 40 self.dom = dom 41 self.__header = [] 42 self.__body = [] 43 self.__docs = [] 44 45 self.prefix_lc = prefix.lower() 46 self.prefix_uc = prefix.upper() 47 self.prefix_mc = prefix.replace('_', '') 48 self.basename = basename 49 self.group = opts.get('--group', None) 50 self.iface_quark_prefix = opts.get('--iface-quark-prefix', None) 51 self.tp_proxy_api = tuple(map(int, 52 opts.get('--tp-proxy-api', '0').split('.'))) 53 self.proxy_cls = opts.get('--subclass', 'TpProxy') + ' *' 54 self.proxy_arg = opts.get('--subclass', 'void') + ' *' 55 self.proxy_assert = opts.get('--subclass-assert', 'TP_IS_PROXY') 56 self.proxy_doc = ('A #%s or subclass' 57 % opts.get('--subclass', 'TpProxy')) 58 if self.proxy_arg == 'void *': 59 self.proxy_arg = 'gpointer ' 60 61 self.reentrant_symbols = set() 62 try: 63 filename = opts['--generate-reentrant'] 64 with open(filename, 'r') as f: 65 for line in f.readlines(): 66 self.reentrant_symbols.add(line.strip()) 67 except KeyError: 68 pass 69 70 self.deprecate_reentrant = opts.get('--deprecate-reentrant', None) 71 self.deprecation_attribute = opts.get('--deprecation-attribute', 72 'G_GNUC_DEPRECATED') 73 74 self.guard = opts.get('--guard', None) 75 76 def h(self, s): 77 self.__header.append(s) 78 79 def b(self, s): 80 self.__body.append(s) 81 82 def d(self, s): 83 self.__docs.append(s) 84 85 def get_iface_quark(self): 86 assert self.iface_dbus is not None 87 assert self.iface_uc is not None 88 if self.iface_quark_prefix is None: 89 return 'g_quark_from_static_string (\"%s\")' % self.iface_dbus 90 else: 91 return '%s_%s' % (self.iface_quark_prefix, self.iface_uc) 92 93 def do_signal(self, iface, signal): 94 iface_lc = iface.lower() 95 96 member = signal.getAttribute('name') 97 member_lc = signal.getAttribute('tp:name-for-bindings') 98 if member != member_lc.replace('_', ''): 99 raise AssertionError('Signal %s tp:name-for-bindings (%s) does ' 100 'not match' % (member, member_lc)) 101 member_lc = member_lc.lower() 102 member_uc = member_lc.upper() 103 104 arg_count = 0 105 args = [] 106 out_args = [] 107 108 for arg in signal.getElementsByTagName('arg'): 109 name = arg.getAttribute('name') 110 type = arg.getAttribute('type') 111 tp_type = arg.getAttribute('tp:type') 112 113 if not name: 114 name = 'arg%u' % arg_count 115 arg_count += 1 116 else: 117 name = 'arg_%s' % name 118 119 info = type_to_gtype(type) 120 args.append((name, info, tp_type, arg)) 121 122 callback_name = ('%s_%s_signal_callback_%s' 123 % (self.prefix_lc, iface_lc, member_lc)) 124 collect_name = ('_%s_%s_collect_args_of_%s' 125 % (self.prefix_lc, iface_lc, member_lc)) 126 invoke_name = ('_%s_%s_invoke_callback_for_%s' 127 % (self.prefix_lc, iface_lc, member_lc)) 128 129 # Example: 130 # 131 # typedef void (*tp_cli_connection_signal_callback_new_channel) 132 # (TpConnection *proxy, const gchar *arg_object_path, 133 # const gchar *arg_channel_type, guint arg_handle_type, 134 # guint arg_handle, gboolean arg_suppress_handler, 135 # gpointer user_data, GObject *weak_object); 136 137 self.d('/**') 138 self.d(' * %s:' % callback_name) 139 self.d(' * @proxy: The proxy on which %s_%s_connect_to_%s ()' 140 % (self.prefix_lc, iface_lc, member_lc)) 141 self.d(' * was called') 142 143 for arg in args: 144 name, info, tp_type, elt = arg 145 ctype, gtype, marshaller, pointer = info 146 147 docs = get_docstring(elt) or '(Undocumented)' 148 149 if ctype == 'guint ' and tp_type != '': 150 docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) 151 152 self.d(' * @%s: %s' % (name, xml_escape(docs))) 153 154 self.d(' * @user_data: User-supplied data') 155 self.d(' * @weak_object: User-supplied weakly referenced object') 156 self.d(' *') 157 self.d(' * Represents the signature of a callback for the signal %s.' 158 % member) 159 self.d(' */') 160 self.d('') 161 162 self.h('typedef void (*%s) (%sproxy,' 163 % (callback_name, self.proxy_cls)) 164 165 for arg in args: 166 name, info, tp_type, elt = arg 167 ctype, gtype, marshaller, pointer = info 168 169 const = pointer and 'const ' or '' 170 171 self.h(' %s%s%s,' % (const, ctype, name)) 172 173 self.h(' gpointer user_data, GObject *weak_object);') 174 175 if args: 176 self.b('static void') 177 self.b('%s (DBusGProxy *proxy G_GNUC_UNUSED,' % collect_name) 178 179 for arg in args: 180 name, info, tp_type, elt = arg 181 ctype, gtype, marshaller, pointer = info 182 183 const = pointer and 'const ' or '' 184 185 self.b(' %s%s%s,' % (const, ctype, name)) 186 187 self.b(' TpProxySignalConnection *sc)') 188 self.b('{') 189 self.b(' GValueArray *args = g_value_array_new (%d);' % len(args)) 190 self.b(' GValue blank = { 0 };') 191 self.b(' guint i;') 192 self.b('') 193 self.b(' g_value_init (&blank, G_TYPE_INT);') 194 self.b('') 195 self.b(' for (i = 0; i < %d; i++)' % len(args)) 196 self.b(' g_value_array_append (args, &blank);') 197 self.b('') 198 199 for i, arg in enumerate(args): 200 name, info, tp_type, elt = arg 201 ctype, gtype, marshaller, pointer = info 202 203 self.b(' g_value_unset (args->values + %d);' % i) 204 self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) 205 206 if gtype == 'G_TYPE_STRING': 207 self.b(' g_value_set_string (args->values + %d, %s);' 208 % (i, name)) 209 elif marshaller == 'BOXED': 210 self.b(' g_value_set_boxed (args->values + %d, %s);' 211 % (i, name)) 212 elif gtype == 'G_TYPE_UCHAR': 213 self.b(' g_value_set_uchar (args->values + %d, %s);' 214 % (i, name)) 215 elif gtype == 'G_TYPE_BOOLEAN': 216 self.b(' g_value_set_boolean (args->values + %d, %s);' 217 % (i, name)) 218 elif gtype == 'G_TYPE_INT': 219 self.b(' g_value_set_int (args->values + %d, %s);' 220 % (i, name)) 221 elif gtype == 'G_TYPE_UINT': 222 self.b(' g_value_set_uint (args->values + %d, %s);' 223 % (i, name)) 224 elif gtype == 'G_TYPE_INT64': 225 self.b(' g_value_set_int (args->values + %d, %s);' 226 % (i, name)) 227 elif gtype == 'G_TYPE_UINT64': 228 self.b(' g_value_set_uint64 (args->values + %d, %s);' 229 % (i, name)) 230 elif gtype == 'G_TYPE_DOUBLE': 231 self.b(' g_value_set_double (args->values + %d, %s);' 232 % (i, name)) 233 else: 234 assert False, ("Don't know how to put %s in a GValue" 235 % gtype) 236 self.b('') 237 238 self.b(' tp_proxy_signal_connection_v0_take_results (sc, args);') 239 self.b('}') 240 241 self.b('static void') 242 self.b('%s (TpProxy *tpproxy,' % invoke_name) 243 self.b(' GError *error G_GNUC_UNUSED,') 244 self.b(' GValueArray *args,') 245 self.b(' GCallback generic_callback,') 246 self.b(' gpointer user_data,') 247 self.b(' GObject *weak_object)') 248 self.b('{') 249 self.b(' %s callback =' % callback_name) 250 self.b(' (%s) generic_callback;' % callback_name) 251 self.b('') 252 self.b(' if (callback != NULL)') 253 self.b(' callback (g_object_ref (tpproxy),') 254 255 # FIXME: factor out into a function 256 for i, arg in enumerate(args): 257 name, info, tp_type, elt = arg 258 ctype, gtype, marshaller, pointer = info 259 260 if marshaller == 'BOXED': 261 self.b(' g_value_get_boxed (args->values + %d),' % i) 262 elif gtype == 'G_TYPE_STRING': 263 self.b(' g_value_get_string (args->values + %d),' % i) 264 elif gtype == 'G_TYPE_UCHAR': 265 self.b(' g_value_get_uchar (args->values + %d),' % i) 266 elif gtype == 'G_TYPE_BOOLEAN': 267 self.b(' g_value_get_boolean (args->values + %d),' % i) 268 elif gtype == 'G_TYPE_UINT': 269 self.b(' g_value_get_uint (args->values + %d),' % i) 270 elif gtype == 'G_TYPE_INT': 271 self.b(' g_value_get_int (args->values + %d),' % i) 272 elif gtype == 'G_TYPE_UINT64': 273 self.b(' g_value_get_uint64 (args->values + %d),' % i) 274 elif gtype == 'G_TYPE_INT64': 275 self.b(' g_value_get_int64 (args->values + %d),' % i) 276 elif gtype == 'G_TYPE_DOUBLE': 277 self.b(' g_value_get_double (args->values + %d),' % i) 278 else: 279 assert False, "Don't know how to get %s from a GValue" % gtype 280 281 self.b(' user_data,') 282 self.b(' weak_object);') 283 self.b('') 284 285 if len(args) > 0: 286 self.b(' g_value_array_free (args);') 287 else: 288 self.b(' if (args != NULL)') 289 self.b(' g_value_array_free (args);') 290 self.b('') 291 292 self.b(' g_object_unref (tpproxy);') 293 self.b('}') 294 295 # Example: 296 # 297 # TpProxySignalConnection * 298 # tp_cli_connection_connect_to_new_channel 299 # (TpConnection *proxy, 300 # tp_cli_connection_signal_callback_new_channel callback, 301 # gpointer user_data, 302 # GDestroyNotify destroy); 303 # 304 # destroy is invoked when the signal becomes disconnected. This 305 # is either because the signal has been disconnected explicitly 306 # by the user, because the TpProxy has become invalid and 307 # emitted the 'invalidated' signal, or because the weakly referenced 308 # object has gone away. 309 310 self.d('/**') 311 self.d(' * %s_%s_connect_to_%s:' 312 % (self.prefix_lc, iface_lc, member_lc)) 313 self.d(' * @proxy: %s' % self.proxy_doc) 314 self.d(' * @callback: Callback to be called when the signal is') 315 self.d(' * received') 316 self.d(' * @user_data: User-supplied data for the callback') 317 self.d(' * @destroy: Destructor for the user-supplied data, which') 318 self.d(' * will be called when this signal is disconnected, or') 319 self.d(' * before this function returns %NULL') 320 self.d(' * @weak_object: A #GObject which will be weakly referenced; ') 321 self.d(' * if it is destroyed, this callback will automatically be') 322 self.d(' * disconnected') 323 self.d(' * @error: If not %NULL, used to raise an error if %NULL is') 324 self.d(' * returned') 325 self.d(' *') 326 self.d(' * Connect a handler to the signal %s.' % member) 327 self.d(' *') 328 self.d(' * %s' % xml_escape(get_docstring(signal) or '(Undocumented)')) 329 self.d(' *') 330 self.d(' * Returns: a #TpProxySignalConnection containing all of the') 331 self.d(' * above, which can be used to disconnect the signal; or') 332 self.d(' * %NULL if the proxy does not have the desired interface') 333 self.d(' * or has become invalid.') 334 self.d(' */') 335 self.d('') 336 337 self.h('TpProxySignalConnection *%s_%s_connect_to_%s (%sproxy,' 338 % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) 339 self.h(' %s callback,' % callback_name) 340 self.h(' gpointer user_data,') 341 self.h(' GDestroyNotify destroy,') 342 self.h(' GObject *weak_object,') 343 self.h(' GError **error);') 344 self.h('') 345 346 self.b('TpProxySignalConnection *') 347 self.b('%s_%s_connect_to_%s (%sproxy,' 348 % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) 349 self.b(' %s callback,' % callback_name) 350 self.b(' gpointer user_data,') 351 self.b(' GDestroyNotify destroy,') 352 self.b(' GObject *weak_object,') 353 self.b(' GError **error)') 354 self.b('{') 355 self.b(' GType expected_types[%d] = {' % (len(args) + 1)) 356 357 for arg in args: 358 name, info, tp_type, elt = arg 359 ctype, gtype, marshaller, pointer = info 360 361 self.b(' %s,' % gtype) 362 363 self.b(' G_TYPE_INVALID };') 364 self.b('') 365 self.b(' g_return_val_if_fail (%s (proxy), NULL);' 366 % self.proxy_assert) 367 self.b(' g_return_val_if_fail (callback != NULL, NULL);') 368 self.b('') 369 self.b(' return tp_proxy_signal_connection_v0_new ((TpProxy *) proxy,') 370 self.b(' %s, \"%s\",' % (self.get_iface_quark(), member)) 371 self.b(' expected_types,') 372 373 if args: 374 self.b(' G_CALLBACK (%s),' % collect_name) 375 else: 376 self.b(' NULL, /* no args => no collector function */') 377 378 self.b(' %s,' % invoke_name) 379 self.b(' G_CALLBACK (callback), user_data, destroy,') 380 self.b(' weak_object, error);') 381 self.b('}') 382 self.b('') 383 384 def do_method(self, iface, method): 385 iface_lc = iface.lower() 386 387 member = method.getAttribute('name') 388 member_lc = method.getAttribute('tp:name-for-bindings') 389 if member != member_lc.replace('_', ''): 390 raise AssertionError('Method %s tp:name-for-bindings (%s) does ' 391 'not match' % (member, member_lc)) 392 member_lc = member_lc.lower() 393 member_uc = member_lc.upper() 394 395 in_count = 0 396 ret_count = 0 397 in_args = [] 398 out_args = [] 399 400 for arg in method.getElementsByTagName('arg'): 401 name = arg.getAttribute('name') 402 direction = arg.getAttribute('direction') 403 type = arg.getAttribute('type') 404 tp_type = arg.getAttribute('tp:type') 405 406 if direction != 'out': 407 if not name: 408 name = 'in%u' % in_count 409 in_count += 1 410 else: 411 name = 'in_%s' % name 412 else: 413 if not name: 414 name = 'out%u' % ret_count 415 ret_count += 1 416 else: 417 name = 'out_%s' % name 418 419 info = type_to_gtype(type) 420 if direction != 'out': 421 in_args.append((name, info, tp_type, arg)) 422 else: 423 out_args.append((name, info, tp_type, arg)) 424 425 # Async reply callback type 426 427 # Example: 428 # void (*tp_cli_properties_interface_callback_for_get_properties) 429 # (TpProxy *proxy, 430 # const GPtrArray *out0, 431 # const GError *error, 432 # gpointer user_data, 433 # GObject *weak_object); 434 435 self.d('/**') 436 self.d(' * %s_%s_callback_for_%s:' 437 % (self.prefix_lc, iface_lc, member_lc)) 438 self.d(' * @proxy: the proxy on which the call was made') 439 440 for arg in out_args: 441 name, info, tp_type, elt = arg 442 ctype, gtype, marshaller, pointer = info 443 444 docs = xml_escape(get_docstring(elt) or '(Undocumented)') 445 446 if ctype == 'guint ' and tp_type != '': 447 docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) 448 449 self.d(' * @%s: Used to return an \'out\' argument if @error is ' 450 '%%NULL: %s' 451 % (name, docs)) 452 453 self.d(' * @error: %NULL on success, or an error on failure') 454 self.d(' * @user_data: user-supplied data') 455 self.d(' * @weak_object: user-supplied object') 456 self.d(' *') 457 self.d(' * Signature of the callback called when a %s method call' 458 % member) 459 self.d(' * succeeds or fails.') 460 461 deprecated = method.getElementsByTagName('tp:deprecated') 462 if deprecated: 463 d = deprecated[0] 464 self.d(' *') 465 self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) 466 467 self.d(' */') 468 self.d('') 469 470 callback_name = '%s_%s_callback_for_%s' % (self.prefix_lc, iface_lc, 471 member_lc) 472 473 self.h('typedef void (*%s) (%sproxy,' 474 % (callback_name, self.proxy_cls)) 475 476 for arg in out_args: 477 name, info, tp_type, elt = arg 478 ctype, gtype, marshaller, pointer = info 479 const = pointer and 'const ' or '' 480 481 self.h(' %s%s%s,' % (const, ctype, name)) 482 483 self.h(' const GError *error, gpointer user_data,') 484 self.h(' GObject *weak_object);') 485 self.h('') 486 487 # Async callback implementation 488 489 invoke_callback = '_%s_%s_invoke_callback_%s' % (self.prefix_lc, 490 iface_lc, 491 member_lc) 492 493 collect_callback = '_%s_%s_collect_callback_%s' % (self.prefix_lc, 494 iface_lc, 495 member_lc) 496 497 # The callback called by dbus-glib; this ends the call and collects 498 # the results into a GValueArray. 499 self.b('static void') 500 self.b('%s (DBusGProxy *proxy,' % collect_callback) 501 self.b(' DBusGProxyCall *call,') 502 self.b(' gpointer user_data)') 503 self.b('{') 504 self.b(' GError *error = NULL;') 505 506 if len(out_args) > 0: 507 self.b(' GValueArray *args;') 508 self.b(' GValue blank = { 0 };') 509 self.b(' guint i;') 510 511 for arg in out_args: 512 name, info, tp_type, elt = arg 513 ctype, gtype, marshaller, pointer = info 514 515 # "We handle variants specially; the caller is expected to 516 # have already allocated storage for them". Thanks, 517 # dbus-glib... 518 if gtype == 'G_TYPE_VALUE': 519 self.b(' GValue *%s = g_new0 (GValue, 1);' % name) 520 else: 521 self.b(' %s%s;' % (ctype, name)) 522 523 self.b('') 524 self.b(' dbus_g_proxy_end_call (proxy, call, &error,') 525 526 for arg in out_args: 527 name, info, tp_type, elt = arg 528 ctype, gtype, marshaller, pointer = info 529 530 if gtype == 'G_TYPE_VALUE': 531 self.b(' %s, %s,' % (gtype, name)) 532 else: 533 self.b(' %s, &%s,' % (gtype, name)) 534 535 self.b(' G_TYPE_INVALID);') 536 537 if len(out_args) == 0: 538 self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,' 539 'NULL);') 540 else: 541 self.b('') 542 self.b(' if (error != NULL)') 543 self.b(' {') 544 self.b(' tp_proxy_pending_call_v0_take_results (user_data, error,') 545 self.b(' NULL);') 546 547 for arg in out_args: 548 name, info, tp_type, elt = arg 549 ctype, gtype, marshaller, pointer = info 550 if gtype == 'G_TYPE_VALUE': 551 self.b(' g_free (%s);' % name) 552 553 self.b(' return;') 554 self.b(' }') 555 self.b('') 556 self.b(' args = g_value_array_new (%d);' % len(out_args)) 557 self.b(' g_value_init (&blank, G_TYPE_INT);') 558 self.b('') 559 self.b(' for (i = 0; i < %d; i++)' % len(out_args)) 560 self.b(' g_value_array_append (args, &blank);') 561 562 for i, arg in enumerate(out_args): 563 name, info, tp_type, elt = arg 564 ctype, gtype, marshaller, pointer = info 565 566 self.b('') 567 self.b(' g_value_unset (args->values + %d);' % i) 568 self.b(' g_value_init (args->values + %d, %s);' % (i, gtype)) 569 570 if gtype == 'G_TYPE_STRING': 571 self.b(' g_value_take_string (args->values + %d, %s);' 572 % (i, name)) 573 elif marshaller == 'BOXED': 574 self.b(' g_value_take_boxed (args->values + %d, %s);' 575 % (i, name)) 576 elif gtype == 'G_TYPE_UCHAR': 577 self.b(' g_value_set_uchar (args->values + %d, %s);' 578 % (i, name)) 579 elif gtype == 'G_TYPE_BOOLEAN': 580 self.b(' g_value_set_boolean (args->values + %d, %s);' 581 % (i, name)) 582 elif gtype == 'G_TYPE_INT': 583 self.b(' g_value_set_int (args->values + %d, %s);' 584 % (i, name)) 585 elif gtype == 'G_TYPE_UINT': 586 self.b(' g_value_set_uint (args->values + %d, %s);' 587 % (i, name)) 588 elif gtype == 'G_TYPE_INT64': 589 self.b(' g_value_set_int (args->values + %d, %s);' 590 % (i, name)) 591 elif gtype == 'G_TYPE_UINT64': 592 self.b(' g_value_set_uint (args->values + %d, %s);' 593 % (i, name)) 594 elif gtype == 'G_TYPE_DOUBLE': 595 self.b(' g_value_set_double (args->values + %d, %s);' 596 % (i, name)) 597 else: 598 assert False, ("Don't know how to put %s in a GValue" 599 % gtype) 600 601 self.b(' tp_proxy_pending_call_v0_take_results (user_data, ' 602 'NULL, args);') 603 604 self.b('}') 605 606 self.b('static void') 607 self.b('%s (TpProxy *self,' % invoke_callback) 608 self.b(' GError *error,') 609 self.b(' GValueArray *args,') 610 self.b(' GCallback generic_callback,') 611 self.b(' gpointer user_data,') 612 self.b(' GObject *weak_object)') 613 self.b('{') 614 self.b(' %s callback = (%s) generic_callback;' 615 % (callback_name, callback_name)) 616 self.b('') 617 self.b(' if (error != NULL)') 618 self.b(' {') 619 self.b(' callback ((%s) self,' % self.proxy_cls) 620 621 for arg in out_args: 622 name, info, tp_type, elt = arg 623 ctype, gtype, marshaller, pointer = info 624 625 if marshaller == 'BOXED' or pointer: 626 self.b(' NULL,') 627 elif gtype == 'G_TYPE_DOUBLE': 628 self.b(' 0.0,') 629 else: 630 self.b(' 0,') 631 632 self.b(' error, user_data, weak_object);') 633 self.b(' g_error_free (error);') 634 self.b(' return;') 635 self.b(' }') 636 637 self.b(' callback ((%s) self,' % self.proxy_cls) 638 639 # FIXME: factor out into a function 640 for i, arg in enumerate(out_args): 641 name, info, tp_type, elt = arg 642 ctype, gtype, marshaller, pointer = info 643 644 if marshaller == 'BOXED': 645 self.b(' g_value_get_boxed (args->values + %d),' % i) 646 elif gtype == 'G_TYPE_STRING': 647 self.b(' g_value_get_string (args->values + %d),' % i) 648 elif gtype == 'G_TYPE_UCHAR': 649 self.b(' g_value_get_uchar (args->values + %d),' % i) 650 elif gtype == 'G_TYPE_BOOLEAN': 651 self.b(' g_value_get_boolean (args->values + %d),' % i) 652 elif gtype == 'G_TYPE_UINT': 653 self.b(' g_value_get_uint (args->values + %d),' % i) 654 elif gtype == 'G_TYPE_INT': 655 self.b(' g_value_get_int (args->values + %d),' % i) 656 elif gtype == 'G_TYPE_UINT64': 657 self.b(' g_value_get_uint64 (args->values + %d),' % i) 658 elif gtype == 'G_TYPE_INT64': 659 self.b(' g_value_get_int64 (args->values + %d),' % i) 660 elif gtype == 'G_TYPE_DOUBLE': 661 self.b(' g_value_get_double (args->values + %d),' % i) 662 else: 663 assert False, "Don't know how to get %s from a GValue" % gtype 664 665 self.b(' error, user_data, weak_object);') 666 self.b('') 667 668 if len(out_args) > 0: 669 self.b(' g_value_array_free (args);') 670 else: 671 self.b(' if (args != NULL)') 672 self.b(' g_value_array_free (args);') 673 674 self.b('}') 675 self.b('') 676 677 # Async stub 678 679 # Example: 680 # TpProxyPendingCall * 681 # tp_cli_properties_interface_call_get_properties 682 # (gpointer proxy, 683 # gint timeout_ms, 684 # const GArray *in_properties, 685 # tp_cli_properties_interface_callback_for_get_properties callback, 686 # gpointer user_data, 687 # GDestroyNotify *destructor); 688 689 self.h('TpProxyPendingCall *%s_%s_call_%s (%sproxy,' 690 % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) 691 self.h(' gint timeout_ms,') 692 693 self.d('/**') 694 self.d(' * %s_%s_call_%s:' 695 % (self.prefix_lc, iface_lc, member_lc)) 696 self.d(' * @proxy: the #TpProxy') 697 self.d(' * @timeout_ms: the timeout in milliseconds, or -1 to use the') 698 self.d(' * default') 699 700 for arg in in_args: 701 name, info, tp_type, elt = arg 702 ctype, gtype, marshaller, pointer = info 703 704 docs = xml_escape(get_docstring(elt) or '(Undocumented)') 705 706 if ctype == 'guint ' and tp_type != '': 707 docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) 708 709 self.d(' * @%s: Used to pass an \'in\' argument: %s' 710 % (name, docs)) 711 712 self.d(' * @callback: called when the method call succeeds or fails;') 713 self.d(' * may be %NULL to make a "fire and forget" call with no ') 714 self.d(' * reply tracking') 715 self.d(' * @user_data: user-supplied data passed to the callback;') 716 self.d(' * must be %NULL if @callback is %NULL') 717 self.d(' * @destroy: called with the user_data as argument, after the') 718 self.d(' * call has succeeded, failed or been cancelled;') 719 self.d(' * must be %NULL if @callback is %NULL') 720 self.d(' * @weak_object: If not %NULL, a #GObject which will be ') 721 self.d(' * weakly referenced; if it is destroyed, this call ') 722 self.d(' * will automatically be cancelled. Must be %NULL if ') 723 self.d(' * @callback is %NULL') 724 self.d(' *') 725 self.d(' * Start a %s method call.' % member) 726 self.d(' *') 727 self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) 728 self.d(' *') 729 self.d(' * Returns: a #TpProxyPendingCall representing the call in') 730 self.d(' * progress. It is borrowed from the object, and will become') 731 self.d(' * invalid when the callback is called, the call is') 732 self.d(' * cancelled or the #TpProxy becomes invalid.') 733 734 deprecated = method.getElementsByTagName('tp:deprecated') 735 if deprecated: 736 d = deprecated[0] 737 self.d(' *') 738 self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) 739 740 self.d(' */') 741 self.d('') 742 743 self.b('TpProxyPendingCall *\n%s_%s_call_%s (%sproxy,' 744 % (self.prefix_lc, iface_lc, member_lc, self.proxy_arg)) 745 self.b(' gint timeout_ms,') 746 747 for arg in in_args: 748 name, info, tp_type, elt = arg 749 ctype, gtype, marshaller, pointer = info 750 751 const = pointer and 'const ' or '' 752 753 self.h(' %s%s%s,' % (const, ctype, name)) 754 self.b(' %s%s%s,' % (const, ctype, name)) 755 756 self.h(' %s callback,' % callback_name) 757 self.h(' gpointer user_data,') 758 self.h(' GDestroyNotify destroy,') 759 self.h(' GObject *weak_object);') 760 self.h('') 761 762 self.b(' %s callback,' % callback_name) 763 self.b(' gpointer user_data,') 764 self.b(' GDestroyNotify destroy,') 765 self.b(' GObject *weak_object)') 766 self.b('{') 767 self.b(' GError *error = NULL;') 768 self.b(' GQuark interface = %s;' % self.get_iface_quark()) 769 self.b(' DBusGProxy *iface;') 770 self.b('') 771 self.b(' g_return_val_if_fail (%s (proxy), NULL);' 772 % self.proxy_assert) 773 self.b(' g_return_val_if_fail (callback != NULL || ' 774 'user_data == NULL, NULL);') 775 self.b(' g_return_val_if_fail (callback != NULL || ' 776 'destroy == NULL, NULL);') 777 self.b(' g_return_val_if_fail (callback != NULL || ' 778 'weak_object == NULL, NULL);') 779 self.b('') 780 self.b(' iface = tp_proxy_borrow_interface_by_id (') 781 self.b(' (TpProxy *) proxy,') 782 self.b(' interface, &error);') 783 self.b('') 784 self.b(' if (iface == NULL)') 785 self.b(' {') 786 self.b(' if (callback != NULL)') 787 self.b(' callback (proxy,') 788 789 for arg in out_args: 790 name, info, tp_type, elt = arg 791 ctype, gtype, marshaller, pointer = info 792 793 if pointer: 794 self.b(' NULL,') 795 else: 796 self.b(' 0,') 797 798 self.b(' error, user_data, weak_object);') 799 self.b('') 800 self.b(' if (destroy != NULL)') 801 self.b(' destroy (user_data);') 802 self.b('') 803 self.b(' g_error_free (error);') 804 self.b(' return NULL;') 805 self.b(' }') 806 self.b('') 807 self.b(' if (callback == NULL)') 808 self.b(' {') 809 self.b(' dbus_g_proxy_call_no_reply (iface, "%s",' % member) 810 811 for arg in in_args: 812 name, info, tp_type, elt = arg 813 ctype, gtype, marshaller, pointer = info 814 815 const = pointer and 'const ' or '' 816 817 self.b(' %s, %s,' % (gtype, name)) 818 819 self.b(' G_TYPE_INVALID);') 820 self.b(' return NULL;') 821 self.b(' }') 822 self.b(' else') 823 self.b(' {') 824 self.b(' TpProxyPendingCall *data;') 825 self.b('') 826 self.b(' data = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') 827 self.b(' interface, "%s", iface,' % member) 828 self.b(' %s,' % invoke_callback) 829 self.b(' G_CALLBACK (callback), user_data, destroy,') 830 self.b(' weak_object, FALSE);') 831 self.b(' tp_proxy_pending_call_v0_take_pending_call (data,') 832 self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') 833 self.b(' "%s",' % member) 834 self.b(' %s,' % collect_callback) 835 self.b(' data,') 836 self.b(' tp_proxy_pending_call_v0_completed,') 837 self.b(' timeout_ms,') 838 839 for arg in in_args: 840 name, info, tp_type, elt = arg 841 ctype, gtype, marshaller, pointer = info 842 843 const = pointer and 'const ' or '' 844 845 self.b(' %s, %s,' % (gtype, name)) 846 847 self.b(' G_TYPE_INVALID));') 848 self.b('') 849 self.b(' return data;') 850 self.b(' }') 851 self.b('}') 852 self.b('') 853 854 self.do_method_reentrant(method, iface_lc, member, member_lc, 855 in_args, out_args, collect_callback) 856 857 # leave a gap for the end of the method 858 self.d('') 859 self.b('') 860 self.h('') 861 862 def do_method_reentrant(self, method, iface_lc, member, member_lc, in_args, 863 out_args, collect_callback): 864 # Reentrant blocking calls 865 # Example: 866 # gboolean tp_cli_properties_interface_run_get_properties 867 # (gpointer proxy, 868 # gint timeout_ms, 869 # const GArray *in_properties, 870 # GPtrArray **out0, 871 # GError **error, 872 # GMainLoop **loop); 873 874 run_method_name = '%s_%s_run_%s' % (self.prefix_lc, iface_lc, member_lc) 875 if run_method_name not in self.reentrant_symbols: 876 return 877 878 self.b('typedef struct {') 879 self.b(' GMainLoop *loop;') 880 self.b(' GError **error;') 881 882 for arg in out_args: 883 name, info, tp_type, elt = arg 884 ctype, gtype, marshaller, pointer = info 885 886 self.b(' %s*%s;' % (ctype, name)) 887 888 self.b(' unsigned success:1;') 889 self.b(' unsigned completed:1;') 890 self.b('} _%s_%s_run_state_%s;' 891 % (self.prefix_lc, iface_lc, member_lc)) 892 893 reentrant_invoke = '_%s_%s_finish_running_%s' % (self.prefix_lc, 894 iface_lc, 895 member_lc) 896 897 self.b('static void') 898 self.b('%s (TpProxy *self G_GNUC_UNUSED,' % reentrant_invoke) 899 self.b(' GError *error,') 900 self.b(' GValueArray *args,') 901 self.b(' GCallback unused G_GNUC_UNUSED,') 902 self.b(' gpointer user_data G_GNUC_UNUSED,') 903 self.b(' GObject *unused2 G_GNUC_UNUSED)') 904 self.b('{') 905 self.b(' _%s_%s_run_state_%s *state = user_data;' 906 % (self.prefix_lc, iface_lc, member_lc)) 907 self.b('') 908 self.b(' state->success = (error == NULL);') 909 self.b(' state->completed = TRUE;') 910 self.b(' g_main_loop_quit (state->loop);') 911 self.b('') 912 self.b(' if (error != NULL)') 913 self.b(' {') 914 self.b(' if (state->error != NULL)') 915 self.b(' *state->error = error;') 916 self.b(' else') 917 self.b(' g_error_free (error);') 918 self.b('') 919 self.b(' return;') 920 self.b(' }') 921 self.b('') 922 923 for i, arg in enumerate(out_args): 924 name, info, tp_type, elt = arg 925 ctype, gtype, marshaller, pointer = info 926 927 self.b(' if (state->%s != NULL)' % name) 928 if marshaller == 'BOXED': 929 self.b(' *state->%s = g_value_dup_boxed (' 930 'args->values + %d);' % (name, i)) 931 elif marshaller == 'STRING': 932 self.b(' *state->%s = g_value_dup_string ' 933 '(args->values + %d);' % (name, i)) 934 elif marshaller in ('UCHAR', 'BOOLEAN', 'INT', 'UINT', 935 'INT64', 'UINT64', 'DOUBLE'): 936 self.b(' *state->%s = g_value_get_%s (args->values + %d);' 937 % (name, marshaller.lower(), i)) 938 else: 939 assert False, "Don't know how to copy %s" % gtype 940 941 self.b('') 942 943 if len(out_args) > 0: 944 self.b(' g_value_array_free (args);') 945 else: 946 self.b(' if (args != NULL)') 947 self.b(' g_value_array_free (args);') 948 949 self.b('}') 950 self.b('') 951 952 if self.deprecate_reentrant: 953 self.h('#ifndef %s' % self.deprecate_reentrant) 954 955 self.h('gboolean %s (%sproxy,' 956 % (run_method_name, self.proxy_arg)) 957 self.h(' gint timeout_ms,') 958 959 self.d('/**') 960 self.d(' * %s:' % run_method_name) 961 self.d(' * @proxy: %s' % self.proxy_doc) 962 self.d(' * @timeout_ms: Timeout in milliseconds, or -1 for default') 963 964 for arg in in_args: 965 name, info, tp_type, elt = arg 966 ctype, gtype, marshaller, pointer = info 967 968 docs = xml_escape(get_docstring(elt) or '(Undocumented)') 969 970 if ctype == 'guint ' and tp_type != '': 971 docs += ' (#%s)' % ('Tp' + tp_type.replace('_', '')) 972 973 self.d(' * @%s: Used to pass an \'in\' argument: %s' 974 % (name, docs)) 975 976 for arg in out_args: 977 name, info, tp_type, elt = arg 978 ctype, gtype, marshaller, pointer = info 979 980 self.d(' * @%s: Used to return an \'out\' argument if %%TRUE is ' 981 'returned: %s' 982 % (name, xml_escape(get_docstring(elt) or '(Undocumented)'))) 983 984 self.d(' * @error: If not %NULL, used to return errors if %FALSE ') 985 self.d(' * is returned') 986 self.d(' * @loop: If not %NULL, set before re-entering ') 987 self.d(' * the main loop, to point to a #GMainLoop ') 988 self.d(' * which can be used to cancel this call with ') 989 self.d(' * g_main_loop_quit(), causing a return of ') 990 self.d(' * %FALSE with @error set to %TP_DBUS_ERROR_CANCELLED') 991 self.d(' *') 992 self.d(' * Call the method %s and run the main loop' % member) 993 self.d(' * until it returns. Before calling this method, you must') 994 self.d(' * add a reference to any borrowed objects you need to keep,') 995 self.d(' * and generally ensure that everything is in a consistent') 996 self.d(' * state.') 997 self.d(' *') 998 self.d(' * %s' % xml_escape(get_docstring(method) or '(Undocumented)')) 999 self.d(' *') 1000 self.d(' * Returns: TRUE on success, FALSE and sets @error on error') 1001 1002 deprecated = method.getElementsByTagName('tp:deprecated') 1003 if deprecated: 1004 d = deprecated[0] 1005 self.d(' *') 1006 self.d(' * Deprecated: %s' % xml_escape(get_deprecated(d))) 1007 1008 self.d(' */') 1009 self.d('') 1010 1011 self.b('gboolean\n%s (%sproxy,' 1012 % (run_method_name, self.proxy_arg)) 1013 self.b(' gint timeout_ms,') 1014 1015 for arg in in_args: 1016 name, info, tp_type, elt = arg 1017 ctype, gtype, marshaller, pointer = info 1018 1019 const = pointer and 'const ' or '' 1020 1021 self.h(' %s%s%s,' % (const, ctype, name)) 1022 self.b(' %s%s%s,' % (const, ctype, name)) 1023 1024 for arg in out_args: 1025 name, info, tp_type, elt = arg 1026 ctype, gtype, marshaller, pointer = info 1027 1028 self.h(' %s*%s,' % (ctype, name)) 1029 self.b(' %s*%s,' % (ctype, name)) 1030 1031 self.h(' GError **error,') 1032 1033 if self.deprecate_reentrant: 1034 self.h(' GMainLoop **loop) %s;' % self.deprecation_attribute) 1035 self.h('#endif /* not %s */' % self.deprecate_reentrant) 1036 else: 1037 self.h(' GMainLoop **loop);') 1038 1039 self.h('') 1040 1041 self.b(' GError **error,') 1042 self.b(' GMainLoop **loop)') 1043 self.b('{') 1044 self.b(' DBusGProxy *iface;') 1045 self.b(' GQuark interface = %s;' % self.get_iface_quark()) 1046 self.b(' TpProxyPendingCall *pc;') 1047 self.b(' _%s_%s_run_state_%s state = {' 1048 % (self.prefix_lc, iface_lc, member_lc)) 1049 self.b(' NULL /* loop */, error,') 1050 1051 for arg in out_args: 1052 name, info, tp_type, elt = arg 1053 1054 self.b(' %s,' % name) 1055 1056 self.b(' FALSE /* completed */, FALSE /* success */ };') 1057 self.b('') 1058 self.b(' g_return_val_if_fail (%s (proxy), FALSE);' 1059 % self.proxy_assert) 1060 self.b('') 1061 self.b(' iface = tp_proxy_borrow_interface_by_id') 1062 self.b(' ((TpProxy *) proxy, interface, error);') 1063 self.b('') 1064 self.b(' if (iface == NULL)') 1065 self.b(' return FALSE;') 1066 self.b('') 1067 self.b(' state.loop = g_main_loop_new (NULL, FALSE);') 1068 self.b('') 1069 self.b(' pc = tp_proxy_pending_call_v0_new ((TpProxy *) proxy,') 1070 self.b(' interface, "%s", iface,' % member) 1071 self.b(' %s,' % reentrant_invoke) 1072 self.b(' NULL, &state, NULL, NULL, TRUE);') 1073 self.b('') 1074 self.b(' if (loop != NULL)') 1075 self.b(' *loop = state.loop;') 1076 self.b('') 1077 self.b(' tp_proxy_pending_call_v0_take_pending_call (pc,') 1078 self.b(' dbus_g_proxy_begin_call_with_timeout (iface,') 1079 self.b(' "%s",' % member) 1080 self.b(' %s,' % collect_callback) 1081 self.b(' pc,') 1082 self.b(' tp_proxy_pending_call_v0_completed,') 1083 self.b(' timeout_ms,') 1084 1085 for arg in in_args: 1086 name, info, tp_type, elt = arg 1087 ctype, gtype, marshaller, pointer = info 1088 1089 const = pointer and 'const ' or '' 1090 1091 self.b(' %s, %s,' % (gtype, name)) 1092 1093 self.b(' G_TYPE_INVALID));') 1094 self.b('') 1095 self.b(' if (!state.completed)') 1096 self.b(' g_main_loop_run (state.loop);') 1097 self.b('') 1098 self.b(' if (!state.completed)') 1099 self.b(' tp_proxy_pending_call_cancel (pc);') 1100 self.b('') 1101 self.b(' if (loop != NULL)') 1102 self.b(' *loop = NULL;') 1103 self.b('') 1104 self.b(' g_main_loop_unref (state.loop);') 1105 self.b('') 1106 self.b(' return state.success;') 1107 self.b('}') 1108 self.b('') 1109 1110 def do_signal_add(self, signal): 1111 marshaller_items = [] 1112 gtypes = [] 1113 1114 for i in signal.getElementsByTagName('arg'): 1115 name = i.getAttribute('name') 1116 type = i.getAttribute('type') 1117 info = type_to_gtype(type) 1118 # type, GType, STRING, is a pointer 1119 gtypes.append(info[1]) 1120 1121 self.b(' dbus_g_proxy_add_signal (proxy, "%s",' 1122 % signal.getAttribute('name')) 1123 for gtype in gtypes: 1124 self.b(' %s,' % gtype) 1125 self.b(' G_TYPE_INVALID);') 1126 1127 def do_interface(self, node): 1128 ifaces = node.getElementsByTagName('interface') 1129 assert len(ifaces) == 1 1130 iface = ifaces[0] 1131 name = node.getAttribute('name').replace('/', '') 1132 1133 self.iface = name 1134 self.iface_lc = name.lower() 1135 self.iface_uc = name.upper() 1136 self.iface_mc = name.replace('_', '') 1137 self.iface_dbus = iface.getAttribute('name') 1138 1139 signals = node.getElementsByTagName('signal') 1140 methods = node.getElementsByTagName('method') 1141 1142 if signals: 1143 self.b('static inline void') 1144 self.b('%s_add_signals_for_%s (DBusGProxy *proxy)' 1145 % (self.prefix_lc, name.lower())) 1146 self.b('{') 1147 1148 if self.tp_proxy_api >= (0, 7, 6): 1149 self.b(' if (!tp_proxy_dbus_g_proxy_claim_for_signal_adding ' 1150 '(proxy))') 1151 self.b(' return;') 1152 1153 for signal in signals: 1154 self.do_signal_add(signal) 1155 1156 self.b('}') 1157 self.b('') 1158 self.b('') 1159 1160 for signal in signals: 1161 self.do_signal(name, signal) 1162 1163 for method in methods: 1164 self.do_method(name, method) 1165 1166 self.iface_dbus = None 1167 1168 def __call__(self): 1169 1170 if self.guard is not None: 1171 self.h('#ifndef %s' % self.guard) 1172 self.h('#define %s' % self.guard) 1173 self.h('') 1174 1175 self.h('G_BEGIN_DECLS') 1176 self.h('') 1177 1178 self.b('/* We don\'t want gtkdoc scanning this file, it\'ll get') 1179 self.b(' * confused by seeing function definitions, so mark it as: */') 1180 self.b('/*<private_header>*/') 1181 self.b('') 1182 1183 nodes = self.dom.getElementsByTagName('node') 1184 nodes.sort(key=key_by_name) 1185 1186 for node in nodes: 1187 self.do_interface(node) 1188 1189 if self.group is not None: 1190 1191 self.b('/*') 1192 self.b(' * %s_%s_add_signals:' % (self.prefix_lc, self.group)) 1193 self.b(' * @self: the #TpProxy') 1194 self.b(' * @quark: a quark whose string value is the interface') 1195 self.b(' * name whose signals should be added') 1196 self.b(' * @proxy: the D-Bus proxy to which to add the signals') 1197 self.b(' * @unused: not used for anything') 1198 self.b(' *') 1199 self.b(' * Tell dbus-glib that @proxy has the signatures of all') 1200 self.b(' * signals on the given interface, if it\'s one we') 1201 self.b(' * support.') 1202 self.b(' *') 1203 self.b(' * This function should be used as a signal handler for') 1204 self.b(' * #TpProxy::interface-added.') 1205 self.b(' */') 1206 self.b('static void') 1207 self.b('%s_%s_add_signals (TpProxy *self G_GNUC_UNUSED,' 1208 % (self.prefix_lc, self.group)) 1209 self.b(' guint quark,') 1210 self.b(' DBusGProxy *proxy,') 1211 self.b(' gpointer unused G_GNUC_UNUSED)') 1212 1213 self.b('{') 1214 1215 for node in nodes: 1216 iface = node.getElementsByTagName('interface')[0] 1217 self.iface_dbus = iface.getAttribute('name') 1218 signals = node.getElementsByTagName('signal') 1219 if not signals: 1220 continue 1221 name = node.getAttribute('name').replace('/', '').lower() 1222 self.iface_uc = name.upper() 1223 self.b(' if (quark == %s)' % self.get_iface_quark()) 1224 self.b(' %s_add_signals_for_%s (proxy);' 1225 % (self.prefix_lc, name)) 1226 1227 self.b('}') 1228 self.b('') 1229 1230 self.h('G_END_DECLS') 1231 self.h('') 1232 1233 if self.guard is not None: 1234 self.h('#endif /* defined (%s) */' % self.guard) 1235 self.h('') 1236 1237 file_set_contents(self.basename + '.h', u('\n').join(self.__header).encode('utf-8')) 1238 file_set_contents(self.basename + '-body.h', u('\n').join(self.__body).encode('utf-8')) 1239 file_set_contents(self.basename + '-gtk-doc.h', u('\n').join(self.__docs).encode('utf-8')) 1240 1241def types_to_gtypes(types): 1242 return [type_to_gtype(t)[1] for t in types] 1243 1244 1245if __name__ == '__main__': 1246 options, argv = gnu_getopt(sys.argv[1:], '', 1247 ['group=', 'subclass=', 'subclass-assert=', 1248 'iface-quark-prefix=', 'tp-proxy-api=', 1249 'generate-reentrant=', 'deprecate-reentrant=', 1250 'deprecation-attribute=', 'guard=']) 1251 1252 opts = {} 1253 1254 for option, value in options: 1255 opts[option] = value 1256 1257 dom = xml.dom.minidom.parse(argv[0]) 1258 1259 Generator(dom, argv[1], argv[2], opts)() 1260