2a6bcf042ff7c5bf4433107e5a2a29538a1e6895
[platform/upstream/dbus.git] / qt / qdbusabstractinterface.cpp
1 /* -*- C++ -*-
2  *
3  * Copyright (C) 2005 Thiago Macieira <thiago@kde.org>
4  * Copyright (C) 2006 Trolltech AS. All rights reserved.
5  *    Author: Thiago Macieira <thiago.macieira@trolltech.com>
6  *
7  * Licensed under the Academic Free License version 2.1
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "qdbusabstractinterface.h"
26
27 #include "qdbusabstractinterface_p.h"
28 #include "qdbusmetaobject_p.h"
29 #include "qdbusconnection_p.h"
30
31 QVariant QDBusAbstractInterfacePrivate::property(const QMetaProperty &mp) const
32 {
33     // try to read this property
34     QDBusMessage msg = QDBusMessage::methodCall(service, path, DBUS_INTERFACE_PROPERTIES,
35                                                 QLatin1String("Get"));
36     msg << interface << QString::fromUtf8(mp.name());
37     QDBusMessage reply = connp->sendWithReply(msg, QDBusConnection::NoUseEventLoop);
38
39     if (reply.type() == QDBusMessage::ReplyMessage && reply.count() == 1 &&
40         reply.signature() == QLatin1String("v")) {
41         QVariant value = QDBusTypeHelper<QVariant>::fromVariant(reply.at(0));
42
43         // make sure the type is right
44         if (qstrcmp(mp.typeName(), value.typeName()) == 0) {
45             if (mp.type() == QVariant::LastType)
46                 // QVariant is special in this context
47                 return QDBusTypeHelper<QVariant>::fromVariant(value);
48
49             return value;
50         }
51     }
52
53     // there was an error...
54     if (reply.type() == QDBusMessage::ErrorMessage)
55         lastError = reply;
56     else if (reply.signature() != QLatin1String("v")) {
57         QString errmsg = QLatin1String("Invalid signature `%1' in return from call to "
58                                        DBUS_INTERFACE_PROPERTIES);
59         lastError = QDBusError(QDBusError::InvalidSignature, errmsg.arg(reply.signature()));
60     } else {
61         QString errmsg = QLatin1String("Unexpected type `%1' when retrieving property "
62                                        "`%2 %3.%4'");
63         lastError = QDBusError(QDBusError::InvalidSignature,
64                                errmsg.arg(QLatin1String(reply.at(0).typeName()),
65                                           QLatin1String(mp.typeName()),
66                                           interface, QString::fromUtf8(mp.name())));
67     }
68
69     return QVariant();
70 }
71
72 void QDBusAbstractInterfacePrivate::setProperty(const QMetaProperty &mp, const QVariant &value)
73 {
74     // send the value
75     QDBusMessage msg = QDBusMessage::methodCall(service, path, DBUS_INTERFACE_PROPERTIES,
76                                                 QLatin1String("Set"));
77     msg.setSignature(QLatin1String("ssv"));
78     msg << interface << QString::fromUtf8(mp.name()) << value;
79     QDBusMessage reply = connp->sendWithReply(msg, QDBusConnection::NoUseEventLoop);
80
81     if (reply.type() != QDBusMessage::ReplyMessage)
82         lastError = reply;
83 }    
84
85 /*!
86     \class QDBusAbstractInterface
87     \brief Base class for all D-Bus interfaces in the QtDBus binding, allowing access to remote interfaces.
88
89     Generated-code classes also derive from QDBusAbstractInterface, all methods described here are also
90     valid for generated-code classes. In addition to those described here, generated-code classes
91     provide member functions for the remote methods, which allow for compile-time checking of the
92     correct parameters and return values, as well as property type-matching and signal
93     parameter-matching.
94
95     \sa {dbusidl2cpp.html}{The dbusidl2cpp compiler}, QDBusInterface
96 */
97
98 /*!
99     \enum QDBusAbstractInterface::CallMode
100
101     Specifies how a call should be placed. The valid options are:
102     \value NoWaitForReply       place the call but don't wait for the reply (the reply's contents
103                                 will be discarded)
104     \value NoUseEventLoop       don't use an event loop to wait for a reply, but instead block on
105                                 network operations while waiting. This option means the
106                                 user-interface may not be updated for the duration of the call.
107     \value UseEventLoop         use the Qt event loop to wait for a reply. This option means the
108                                 user-interface will update, but it also means other events may
109                                 happen, like signal delivery and other D-Bus method calls.
110
111     When using UseEventLoop, applications must be prepared for reentrancy in any function.
112 */
113
114 /*!
115     \internal
116 */
117 QDBusAbstractInterface::QDBusAbstractInterface(QDBusAbstractInterfacePrivate* d)
118 #if QT_VERSION < 0x040200
119     : d_ptr(d)
120 {
121     d_ptr->q_ptr = this;
122 }
123 #endif
124
125 /*!
126     Releases this object's resources.
127 */
128 QDBusAbstractInterface::~QDBusAbstractInterface()
129 {
130     delete d_ptr;
131 }
132
133 /*!
134     Returns true if this is a valid reference to a remote object. It returns false if
135     there was an error during the creation of this interface (for instance, if the remote
136     application does not exist).
137
138     Note: when dealing with remote objects, it is not always possible to determine if it
139     exists when creating a QDBusInterface or QDBusInterfacePtr object.
140 */
141 bool QDBusAbstractInterface::isValid() const
142 {
143     return d_func()->isValid;
144 }
145
146 /*!
147     Returns the connection this interface is assocated with.
148 */
149 QDBusConnection QDBusAbstractInterface::connection() const
150 {
151     return d_func()->conn;
152 }
153
154 /*!
155     Returns the name of the service this interface is associated with.
156 */
157 QString QDBusAbstractInterface::service() const
158 {
159     return d_func()->service;
160 }
161
162 /*!
163     Returns the object path that this interface is associated with.
164 */
165 QString QDBusAbstractInterface::path() const
166 {
167     return d_func()->path;
168 }
169
170 /*!
171     Returns the name of this interface.
172 */
173 QString QDBusAbstractInterface::interface() const
174 {
175     return d_func()->interface;
176 }
177
178 /*!
179     Returns the error the last operation produced, or an invalid error if the last operation did not
180     produce an error.
181 */
182 QDBusError QDBusAbstractInterface::lastError() const
183 {
184     return d_func()->lastError;
185 }
186
187 /*!
188     \threadsafe
189     Places a call to the remote method specified by \a method on this interface, using \a args as
190     arguments. This function returns the message that was received as a reply, which can be a normal
191     QDBusMessage::ReplyMessage (indicating success) or QDBusMessage::ErrorMessage (if the call
192     failed). The \a mode parameter specifies how this call should be placed.
193
194     If the call succeeds, lastError() will be cleared; otherwise, it will contain the error this
195     call produced.
196
197     Normally, you should place calls using call().
198
199     \warning If you use \c UseEventLoop, your code must be prepared to deal with any reentrancy:
200              other method calls and signals may be delivered before this function returns, as well
201              as other Qt queued signals and events.
202 */
203 QDBusMessage QDBusAbstractInterface::callWithArgs(const QString& method, const QList<QVariant>& args,
204                                           CallMode mode)
205 {
206     Q_D(QDBusAbstractInterface);
207
208     QString m = method, sig;
209     // split out the signature from the method
210     int pos = method.indexOf(QLatin1Char('.'));
211     if (pos != -1) {
212         m.truncate(pos);
213         sig = method.mid(pos + 1);
214     }
215
216     if (mode == AutoDetect) {
217         // determine if this a sync or async call
218         mode = NoUseEventLoop;
219         const QMetaObject *mo = metaObject();
220         QByteArray match = method.toLatin1() + '(';
221
222         for (int i = staticMetaObject.methodCount(); i < mo->methodCount(); ++i) {
223             QMetaMethod mm = mo->method(i);
224             if (QByteArray(mm.signature()).startsWith(match)) {
225                 // found a method with the same name as what we're looking for
226                 // hopefully, nobody is overloading asynchronous and synchronous methods with
227                 // the same name
228
229                 QList<QByteArray> tags = QByteArray(mm.tag()).split(' ');
230                 if (tags.contains("async") || tags.contains("Q_ASYNC"))
231                     mode = NoWaitForReply;
232
233                 break;
234             }
235         }
236     }
237
238     QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), m);
239     msg.setSignature(sig);
240     msg.QList<QVariant>::operator=(args);
241
242     QDBusMessage reply;
243     if (mode != NoWaitForReply)
244         reply = d->conn.sendWithReply(msg, mode == UseEventLoop ?
245                                       QDBusConnection::UseEventLoop : QDBusConnection::NoUseEventLoop);
246     else
247         d->conn.send(msg);
248
249     d->lastError = reply;       // will clear if reply isn't an error
250
251     // ensure that there is at least one element
252     if (reply.isEmpty())
253         reply << QVariant();
254
255     return reply;
256 }
257
258 /*!
259     \overload
260     Places a call to the remote method specified by \a method on this interface, using \a args as
261     arguments. This function will return immediately after queueing the call. The reply from the
262     remote function or any errors emitted by it will be delivered to the \a slot slot on object \a
263     receiver.
264
265     This function returns true if the queueing succeeded: it does not indicate that the call
266     succeeded. If it failed, the slot will be called with an error message. lastError() will not be
267     set under those circumstances.
268
269     \sa QDBusError, QDBusMessage
270 */
271 bool QDBusAbstractInterface::callWithArgs(const QString &method, QObject *receiver, const char *slot,
272                                           const QList<QVariant> &args)
273 {
274     Q_D(QDBusAbstractInterface);
275     
276     QString m = method, sig;
277     // split out the signature from the method
278     int pos = method.indexOf(QLatin1Char('.'));
279     if (pos != -1) {
280         m.truncate(pos);
281         sig = method.mid(pos + 1);
282     }
283
284     QDBusMessage msg = QDBusMessage::methodCall(service(), path(), interface(), m);
285     msg.setSignature(sig);
286     msg.QList<QVariant>::operator=(args);
287
288     d->lastError = 0;           // clear
289     return d->conn.sendWithReplyAsync(msg, receiver, slot);
290 }
291
292 /*!
293     \internal
294     Catch signal connections.
295 */
296 void QDBusAbstractInterface::connectNotify(const char *signal)
297 {
298     // someone connecting to one of our signals
299     Q_D(QDBusAbstractInterface);
300
301     d->connp->connectRelay(d->service, d->path, d->interface, this, signal);
302 }
303
304 /*!
305     \internal
306     Catch signal disconnections.
307 */
308 void QDBusAbstractInterface::disconnectNotify(const char *signal)
309 {
310     // someone disconnecting from one of our signals
311     Q_D(QDBusAbstractInterface);
312
313     d->connp->disconnectRelay(d->service, d->path, d->interface, this, signal);
314 }
315
316 /*!
317     \internal
318     Get the value of the property \a propname.
319 */
320 QVariant QDBusAbstractInterface::internalPropGet(const char *propname) const
321 {
322     // assume this property exists and is readable
323     // we're only called from generated code anyways
324
325     int idx = metaObject()->indexOfProperty(propname);
326     if (idx != -1)
327         return d_func()->property(metaObject()->property(idx));
328     qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname);
329     return QVariant();          // error
330 }
331
332 /*!
333     \internal
334     Set the value of the property \a propname to \a value.
335 */
336 void QDBusAbstractInterface::internalPropSet(const char *propname, const QVariant &value)
337 {
338     Q_D(QDBusAbstractInterface);
339
340     // assume this property exists and is writeable
341     // we're only called from generated code anyways
342
343     int idx = metaObject()->indexOfProperty(propname);
344     if (idx != -1)
345         d->setProperty(metaObject()->property(idx), value);
346     else
347         qWarning("QDBusAbstractInterface::internalPropGet called with unknown property '%s'", propname);
348 }
349
350 /*!
351     \overload
352     \fn QDBusMessage QDBusAbstractInterface::call(const QString &method)
353
354     Calls the method \a method on this interface and passes the parameters to this function to the
355     method.
356
357     The parameters to \c call are passed on to the remote function via D-Bus as input
358     arguments. Output arguments are returned in the QDBusMessage reply. If the reply is an error
359     reply, lastError() will also be set to the contents of the error message.
360
361     This function is implemented by actually 9 different function overloads called \c call, so you
362     can pass up to 8 parameters to your function call, which can be of any type accepted by QtDBus
363     (see the \l {allowedparameters.html}{allowed parameters} page for information on what types are
364     accepted).
365
366     It can be used the following way:
367
368     \code
369       QString value = retrieveValue();
370       QDBusMessage reply;
371
372       QDBusReply<int> api = interface->call(QLatin1String("GetAPIVersion"));
373       if (api >= 14)
374         reply = interface->call(QLatin1String("ProcessWorkUnicode"), value);
375       else
376         reply = interface->call(QLatin1String("ProcessWork"), QLatin1String("UTF-8"), value.toUtf8());
377     \endcode
378
379     This example illustrates function calling with 0, 1 and 2 parameters and illustrates different
380     parameter types passed in each (the first call to \c "ProcessWorkUnicode" will contain one
381     Unicode string, the second call to \c "ProcessWork" will contain one string and one byte array).
382         
383     \warning This function reenters the Qt event loop in order to wait for the reply, excluding user
384              input. During the wait, it may deliver signals and other method calls to your
385              application. Therefore, it must be prepared to handle a reentrancy whenever a call is
386              placed with call().
387 */
388
389 #include "qdbusabstractinterface.moc"