[Telepathy-commits] [telepathy-qt4/master] New architecture of the high-level proxies; Autointrospect for Connection implemented
Olli Salli
olli.salli at collabora.co.uk
Mon Sep 22 04:58:09 PDT 2008
---
TelepathyQt4/cli-connection.cpp | 193 +++++++++++++++++++++++++++++----------
TelepathyQt4/cli-connection.h | 196 +++++++++++++++++++++------------------
2 files changed, 252 insertions(+), 137 deletions(-)
diff --git a/TelepathyQt4/cli-connection.cpp b/TelepathyQt4/cli-connection.cpp
index 4a385bd..232f841 100644
--- a/TelepathyQt4/cli-connection.cpp
+++ b/TelepathyQt4/cli-connection.cpp
@@ -38,18 +38,25 @@ namespace Client
struct Connection::Private
{
+ // Public object
Connection& parent;
+
+ // Optional interface proxies
ConnectionInterfaceAliasingInterface aliasing;
ConnectionInterfacePresenceInterface presence;
ConnectionInterfaceSimplePresenceInterface simplePresence;
DBus::PropertiesInterface properties;
+
+ // Introspection
+ bool initialIntrospection;
+ Readiness readiness;
+ QStringList interfaces;
QQueue<void (Private::*)()> introspectQueue;
- bool ready;
- long status;
+ // Introspected properties
+ uint status;
uint statusReason;
- QStringList interfaces;
- ConnectionAliasFlags aliasFlags;
+ uint aliasFlags;
StatusSpecMap presenceStatuses;
SimpleStatusSpecMap simplePresenceStatuses;
@@ -60,9 +67,13 @@ struct Connection::Private
simplePresence(parent),
properties(parent)
{
- ready = false;
- status = -1;
+ initialIntrospection = false;
+ readiness = ReadinessJustCreated;
+ status = ConnectionStatusDisconnected;
statusReason = ConnectionStatusReasonNoneSpecified;
+ aliasFlags = 0;
+
+ debug() << "Connecting to StatusChanged()";
parent.connect(&parent,
SIGNAL(StatusChanged(uint, uint)),
@@ -79,6 +90,12 @@ struct Connection::Private
void introspectAliasing()
{
+ // The Aliasing interface is not usable before the connection is established
+ if (initialIntrospection) {
+ continueIntrospection();
+ return;
+ }
+
debug() << "Calling GetAliasFlags()";
QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(aliasing.GetAliasFlags(), &parent);
@@ -87,8 +104,27 @@ struct Connection::Private
SLOT(gotAliasFlags(QDBusPendingCallWatcher*)));
}
+ void introspectMain()
+ {
+ // Introspecting the main interface is currently just calling
+ // GetInterfaces(), but it might include other stuff in the future if we
+ // gain GetAll-able properties on the connection
+ debug() << "Calling GetInterfaces()";
+ QDBusPendingCallWatcher* watcher =
+ new QDBusPendingCallWatcher(parent.GetInterfaces(), &parent);
+ parent.connect(watcher,
+ SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
+ }
+
void introspectPresence()
{
+ // The Presence interface is not usable before the connection is established
+ if (initialIntrospection) {
+ continueIntrospection();
+ return;
+ }
+
debug() << "Calling GetStatuses() (legacy)";
QDBusPendingCallWatcher* watcher =
new QDBusPendingCallWatcher(presence.GetStatuses(), &parent);
@@ -113,13 +149,46 @@ struct Connection::Private
void continueIntrospection()
{
if (introspectQueue.isEmpty()) {
- debug() << "Connection ready";
- ready = true;
- emit parent.nowReady();
+ if (initialIntrospection) {
+ initialIntrospection = false;
+ if (readiness < ReadinessNotYetConnected)
+ changeReadiness(ReadinessNotYetConnected);
+ } else {
+ if (readiness != ReadinessDead)
+ changeReadiness(ReadinessFull);
+ }
} else {
(this->*introspectQueue.dequeue())();
}
}
+
+ void changeReadiness(Readiness newReadiness)
+ {
+ Q_ASSERT(newReadiness != readiness);
+
+ switch (readiness) {
+ case ReadinessJustCreated:
+ break;
+ case ReadinessNotYetConnected:
+ Q_ASSERT(newReadiness == ReadinessConnecting
+ || newReadiness == ReadinessDead);
+ break;
+ case ReadinessConnecting:
+ Q_ASSERT(newReadiness == ReadinessFull
+ || newReadiness == ReadinessDead);
+ break;
+ case ReadinessFull:
+ Q_ASSERT(newReadiness == ReadinessDead);
+ break;
+ case ReadinessDead:
+ Q_ASSERT(false);
+ break;
+ }
+
+ debug() << "Readiness changed from" << readiness << "to" << newReadiness;
+ readiness = newReadiness;
+ emit parent.readinessChanged(newReadiness);
+ }
};
Connection::Connection(const QString& serviceName,
@@ -144,12 +213,12 @@ Connection::~Connection()
delete mPriv;
}
-bool Connection::ready() const
+Connection::Readiness Connection::readiness() const
{
- return mPriv->ready;
+ return mPriv->readiness;
}
-long Connection::status() const
+uint Connection::status() const
{
return mPriv->status;
}
@@ -164,9 +233,7 @@ QStringList Connection::interfaces() const
return mPriv->interfaces;
}
-#if 0
-
-ConnectionAliasFlags Connection::aliasFlags() const
+uint Connection::aliasFlags() const
{
return mPriv->aliasFlags;
}
@@ -176,37 +243,42 @@ StatusSpecMap Connection::presenceStatuses() const
return mPriv->presenceStatuses;
}
-
SimpleStatusSpecMap Connection::simplePresenceStatuses() const
{
- if (!ready() && mPriv->simplePresenceStatuses.isEmpty()) {
- debug() << "Getting the simple presence statuses available before connecting";
- mPriv->simplePresenceStatuses = mPriv->simplePresence.statuses();
- }
-
return mPriv->simplePresenceStatuses;
}
-#endif
void Connection::onStatusChanged(uint status, uint reason)
{
- if (mPriv->status == -1) {
- // We've got a StatusChanged before the initial GetStatus reply, ignore it
- return;
- }
-
- debug().nospace() << "New status (" << status << ", " << reason << ')';
+ debug() << "Status changed from" << mPriv->status << "to" << status << "because of" << reason;
mPriv->status = status;
mPriv->statusReason = reason;
- if (status == ConnectionStatusConnected) {
- debug() << "Calling GetInterfaces()";
- QDBusPendingCallWatcher* watcher =
- new QDBusPendingCallWatcher(GetInterfaces(), this);
- connect(watcher,
- SIGNAL(finished(QDBusPendingCallWatcher*)),
- SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
+ switch (status) {
+ case ConnectionStatusConnected:
+ debug() << "Performing introspection for the Connected status";
+ mPriv->introspectQueue.enqueue(&Private::introspectMain);
+ mPriv->continueIntrospection();
+ break;
+
+ case ConnectionStatusConnecting:
+ if (mPriv->readiness < ReadinessConnecting)
+ mPriv->changeReadiness(ReadinessConnecting);
+ else
+ warning() << "Got unexpected status change to Connecting";
+ break;
+
+ case ConnectionStatusDisconnected:
+ if (mPriv->readiness != ReadinessDead)
+ mPriv->changeReadiness(ReadinessDead);
+ else
+ warning() << "Got unexpected status change to Disconnected";
+ break;
+
+ default:
+ warning() << "Unknown connection status" << status;
+ break;
}
}
@@ -214,14 +286,40 @@ void Connection::gotStatus(QDBusPendingCallWatcher* watcher)
{
QDBusPendingReply<uint> reply = *watcher;
- if (!reply.isError()) {
- debug() << "Got reply to initial GetStatus()";
- // Avoid early return in onStatusChanged()
- mPriv->status = reply.argumentAt<0>();
- onStatusChanged(reply.argumentAt<0>(), ConnectionStatusReasonNoneSpecified);
- } else {
+ if (reply.isError()) {
warning().nospace() << "GetStatus() failed with " << reply.error().name() << ": " << reply.error().message();
+ mPriv->changeReadiness(ReadinessDead);
+ return;
}
+
+ uint status = reply.value();
+
+ debug() << "Got connection status" << status;
+ mPriv->status = status;
+
+ // Don't do any introspection yet if the connection is in the Connecting
+ // state; the StatusChanged handler will take care of doing that, if the
+ // connection ever gets to the Connected state.
+ if (status == ConnectionStatusConnecting) {
+ debug() << "Not introspecting yet because the connection is currently Connecting";
+ mPriv->changeReadiness(ReadinessConnecting);
+ return;
+ }
+
+ if (status == ConnectionStatusDisconnected) {
+ debug() << "Performing introspection for the Disconnected status";
+ mPriv->initialIntrospection = true;
+ } else {
+ if (status != ConnectionStatusConnected) {
+ warning() << "Not performing introspection for unknown status" << status;
+ return;
+ } else {
+ debug() << "Performing introspection for the Connected status";
+ }
+ }
+
+ mPriv->introspectQueue.enqueue(&Private::introspectMain);
+ mPriv->continueIntrospection();
}
void Connection::gotInterfaces(QDBusPendingCallWatcher* watcher)
@@ -230,10 +328,9 @@ void Connection::gotInterfaces(QDBusPendingCallWatcher* watcher)
if (!reply.isError()) {
mPriv->interfaces = reply.value();
- debug() << "Got reply to initial GetInterfaces():";
- debug() << mPriv->interfaces;
+ debug() << "Got reply to GetInterfaces():" << mPriv->interfaces;
} else {
- warning().nospace() << "GetInterfaces() failed with " << reply.error().name() << ": " << reply.error().message() << " - assuming no interfaces";
+ warning().nospace() << "GetInterfaces() failed with " << reply.error().name() << ": " << reply.error().message() << " - assuming no new interfaces";
}
for (QStringList::const_iterator i = mPriv->interfaces.constBegin();
@@ -261,7 +358,7 @@ void Connection::gotAliasFlags(QDBusPendingCallWatcher* watcher)
if (!reply.isError()) {
mPriv->aliasFlags = static_cast<ConnectionAliasFlag>(reply.value());
- debug().nospace() << "Got initial alias flags 0x" << hex << mPriv->aliasFlags;
+ debug().nospace() << "Got alias flags 0x" << hex << mPriv->aliasFlags;
} else {
warning().nospace() << "GetAliasFlags() failed with " << reply.error().name() << ": " << reply.error().message();
}
@@ -275,7 +372,7 @@ void Connection::gotStatuses(QDBusPendingCallWatcher* watcher)
if (!reply.isError()) {
mPriv->presenceStatuses = reply.value();
- debug() << "Got initial legacy presence statuses";
+ debug() << "Got" << mPriv->presenceStatuses.size() << "legacy presence statuses";
} else {
warning().nospace() << "GetStatuses() failed with " << reply.error().name() << ": " << reply.error().message();
}
@@ -289,9 +386,9 @@ void Connection::gotSimpleStatuses(QDBusPendingCallWatcher* watcher)
if (!reply.isError()) {
mPriv->simplePresenceStatuses = qdbus_cast<SimpleStatusSpecMap>(reply.value().variant());
- debug() << "Got initial simple presence statuses";
+ debug() << "Got" << mPriv->simplePresenceStatuses.size() << "simple presence statuses";
} else {
- warning().nospace() << "Getting initial simple presence statuses failed with " << reply.error().name() << ": " << reply.error().message();
+ warning().nospace() << "Getting simple presence statuses failed with " << reply.error().name() << ": " << reply.error().message();
}
mPriv->continueIntrospection();
diff --git a/TelepathyQt4/cli-connection.h b/TelepathyQt4/cli-connection.h
index 959e18e..1908f2b 100644
--- a/TelepathyQt4/cli-connection.h
+++ b/TelepathyQt4/cli-connection.h
@@ -63,15 +63,77 @@ namespace Client
* directly:
* <ul>
* <li>%Connection status tracking</li>
- * <li>Calling #GetInterfaces automatically</li>
- * <li>Calling #GetAliasFlags automatically</li>
+ * <li>Calling GetInterfaces() automatically</li>
+ * <li>Calling GetAliasFlags() automatically</li>
+ * <li>Calling GetStatuses() automatically</li>
+ * <li>Getting the SimplePresence Statuses property automatically</li>
* </ul>
+ *
+ * The remote object state accessor functions on this object (status(),
+ * statusReason(), aliasFlags(), presenceStatuses(), simplePresenceStatuses())
+ * don't make any DBus calls; instead, they return values cached from a previous
+ * introspection run.
*/
class Connection : public ConnectionInterface
{
Q_OBJECT
+ Q_ENUMS(Readiness);
+
public:
/**
+ * Describes readiness of the Connection for usage. The readiness depends
+ * on the state of the remote object. In suitable states, an asynchronous
+ * introspection process is started, and the Connection becomes more ready
+ * when that process is completed.
+ */
+ enum Readiness {
+ /**
+ * The object has just been created and introspection is still in
+ * progress. No functionality is available.
+ *
+ * The readiness can change to any other state depending on the result
+ * of the initial state query to the remote object.
+ */
+ ReadinessJustCreated,
+
+ /**
+ * The remote object is in the Disconnected state and introspection
+ * relevant to that state has been completed.
+ *
+ * This state is useful for being able to set your presence status
+ * (through the SimplePresence interface) before connecting. Most other
+ * functionality is unavailable, though.
+ *
+ * The readiness can change to ReadinessConnecting and ReadinessDead.
+ */
+ ReadinessNotYetConnected,
+
+ /**
+ * The remote object is in the Connecting state. Most functionality is
+ * unavailable.
+ *
+ * The readiness can change to ReadinessFull and ReadinessDead.
+ */
+ ReadinessConnecting,
+
+ /**
+ * The connection is in the Connected state and all introspection
+ * has been completed. Most functionality is available.
+ *
+ * The readiness can change to ReadinessDead.
+ */
+ ReadinessFull,
+
+ /**
+ * The remote object has gone into a state where it can no longer be
+ * used. No functionality is available.
+ *
+ * No further readiness changes are possible.
+ */
+ ReadinessDead
+ };
+
+ /**
* Creates a Connection associated with the given object on the session bus.
*
* \param serviceName Name of the service the object is on.
@@ -101,127 +163,83 @@ public:
~Connection();
/**
- * Initially <code>false</code>, changes to <code>true</code> when the
- * connection has gone to the Connected status, introspection is finished
- * and it's ready for use.
+ * Returns the current readiness of the Connection.
*
- * By the time this property becomes true, the property #interfaces will
- * have been populated.
+ * \return The readiness, as defined in #Readiness.
*/
- Q_PROPERTY(bool ready READ ready)
+ Readiness readiness() const;
/**
- * Getter for the property #ready.
+ * Returns the connection's status. The returned status is undefined if the
+ * object has readiness #ReadinessJustCreated.
*
- * \return If the object is ready for use.
+ * \return The status, as defined in #ConnectionStatus.
*/
- bool ready() const;
+ uint status() const;
/**
- * The connection's status (one of the values of #ConnectionStatus), or -1
- * if we don't know it yet.
+ * Returns the reason for the connection's status. The returned reason is
+ * undefined if the object has readiness #ReadinessJustCreated.
*
- * Connect to the signal <code>statusChanged</code> on the underlying
- * #interface to watch for changes.
- */
- Q_PROPERTY(long status READ status)
-
- /**
- * Getter for the property #status.
- *
- * \return The status of the connection.
- */
- long status() const;
-
- /**
- * The reason why #status changed to its current value, or
- * #ConnectionStatusReasonNoneSpecified if the status is still unknown.
- */
- Q_PROPERTY(uint statusReason READ statusReason)
-
- /**
- * Getter for the property #statusReason.
- *
- * \return The reason for the current status.
+ * \return The reason, as defined in #ConnectionStatusReason.
*/
uint statusReason() const;
/**
- * List of interfaces implemented by the connection.
+ * Returns a list of optional interfaces supported by this object. The
+ * contents of the list is undefined unless the Connection has readiness
+ * #ReadinessNotYetConnected or #ReadinessFull. The returned value stays
+ * constant for the entire time the connection spends in each of these
+ * states; however interfaces might be added to the supported set at the
+ * time #ReadinessFull is reached.
*
- * The value is undefined until the property #ready is <code>true</code>.
- */
- Q_PROPERTY(QStringList interfaces READ interfaces)
-
- /**
- * Getter for the property #interfaces.
- *
- * \return D-Bus interface names of the implemented interfaces.
+ * \return Names of the supported interfaces.
*/
QStringList interfaces() const;
-#if 0
- /**
- * Bitwise OR of flags detailing the behaviour of aliases on the
- * connection.
- *
- * The value is undefined if the value of the property #ready is
- * <code>false</code> or the connection doesn't support the Aliasing
- * interface.
- */
- Q_PROPERTY(Telepathy::ConnectionAliasFlags aliasFlags READ aliasFlags)
-
- /**
- * Getter for the property #aliasFlags.
- *
- * \return Bitfield of the alias flags, as specified in
- * #ConnectionAliasFlag.
- */
- ConnectionAliasFlags aliasFlags() const;
-
/**
- * Dictionary of the valid presence statuses for the connection for use with
- * the legacy Telepathy Presence interface.
+ * Returns the bitwise OR of flags detailing the behavior of the Aliasing
+ * interface on the remote object. The value is undefined if the connection
+ * doesn't have readiness #ReadinessFull or if the remote object doesn't
+ * implement the Aliasing interface.
*
- * The value is undefined if the value of the property #ready is
- * <code>false</code> or the connection doesn't support the Presence
- * interface.
+ * \return Bitfield of flags, as specified in #ConnectionAliasFlag.
*/
- Q_PROPERTY(Telepathy::StatusSpecMap presenceStatuses READ presenceStatuses)
+ uint aliasFlags() const;
/**
- * Getter for the property #presenceStatuses.
+ * Returns a dictionary of presence statuses valid for use with the legacy
+ * Telepathy Presence interface on the remote object. The value is undefined
+ * unless the Connection has readiness #ReadinessFull or if the remote
+ * object doesn't implement the legacy Presence interface.
*
- * \return Dictionary mapping string identifiers to structs for each status.
+ * \return Dictionary from string identifiers to structs for each valid status.
*/
StatusSpecMap presenceStatuses() const;
/**
- * Dictionary of the valid presence statuses for the connection for use with
- * the new simplified Telepathy SimplePresence interface.
- *
- * Getting the value of this property before the connection is ready (as
- * signified by the property #ready) may cause a synchronous call to the
- * remote service to be made.
- *
- * The value is undefined if the connection doesn't support the
+ * Returns a dictionary of presence statuses valid for use with the new(er)
+ * Telepathy SimplePresence interface on the remote object. The value is
+ * undefined unless the Connection has readiness #ReadinessNotYetConnected
+ * or #ReadinessFull, or if the remote object doesn't implement the
* SimplePresence interface.
- */
- Q_PROPERTY(Telepathy::SimpleStatusSpecMap simplePresenceStatuses READ simplePresenceStatuses)
-
- /**
- * Getter for the property #simplePresenceStatuses.
*
- * \return Dictionary mapping string identifiers to structs for each status.
+ * The value will stay fixed for the whole time the connection stays with
+ * readiness #ReadinessNotYetConnected, but may change arbitrarily at the
+ * time #ReadinessFull is reached, staying fixed for the remaining lifetime
+ * of the Connection.
+ *
+ * \return Dictionary from string identifiers to structs for each valid status.
*/
SimpleStatusSpecMap simplePresenceStatuses() const;
-#endif
+
Q_SIGNALS:
/**
- * Emitted when the value of the property #ready changes to
- * <code>true</code>.
+ * Emitted whenever the readiness of the Connection changes.
+ *
+ * \param newReadiness The new readiness, as defined in #Readiness.
*/
- void nowReady();
+ void readinessChanged(Telepathy::Client::Connection::Readiness newReadiness);
private Q_SLOTS:
void onStatusChanged(uint, uint);
--
1.5.6.5
More information about the Telepathy-commits
mailing list