Collaboration Tutorial
This tutorial focuses on activity sharing. Read Shared Sugar Activities for an overview of the concepts. See also Presence Service and Tubes.
Collaboration is implemented with Telepathy, a D-Bus framework for Instant Messaging protocols. The Presence Service provides functionality to Sugar to track shared Activities and Buddies, and talks to Telepathy connection managers for XMPP via a Jabber server (telepathy-gabble), and Link Local XMPP (telepathy-salut).
XMPP is used to provide presence information about Buddies (other XOs you can collaborate with). The mesh view is like an instant messenger buddy list, represented in a more graphical way.
Not all the Buddies you see are on the same network as you. In particular, any of them could be behind a NAT firewall, and so you cannot simply open a TCP/IP socket to them to exchange data.
XMPP via the Jabber server on the school server allows you to exchange data with all the Buddies you can see, whether or not they are directly reachable from your XO.
Chat Rooms
A shared activity is implemented using an XMPP MUC (multi user chat room). Buddies that are in the room, are in the shared activity.
PresenceService provides each shared activity with a Telepathy Text Channel by default. This text channel is a connection to the XMPP chat room, but is not useful for data sharing directly. You can see an example of this Text Channel in direct use in the Chat activity. In future, Sugar will provide "overlay chat" to activities, allowing you to do text chat with the participants of a shared activity.
On top of this Text Channel, we use a dbus Tubes channel to exchange data.
Tubeses!!!
There are two types of Tubes at present: D-Bus Tubes and Stream Tubes.
D-bus Tubes provide D-bus functionality to signal and call methods on everyone in the room.
Stream Tubes wrap TCP/IP sockets and are more suited to streaming data between two participants.
D-Bus Tubes
See the D-Bus tutorial and the dbus-python tutorial.
D-Bus provides signals and methods:
- Signals are multicast - they are sent to all participants in the shared activity (including the sender). They send data and have no return value.
- Method calls are called on a single participant, and they do have a return value.
It is important to design your Activity's D-Bus Tubes API well, to support the functionality that your collaboration will need.
More to come here...
Bus names, handles, Buddy objects
Signals and Methods use bus names to identify senders and recipients. Telepathy uses handles to identify participants. Sugar uses Buddy objects.
The TubeConnection object can convert between bus names and handles - see tubeconn.py for details.
More to come here...
HelloMesh example
HelloMesh is a demo activity with sample code using a D-Bus Tube. The code is in git://dev.laptop.org/projects/hellomesh and can be built in sugar-jhbuild with ./sugar-jhbuild buildone hellomesh
First, let's look at HelloMesh's D-Bus Tubes API. There is one signal and one method:
@signal(dbus_interface=IFACE, signature=) def Hello(self): """Say Hello to whoever else is in the tube.""" self._logger.debug('I said Hello.')
@method(dbus_interface=IFACE, in_signature='s', out_signature=) def World(self, game_state): """To be called on the incoming XO after they Hello.""" if not self.helloworld: self._logger.debug('Somebody said World.') self.helloworld = game_state # now I can World others self.add_hello_handler() else: self._logger.debug("I've already been welcomed, doing nothing")
When a new participant joins the shared activity, it sends the Hello signal. This goes to all existing participants. The Hello signal has an empty signature, since no actual parameters are being sent.
When an (existing) participant receives the Hello signal, it calls the World method of the signal's sender. The World method takes a string parameter (game_state) represented by
in_signature='s'
It doesn't return anything, hence
out_signature=''
Calling World when Hello is received is done in hello_cb:
def hello_cb(self, sender=None): """Somebody Helloed me. World them.""" if sender == self.tube.get_unique_name(): # sender is my bus name, so ignore my own signal return self._logger.debug('Newcomer %s has joined', sender) self._logger.debug('Welcoming newcomer and sending them the game state') game_state = str(time.time()) # Something to send for demo self.tube.get_object(sender, PATH).World(game_state, dbus_interface=IFACE)
Note that signals go to all participants, including yourself - the above code shows how to ignore your own signals.
sender is a bus (as in D-Bus) name, like :2.OWI0MTk2MzNAdmFpbwAA. Signals and Methods use these to identify senders and recipients.
In order to receive the Hello signal, we need to add a signal receiver:
def add_hello_handler(self): self._logger.debug('Adding hello handler.') self.tube.add_signal_receiver(self.hello_cb, 'Hello', IFACE, path=PATH, sender_keyword='sender')
You can see this called in the World method above, because in this example once we have had our World method called by an existing participant, we can then respond to somebody else's Hello signal.
More to come here...
Stream Tubes
See the Read activity's code for an example. It's in git://dev.laptop.org/projects/read-activity and is built by default in sugar-jhbuild.
More to come here...