File indexing completed on 2024-04-21 05:01:51
0001 #!/usr/bin/python 0002 # 0003 # Copyright (C) 2008 Collabora Limited <http://www.collabora.co.uk> 0004 # Copyright (C) 2008 Nokia Corporation 0005 # 0006 # This library is free software; you can redistribute it and/or 0007 # modify it under the terms of the GNU Lesser General Public 0008 # License as published by the Free Software Foundation; either 0009 # version 2.1 of the License, or (at your option) any later version. 0010 # 0011 # This library is distributed in the hope that it will be useful, 0012 # but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 # Lesser General Public License for more details. 0015 # 0016 # You should have received a copy of the GNU Lesser General Public 0017 # License along with this library; if not, write to the Free Software 0018 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 0019 0020 from sys import argv 0021 import xml.dom.minidom 0022 import codecs 0023 from getopt import gnu_getopt 0024 import functools 0025 0026 from libtpcodegen import NS_TP, get_descendant_text, get_by_path 0027 from libqtcodegen import binding_from_usage, extract_arg_or_member_info, format_docstring, gather_externals, gather_custom_lists, get_headerfile_cmd, get_qt_name, qt_identifier_escape, RefRegistry 0028 0029 class Generator(object): 0030 def __init__(self, opts): 0031 try: 0032 self.group = opts.get('--group', '') 0033 self.headerfile = opts['--headerfile'] 0034 self.implfile = opts['--implfile'] 0035 self.namespace = opts['--namespace'] 0036 self.typesnamespace = opts['--typesnamespace'] 0037 self.realinclude = opts['--realinclude'] 0038 self.prettyinclude = opts.get('--prettyinclude') 0039 self.extraincludes = opts.get('--extraincludes', None) 0040 self.mainiface = opts.get('--mainiface', None) 0041 self.must_define = opts.get('--must-define', None) 0042 self.dbus_proxy = opts.get('--dbus-proxy', 0043 'Tp::DBusProxy') 0044 self.visibility = opts.get('--visibility', '') 0045 ifacedom = xml.dom.minidom.parse(opts['--ifacexml']) 0046 specdom = xml.dom.minidom.parse(opts['--specxml']) 0047 except KeyError as k: 0048 assert False, 'Missing required parameter %s' % k.args[0] 0049 0050 self.hs = [] 0051 self.bs = [] 0052 self.ifacenodes = ifacedom.getElementsByTagName('node') 0053 self.spec, = get_by_path(specdom, "spec") 0054 self.custom_lists = gather_custom_lists(self.spec, self.typesnamespace) 0055 self.externals = gather_externals(self.spec) 0056 self.refs = RefRegistry(self.spec) 0057 0058 def __call__(self): 0059 # Output info header and includes 0060 self.h("""\ 0061 /* 0062 * This file contains D-Bus client proxy classes generated by qt-client-gen.py. 0063 * 0064 * This file can be distributed under the same terms as the specification from 0065 * which it was generated. 0066 */ 0067 """) 0068 0069 if self.must_define: 0070 self.h('\n') 0071 self.h('#ifndef %s\n' % self.must_define) 0072 self.h('#error %s\n' % self.must_define) 0073 self.h('#endif\n') 0074 0075 self.h('\n') 0076 0077 if self.extraincludes: 0078 for include in self.extraincludes.split(','): 0079 self.h('#include %s\n' % include) 0080 0081 self.h(""" 0082 #include <QtGlobal> 0083 0084 #include <QString> 0085 #include <QObject> 0086 #include <QVariant> 0087 0088 #include <QDBusPendingReply> 0089 0090 #include <TelepathyQt/AbstractInterface> 0091 #include <TelepathyQt/DBusProxy> 0092 #include <TelepathyQt/Global> 0093 0094 namespace Tp 0095 { 0096 class PendingVariant; 0097 class PendingOperation; 0098 } 0099 0100 """) 0101 0102 if self.must_define: 0103 self.b("""#define %s\n""" % (self.must_define)) 0104 0105 self.b("""#include "%s" 0106 0107 """ % self.realinclude) 0108 0109 # Begin namespace 0110 for ns in self.namespace.split('::'): 0111 self.hb("""\ 0112 namespace %s 0113 { 0114 """ % ns) 0115 0116 # Output interface proxies 0117 def ifacenodecmp(x, y): 0118 xname, yname = [self.namespace + '::' + node.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' for node in (x, y)] 0119 0120 if xname == self.mainiface: 0121 return -1 0122 elif yname == self.mainiface: 0123 return 1 0124 else: 0125 return (xname > yname) - (xname < yname) 0126 0127 self.ifacenodes.sort(key=functools.cmp_to_key(ifacenodecmp)) 0128 for ifacenode in self.ifacenodes: 0129 self.do_ifacenode(ifacenode) 0130 0131 # End namespace 0132 self.hb(''.join(['}\n' for ns in self.namespace.split('::')])) 0133 0134 # Add metatype declaration - otherwise QTBUG #2151 might be triggered 0135 for ifacenode in self.ifacenodes: 0136 classname = ifacenode.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' 0137 self.h("Q_DECLARE_METATYPE(" + self.namespace + "::" + classname + "*)\n") 0138 0139 # Write output to files 0140 (codecs.getwriter('utf-8')(open(self.headerfile, 'wb'))).write(''.join(self.hs)) 0141 (codecs.getwriter('utf-8')(open(self.implfile, 'wb'))).write(''.join(self.bs)) 0142 0143 def do_ifacenode(self, ifacenode): 0144 # Extract info 0145 name = ifacenode.getAttribute('name').replace('/', '').replace('_', '') + 'Interface' 0146 iface, = get_by_path(ifacenode, 'interface') 0147 dbusname = iface.getAttribute('name') 0148 0149 # Begin class, constructors 0150 self.h(""" 0151 /** 0152 * \\class %(name)s 0153 %(headercmd)s\ 0154 %(groupcmd)s\ 0155 * 0156 * Proxy class providing a 1:1 mapping of the D-Bus interface "%(dbusname)s". 0157 */ 0158 class %(visibility)s %(name)s : public Tp::AbstractInterface 0159 { 0160 Q_OBJECT 0161 0162 public: 0163 /** 0164 * Returns the name of the interface "%(dbusname)s", which this class 0165 * represents. 0166 * 0167 * \\return The D-Bus interface name. 0168 */ 0169 static inline QLatin1String staticInterfaceName() 0170 { 0171 return QLatin1String("%(dbusname)s"); 0172 } 0173 0174 /** 0175 * Creates a %(name)s associated with the given object on the session bus. 0176 * 0177 * \\param busName Name of the service the object is on. 0178 * \\param objectPath Path to the object on the service. 0179 * \\param parent Passed to the parent class constructor. 0180 */ 0181 %(name)s( 0182 const QString& busName, 0183 const QString& objectPath, 0184 QObject* parent = 0 0185 ); 0186 0187 /** 0188 * Creates a %(name)s associated with the given object on the given bus. 0189 * 0190 * \\param connection The bus via which the object can be reached. 0191 * \\param busName Name of the service the object is on. 0192 * \\param objectPath Path to the object on the service. 0193 * \\param parent Passed to the parent class constructor. 0194 */ 0195 %(name)s( 0196 const QDBusConnection& connection, 0197 const QString& busName, 0198 const QString& objectPath, 0199 QObject* parent = 0 0200 ); 0201 """ % {'name' : name, 0202 'headercmd' : get_headerfile_cmd(self.realinclude, self.prettyinclude), 0203 'groupcmd' : self.group and (' * \\ingroup %s\n' % self.group), 0204 'dbusname' : dbusname, 0205 'visibility': self.visibility, 0206 }) 0207 0208 self.b(""" 0209 %(name)s::%(name)s(const QString& busName, const QString& objectPath, QObject *parent) 0210 : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), QDBusConnection::sessionBus(), parent) 0211 { 0212 } 0213 0214 %(name)s::%(name)s(const QDBusConnection& connection, const QString& busName, const QString& objectPath, QObject *parent) 0215 : Tp::AbstractInterface(busName, objectPath, staticInterfaceName(), connection, parent) 0216 { 0217 } 0218 """ % {'name' : name}) 0219 0220 # Construct from DBusProxy subclass 0221 self.h(""" 0222 /** 0223 * Creates a %(name)s associated with the same object as the given proxy. 0224 * 0225 * \\param proxy The proxy to use. It will also be the QObject::parent() 0226 * for this object. 0227 */ 0228 %(name)s(%(dbus_proxy)s *proxy); 0229 """ % {'name' : name, 0230 'dbus_proxy' : self.dbus_proxy}) 0231 0232 self.b(""" 0233 %(name)s::%(name)s(%(dbus_proxy)s *proxy) 0234 : Tp::AbstractInterface(proxy, staticInterfaceName()) 0235 { 0236 } 0237 """ % {'name' : name, 0238 'dbus_proxy' : self.dbus_proxy}) 0239 0240 # Main interface 0241 mainiface = self.mainiface or 'Tp::AbstractInterface' 0242 0243 if mainiface != self.namespace + '::' + name: 0244 self.h(""" 0245 /** 0246 * Creates a %(name)s associated with the same object as the given proxy. 0247 * Additionally, the created proxy will have the same parent as the given 0248 * proxy. 0249 * 0250 * \\param mainInterface The proxy to use. 0251 */ 0252 explicit %(name)s(const %(mainiface)s& mainInterface); 0253 0254 /** 0255 * Creates a %(name)s associated with the same object as the given proxy. 0256 * However, a different parent object can be specified. 0257 * 0258 * \\param mainInterface The proxy to use. 0259 * \\param parent Passed to the parent class constructor. 0260 */ 0261 %(name)s(const %(mainiface)s& mainInterface, QObject* parent); 0262 """ % {'name' : name, 0263 'mainiface' : mainiface}) 0264 0265 self.b(""" 0266 %(name)s::%(name)s(const %(mainiface)s& mainInterface) 0267 : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), mainInterface.parent()) 0268 { 0269 } 0270 0271 %(name)s::%(name)s(const %(mainiface)s& mainInterface, QObject *parent) 0272 : Tp::AbstractInterface(mainInterface.service(), mainInterface.path(), staticInterfaceName(), mainInterface.connection(), parent) 0273 { 0274 } 0275 """ % {'name' : name, 0276 'mainiface' : mainiface}) 0277 0278 # Properties 0279 has_props = False 0280 for prop in get_by_path(iface, 'property'): 0281 # Skip tp:properties 0282 if not prop.namespaceURI: 0283 self.do_prop(prop) 0284 has_props = True 0285 0286 self.h(""" 0287 /** 0288 * Request all of the DBus properties on the interface. 0289 * 0290 * \\return A pending variant map which will emit finished when the properties have 0291 * been retrieved. 0292 */ 0293 Tp::PendingVariantMap *requestAllProperties() const 0294 { 0295 return internalRequestAllProperties(); 0296 } 0297 """) 0298 0299 # Methods 0300 methods = get_by_path(iface, 'method') 0301 0302 if methods: 0303 self.h(""" 0304 public Q_SLOTS:\ 0305 """) 0306 0307 for method in methods: 0308 self.do_method(method) 0309 0310 # Signals 0311 signals = get_by_path(iface, 'signal') 0312 0313 if signals: 0314 self.h(""" 0315 Q_SIGNALS:\ 0316 """) 0317 0318 for signal in signals: 0319 self.do_signal(signal) 0320 0321 # invalidated handler (already a slot in the superclass) 0322 # we can't just use disconnect(this, NULL, NULL, NULL) because 0323 # (a) that would disconnect QObject::destroyed() and other non-D-Bus 0324 # signals, and (b) QtDBus doesn't support that usage anyway (it needs 0325 # specific signals in order to remove its signal match rules) 0326 self.h(""" 0327 protected: 0328 virtual void invalidate(Tp::DBusProxy *, const QString &, const QString &); 0329 """) 0330 0331 self.b(""" 0332 void %(name)s::invalidate(Tp::DBusProxy *proxy, 0333 const QString &error, const QString &message) 0334 { 0335 """ % {'name' : name}) 0336 0337 for signal in signals: 0338 self.do_signal_disconnect(signal) 0339 0340 self.b(""" 0341 Tp::AbstractInterface::invalidate(proxy, error, message); 0342 } 0343 """) 0344 0345 # Close class 0346 self.h("""\ 0347 }; 0348 """) 0349 0350 def do_prop(self, prop): 0351 name = prop.getAttribute('name') 0352 access = prop.getAttribute('access') 0353 gettername = name 0354 settername = None 0355 docstring = format_docstring(prop, self.refs, ' * ').replace('*/', '*/') 0356 0357 sig = prop.getAttribute('type') 0358 tptype = prop.getAttributeNS(NS_TP, 'type') 0359 binding = binding_from_usage(sig, tptype, self.custom_lists, (sig, tptype) in self.externals, self.typesnamespace) 0360 0361 if 'write' in access: 0362 settername = 'set' + name 0363 0364 if 'read' in access: 0365 self.h(""" 0366 /** 0367 * Asynchronous getter for the remote object property \\c %(name)s of type \\c %(val)s. 0368 * 0369 %(docstring)s\ 0370 * 0371 * \\return A pending variant which will emit finished when the property has been 0372 * retrieved. 0373 */ 0374 inline Tp::PendingVariant *%(gettername)s() const 0375 { 0376 return internalRequestProperty(QLatin1String("%(name)s")); 0377 } 0378 """ % {'name' : name, 0379 'docstring' : docstring, 0380 'val' : binding.val, 0381 'gettername' : 'requestProperty' + name}) 0382 0383 if 'write' in access: 0384 self.h(""" 0385 /** 0386 * Asynchronous setter for the remote object property \\c %(name)s of type \\c %(type)s. 0387 * 0388 %(docstring)s\ 0389 * 0390 * \\return A pending operation which will emit finished when the property has been 0391 * set. 0392 */ 0393 inline Tp::PendingOperation *%(settername)s(%(type)s newValue) 0394 { 0395 return internalSetProperty(QLatin1String("%(name)s"), QVariant::fromValue(newValue)); 0396 } 0397 """ % {'name' : name, 0398 'docstring' : docstring, 0399 'type' : binding.val, 0400 'name' : name, 0401 'settername' : 'setProperty' + name}) 0402 0403 def do_method(self, method): 0404 name = method.getAttribute('name') 0405 args = get_by_path(method, 'arg') 0406 argnames, argdocstrings, argbindings = extract_arg_or_member_info(args, self.custom_lists, 0407 self.externals, self.typesnamespace, self.refs, ' * ') 0408 0409 inargs = [] 0410 outargs = [] 0411 0412 for i in range(len(args)): 0413 if args[i].getAttribute('direction') == 'out': 0414 outargs.append(i) 0415 else: 0416 inargs.append(i) 0417 assert argnames[i] != None, 'No argument name for input argument at index %d for method %s' % (i, name) 0418 0419 rettypes = ', '.join([argbindings[i].val for i in outargs]) 0420 params = ', '.join([argbindings[i].inarg + ' ' + argnames[i] for i in inargs]) 0421 if params: 0422 params += ', int timeout = -1' 0423 else: 0424 params = 'int timeout = -1' 0425 0426 self.h(""" 0427 /** 0428 * Begins a call to the D-Bus method \\c %s on the remote object. 0429 %s\ 0430 * 0431 * Note that \\a timeout is ignored as of now. It will be used once 0432 * http://bugreports.qt.nokia.com/browse/QTBUG-11775 is fixed. 0433 * 0434 """ % (name, format_docstring(method, self.refs, ' * '))) 0435 0436 for i in inargs: 0437 if argdocstrings[i]: 0438 self.h("""\ 0439 * 0440 * \\param %s 0441 %s\ 0442 """ % (argnames[i], argdocstrings[i])) 0443 0444 self.h("""\ 0445 * \\param timeout The timeout in milliseconds. 0446 """) 0447 0448 for i in outargs: 0449 if argdocstrings[i]: 0450 self.h("""\ 0451 * 0452 * \\return 0453 %s\ 0454 """ % argdocstrings[i]) 0455 0456 self.h("""\ 0457 */ 0458 inline QDBusPendingReply<%(rettypes)s> %(name)s(%(params)s) 0459 { 0460 if (!invalidationReason().isEmpty()) { 0461 return QDBusPendingReply<%(rettypes)s>(QDBusMessage::createError( 0462 invalidationReason(), 0463 invalidationMessage() 0464 )); 0465 } 0466 """ % {'rettypes' : rettypes, 0467 'name' : name, 0468 'params' : params}) 0469 0470 if inargs: 0471 self.h(""" 0472 QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(), 0473 this->staticInterfaceName(), QLatin1String("%s")); 0474 callMessage << %s; 0475 return this->connection().asyncCall(callMessage, timeout); 0476 } 0477 """ % (name, ' << '.join(['QVariant::fromValue(%s)' % argnames[i] for i in inargs]))) 0478 else: 0479 self.h(""" 0480 QDBusMessage callMessage = QDBusMessage::createMethodCall(this->service(), this->path(), 0481 this->staticInterfaceName(), QLatin1String("%s")); 0482 return this->connection().asyncCall(callMessage, timeout); 0483 } 0484 """ % name) 0485 0486 def do_signal(self, signal): 0487 name = signal.getAttribute('name') 0488 argnames, argdocstrings, argbindings = extract_arg_or_member_info(get_by_path(signal, 0489 'arg'), self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') 0490 0491 self.h(""" 0492 /** 0493 * Represents the signal \\c %s on the remote object. 0494 %s\ 0495 """ % (name, format_docstring(signal, self.refs, ' * '))) 0496 0497 for i in range(len(argnames)): 0498 assert argnames[i] != None, 'Name missing from argument at index %d for signal %s' % (i, name) 0499 if argdocstrings[i]: 0500 self.h("""\ 0501 * 0502 * \\param %s 0503 %s\ 0504 """ % (argnames[i], argdocstrings[i])) 0505 0506 self.h("""\ 0507 */ 0508 void %s(%s); 0509 """ % (name, ', '.join(['%s %s' % (binding.inarg, name) for binding, name in zip(argbindings, argnames)]))) 0510 0511 def do_signal_disconnect(self, signal): 0512 name = signal.getAttribute('name') 0513 _, _, argbindings = extract_arg_or_member_info(get_by_path(signal, 'arg'), 0514 self.custom_lists, self.externals, self.typesnamespace, self.refs, ' * ') 0515 0516 self.b("""\ 0517 disconnect(this, SIGNAL(%s(%s)), NULL, NULL); 0518 """ % (name, ', '.join([binding.inarg for binding in argbindings]))) 0519 0520 def h(self, str): 0521 self.hs.append(str) 0522 0523 def b(self, str): 0524 self.bs.append(str) 0525 0526 def hb(self, str): 0527 self.h(str) 0528 self.b(str) 0529 0530 0531 if __name__ == '__main__': 0532 options, argv = gnu_getopt(argv[1:], '', 0533 ['group=', 0534 'namespace=', 0535 'typesnamespace=', 0536 'headerfile=', 0537 'implfile=', 0538 'ifacexml=', 0539 'specxml=', 0540 'realinclude=', 0541 'prettyinclude=', 0542 'extraincludes=', 0543 'mainiface=', 0544 'must-define=', 0545 'dbus-proxy=', 0546 'visibility=']) 0547 0548 Generator(dict(options))()