Overview
Qt Extended IPC API is exposed to the user through the QtopiaIpcAdaptor class. QtopiaIpcAdaptor can be used in two ways: direct access and an inheritance based approach.
To use QtopiaIpcAdaptor directly, we first construct a new object by providing a channel as so:
QtopiaIpcAdaptor *adaptor = new QtopiaIpcAdaptor("QPE/CannonExample");
The newly created adaptor object can now be used to send messages to remote applications, or to receive messages. To send messages use the send method calls:
QtopiaIpcSendEnvelope envelope = adaptor->send(MESSAGE(aimCannon(int,int)));
envelope << direction << elevation;
adaptor->send(MESSAGE(shootCannon(int)), cannonPower);
Another application is responsible for actually aiming and shooting the cannon. Let us suppose that the cannon application can reply with two types of messages, a hit or a miss. E.g. the remote application will send a missed() or hit() message. We can listen for such messages in the following manner:
class CannonListener : public QObject
{
Q_OBJECT
public slots:
void missed();
void hit();
};
...
CannonListener *listener = new CannonListener;
QtopiaIpcAdaptor *adaptor = new QtopiaIpcAdaptor("QPE/CannonExample");
QtopiaIpcAdaptor::connect(adaptor, MESSAGE(missed()), listener, SLOT(missed()));
QtopiaIpcAdaptor::connect(adaptor, MESSAGE(hit()), listener, SLOT(hit()));
We can also use QtopiaIpcAdaptor::connect to enable an object's signals to be sent over a channel automatically. For instance, we can rewrite the above example like this:
class CannonController : public QObject
{
Q_OBJECT
public slots:
void missed();
void hit();
signals:
void aimCannon(int dir, int elev);
void shootCannon(int power);
};
...
CannonController *control = new CannonController;
QtopiaIpcAdaptor *adaptor = new QtopiaIpcAdaptor("QPE/CannonExample");
QtopiaIpcAdaptor::connect(adaptor, MESSAGE(missed()), control, SLOT(missed()));
QtopiaIpcAdaptor::connect(adaptor, MESSAGE(hit()), control, SLOT(hit()));
QtopiaIpcAdaptor::connect(control, SIGNAL(aimCannon(int,int)), adaptor, MESSAGE(aimCannon(int,int)));
QtopiaIpcAdaptor::connect(control, SIGNAL(shootCannon(int)), adaptor, MESSAGE(shootCannon(int)));
We should now recognize that for non-trivial interactions the last example is a quite common use case. It is thus desirable to avoid using QtopiaIpcAdaptor::connect for each connection, which can be quite error prone. QtopiaIpcAdaptor provides an alternate interaction technique based on inheritance. Instead of creating another class and using QtopiaIpcAdaptor::connect, we inherit directly from QtopiaIpcAdaptor and use the publish or publishAll methods to automatically publish all signals and slots on the channel. E.g.:
class CannonController : public QtopiaIpcAdaptor
{
Q_OBJECT
public:
CannonController(QObject *parent = 0);
void shoot();
public slots:
void missed();
void hit();
signals:
void aimCannon(int dir, int elev);
void shootCannon(int power);
};
CannonController::CannonController(QObject *parent)
: QtopiaIpcAdaptor("QPE/CannonExample", parent)
{
publishAll(QtopiaIpcAdaptor::SignalsAndSlots);
}
void CannonController::missed()
{
qDebug() << "Cannon missed!";
}
void CannonController::hit()
{
qDebug() << "Cannon HIT!";
}
void CannonController::shoot()
{
emit aimCannon(0, 45);
emit shootCannon(100);
}
...
CannonController *control = new CannonController;
control->shoot();
Serialization System
Enumerations
To make the example complete, we show how to extend our example with custom data types. First let us suppose that we'd like to use a custom ammunition type. We define a new enumeration named appropriately AmmunitionType. In order to exchange these enumerations, we must first register it with the meta-type system. This is accomplished by using the Q_DECLARE_USER_METATYPE_ENUM macro. This macro must be used in the header file where the enumeration is defined. Additionally we must use the Q_IMPLEMENT_USER_METATYPE_ENUM macro to implement the necessary serialization and deserialization functions.
class CannonController : public QtopiaIpcAdaptor
{
Q_OBJECT
public:
enum AmmunitionType { Explosive = 0, ArmorPiercing, Napalm }
CannonController(QObject *parent = 0);
void shoot();
public slots:
void missed();
void hit();
signals:
void aimCannon(int dir, int elev);
void shootCannon(int power);
void setAmmunitionType(AmmunitionType type);
};
Q_DECLARE_USER_METATYPE_ENUM(CannonController::AmmunitionType)
...
Q_IMPLEMENT_USER_METATYPE_ENUM(CannonController::AmmunitionType)
Custom Classes
In order to shoot our cannon we now need to send three messages to the remote application. For such a complex and mission critical system we should really send only one message for efficiency purposes. We define a new class that encapsulates the entire shoot order:
class CannonFireOrders
{
public:
enum AmmunitionType { Explosive = 0, ArmorPiercing, Napalm }
CannonFireOrders( AmmunitionType type, int direction, int elevation, int power);
int direction() const;
int elevation() const;
int power() const;
AmmunitionType type() const;
private:
int m_dir;
int m_elev;
int m_power;
AmmunitionType m_type;
};
Q_DECLARE_USER_METATYPE_ENUM(CannonFireOrders::AmmunitionType)
Q_DECLARE_USER_METATYPE(CannonFireOrders)
We also modify the CannonController class in the following manner:
class CannonController : public QtopiaIpcAdaptor
{
Q_OBJECT
public:
CannonController(QObject *parent = 0);
void shoot();
public slots:
void missed();
void hit();
signals:
void shootCannon(const CannonFireOrders &orders);
};
The only thing missing is a way to tell the Qt Extended IPC API how to serialize the CannonFireOrders class so it can be shipped to a remote applications, and how the other side would deserialize the information and create a CannonFireOrders. To accomplish this, we must define two new methods in the CannonFireOrders class:
class CannonFireOrders
{
public:
....
template <typename Stream> void serialize(Stream &stream) const;
template <typename Stream> void deserialize(Stream &stream);
...
};
And in the implementation file we implement both methods. It is important to note this fact, as usually templates are defined in the header files!
Q_IMPLEMENT_USER_METATYPE_ENUM(CannonFireOrders::AmmunitionType)
Q_IMPLEMENT_USER_METATYPE(CannonFireOrders)
....
template <typename Stream> void CannonFireOrders::serialize(Stream &s) const
{
s << m_dir;
s << m_elev;
s << m_power;
s << m_type;
}
template <typename Stream> void CannonFireOrders::deserialize(Stream &s)
{
s >> m_dir;
s >> m_elev;
s >> m_power;
s >> m_type;
}
We can now successfully use custom classes across applications! Please note that both applications must link in the implementation of the custom class.
Migrating Legacy Qt Extended Applications
Developers are encouraged to use the new QtopiaIpcAdapter API. It is extremely powerful and can easily support custom classes. However, to support legacy applications and make it easier to port such applications to the new IPC system, Qt Extended introduces two new classes. QtopiaChannel and QtopiaIpcEnvelope. Both of these classes work exactly as their QCop counterparts.
The Application Channel
Each application listens on a channel called QPE/Application/appname , where appname is the executable name (the application identifier). Standard messages on this channel are:
QPE/Application/appname
- setDocument(QString) -- Internal. Causes the setDocument(const QString&) slot of the main document widget be called.
- nextView() -- Applications connected to hardware buttons will receive this message if they are already visible.
- raise() -- Internal. Causes the the main document widget and the current modal widget to be raised.
- quit() -- Internal. Terminates the application.
- quitIfInvisible() -- Internal. Terminates the application if it is not shown.
The QPE/Application/appname channel has a special property: when messages are sent to these channels via QtopiaIpcEnvelope, the message is delivered even if the application is not yet running (the application is run and the message is then sent).
Command-line access to Qt Extended IPC System
The qcop command-line tool can be used to access the Qt Extended IPC system from shell scripts and for debugging purposes.