diff options
author | Morgan Collett <morgan.collett@collabora.co.uk> | 2007-06-07 18:26:27 (GMT) |
---|---|---|
committer | Morgan Collett <morgan.collett@collabora.co.uk> | 2007-06-07 18:26:27 (GMT) |
commit | 01076ff22a8bcefa7155d9f67874e75d72a50bce (patch) | |
tree | 2339f9278943ce0c0ba4048d3f816ed9020f1fa5 |
Initial import
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | activity.py | 206 | ||||
-rw-r--r-- | activity/activity-hellomesh.svg | 16 | ||||
-rw-r--r-- | activity/activity.info | 7 | ||||
-rwxr-xr-x | setup.py | 22 | ||||
-rw-r--r-- | tubeconn.py | 107 |
6 files changed, 361 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1912417 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +Connect-*.xo +*.pyc +.*.sw[op] diff --git a/activity.py b/activity.py new file mode 100644 index 0000000..fb0eebe --- /dev/null +++ b/activity.py @@ -0,0 +1,206 @@ +# Copyright 2007 Collabora Ltd. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +"""HelloMesh Activity: A case study for collaboration using Tubes.""" + +import dbus +import hippo +import gtk +import pango +import logging +import telepathy +import telepathy.client + +from sugar import profile +from sugar.activity.activity import Activity, ActivityToolbox +from sugar.graphics import font +from sugar.graphics.canvasicon import CanvasIcon +from sugar.graphics.roundbox import RoundBox +from sugar.graphics.xocolor import XoColor +from sugar.graphics.units import points_to_pixels as px +from sugar.presence import presenceservice + +# will eventually be imported from telepathy.tubes or something +from tubeconn import TubeConnection + +logger = logging.getLogger('hellomesh-activity') + +class HelloMeshActivity(Activity): + """HelloMesh Activity as specified in activity.info""" + def __init__(self, handle): + """Set up the HelloMesh activity.""" + Activity.__init__(self, handle) + self.set_title('HelloMesh Activity') + + # top toolbar with share and close buttons: + toolbox = ActivityToolbox(self) + self.set_toolbox(toolbox) + toolbox.show() + + # Hippo Canvas: + hbox = hippo.CanvasBox(spacing=4, + orientation=hippo.ORIENTATION_HORIZONTAL) + + self.main_panel = hippo.CanvasBox(spacing=4, + orientation=hippo.ORIENTATION_VERTICAL) + self.entry = gtk.Entry() + self.main_panel.append(hippo.CanvasWidget(widget=self.entry)) + hbox.append(self.main_panel, hippo.PACK_EXPAND) + + canvas = hippo.Canvas() + canvas.set_root(hbox) + self.set_canvas(canvas) + self.show_all() + + # get the Presence Service + self.pservice = presenceservice.get_instance() + bus = dbus.Bus() + name, path = self.pservice.get_preferred_connection() + self.tp_conn_name = name + self.tp_conn_path = path + self.conn = telepathy.client.Connection(name, path) + self.initiating = None + + self.connect('shared', self._shared_cb) + + # Buddy object for you + owner = self.pservice.get_owner() + self.owner = owner + + if self._shared_activity: + # we are joining the activity + self.connect('joined', self._joined_cb) + self._shared_activity.connect('buddy-joined', + self._buddy_joined_cb) + self._shared_activity.connect('buddy-left', + self._buddy_left_cb) + if self.get_shared(): + # we've already joined + self._joined_cb() + else: + # we are creating the activity + pass + + def _shared_cb(self, activity): + logger.debug('My activity was shared') + self.initiating = True + self._setup() + + for buddy in self._shared_activity.get_joined_buddies(): + pass # XXX + + self._shared_activity.connect('buddy-joined', self._buddy_joined_cb) + self._shared_activity.connect('buddy-left', self._buddy_left_cb) + + logger.debug('This is my activity: making a tube...') + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferTube( + telepathy.TUBE_TYPE_DBUS, SERVICE, {}) + + # FIXME: presence service should be tubes-aware and give us more help + # with this + def _setup(self): + if self._shared_activity is None: + logger.error('Failed to share or join activity') + return + + bus_name, conn_path, channel_paths =\ + self._shared_activity.get_channels() + + # Work out what our room is called and whether we have Tubes already + room = None + tubes_chan = None + text_chan = None + for channel_path in channel_paths: + channel = telepathy.client.Channel(bus_name, channel_path) + htype, handle = channel.GetHandle() + if htype == telepathy.HANDLE_TYPE_ROOM: + logger.debug('Found our room: it has handle#%d "%s"', + handle, self.conn.InspectHandles(htype, [handle])[0]) + room = handle + ctype = channel.GetChannelType() + if ctype == telepathy.CHANNEL_TYPE_TUBES: + logger.debug('Found our Tubes channel at %s', channel_path) + tubes_chan = channel + elif ctype == telepathy.CHANNEL_TYPE_TEXT: + logger.debug('Found our Text channel at %s', channel_path) + text_chan = channel + + if room is None: + logger.error("Presence service didn't create a room") + return + if text_chan is None: + logger.error("Presence service didn't create a text channel") + return + + # Make sure we have a Tubes channel - PS doesn't yet provide one + if tubes_chan is None: + logger.debug("Didn't find our Tubes channel, requesting one...") + tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES, + telepathy.HANDLE_TYPE_ROOM, room, True) + + self.tubes_chan = tubes_chan + self.text_chan = text_chan + + tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', + self._new_tube_cb) + + def _list_tubes_reply_cb(self, tubes): + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + logger.error('ListTubes() failed: %s', e) + + def _joined_cb(self, activity): + if self.game is not None: + return + + if not self._shared_activity: + return + + for buddy in self._shared_activity.get_joined_buddies(): + pass # XXX do stuff with buddy + + logger.debug('Joined an existing shared activity') + self.initiating = False + self._setup() + + logger.debug('This is not my activity: waiting for a tube...') + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', id, initiator, type, service, + params, state) + + #if (self.game is None and type == telepathy.TUBE_TYPE_DBUS and + # service == SERVICE): + # if state == telepathy.TUBE_STATE_LOCAL_PENDING: + # self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptTube(id) + + # tube_conn = TubeConnection(self.conn, + # self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], + # id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + # self.game = ConnectGame(tube_conn, self.grid, self.initiating, + # self.buddies_panel, self.owner, self._get_buddy, self) + + def _buddy_joined_cb (self, activity, buddy): + logger.debug('Buddy %s joined' % buddy.props.nick) + + def _buddy_left_cb (self, activity, buddy): + logger.debug('Buddy %s left' % buddy.props.nick) diff --git a/activity/activity-hellomesh.svg b/activity/activity-hellomesh.svg new file mode 100644 index 0000000..45ba759 --- /dev/null +++ b/activity/activity-hellomesh.svg @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 12.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
+ <!ENTITY ns_svg "http://www.w3.org/2000/svg">
+ <!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
+ <!ENTITY stroke_color "#020202"> + <!ENTITY fill_color "#666666"> +]>
+<svg version="1.1" id="Icon" xmlns="&ns_svg;" xmlns:xlink="&ns_xlink;" width="48.92" height="43.846" viewBox="0 0 48.92 43.846"
+ overflow="visible" enable-background="new 0 0 48.92 43.846" xml:space="preserve">
+<path id="Bubble" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5" d="M6.348,41.615c0.682,1.151,6.027,0.059,8.246-1.464
+ c2.102-1.432,3.207-2.596,4.336-2.596c1.133,0,12.54,0.92,20.935-5.715c7.225-5.707,9.772-13.788,4.52-21.438
+ c-5.252-7.644-13.831-9.079-20.878-8.56C13.891,2.562,1.309,10.13,1.762,21.533c0.264,6.711,3.357,9.143,4.922,10.702
+ c1.562,1.566,4.545,1.566,2.992,5.589C9.066,39.402,5.838,40.742,6.348,41.615z"/>
+</svg>
+ diff --git a/activity/activity.info b/activity/activity.info new file mode 100644 index 0000000..cae2946 --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,7 @@ +[Activity] +name = HelloMesh +service_name = org.laptop.HelloMesh +class = activity.HelloMeshActivity +icon = activity-hellomesh +activity_version = 1 +show_launcher = yes diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..fae74b8 --- /dev/null +++ b/setup.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python + +# Copyright (C) 2006, Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.activity import bundlebuilder + +bundlebuilder.start() + diff --git a/tubeconn.py b/tubeconn.py new file mode 100644 index 0000000..d1c1403 --- /dev/null +++ b/tubeconn.py @@ -0,0 +1,107 @@ +# This should eventually land in telepathy-python, so has the same license: + +# Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +__all__ = ('TubeConnection',) +__docformat__ = 'reStructuredText' + + +import logging + +from dbus.connection import Connection + + +logger = logging.getLogger('telepathy.tubeconn') + + +class TubeConnection(Connection): + + def __new__(cls, conn, tubes_iface, tube_id, address=None, + group_iface=None, mainloop=None): + if address is None: + address = tubes_iface.GetDBusServerAddress(tube_id) + self = super(TubeConnection, cls).__new__(cls, address, + mainloop=mainloop) + + self._tubes_iface = tubes_iface + self.tube_id = tube_id + self.participants = {} + self.bus_name_to_handle = {} + self._mapping_watches = [] + + if group_iface is None: + method = conn.GetSelfHandle + else: + method = group_iface.GetSelfHandle + method(reply_handler=self._on_get_self_handle_reply, + error_handler=self._on_get_self_handle_error) + + return self + + def _on_get_self_handle_reply(self, handle): + self.self_handle = handle + match = self._tubes_iface.connect_to_signal('DBusNamesChanged', + self._on_dbus_names_changed) + self._tubes_iface.GetDBusNames(self.tube_id, + reply_handler=self._on_get_dbus_names_reply, + error_handler=self._on_get_dbus_names_error) + self._dbus_names_changed_match = match + + def _on_get_self_handle_error(self, e): + logging.basicConfig() + logger.error('GetSelfHandle failed: %s', e) + + def close(self): + self._dbus_names_changed_match.remove() + self._on_dbus_names_changed(self.tube_id, (), self.participants.keys()) + super(TubeConnection, self).close() + + def _on_get_dbus_names_reply(self, names): + self._on_dbus_names_changed(self.tube_id, names, ()) + + def _on_get_dbus_names_error(self, e): + logging.basicConfig() + logger.error('GetDBusNames failed: %s', e) + + def _on_dbus_names_changed(self, tube_id, added, removed): + if tube_id == self.tube_id: + for handle, bus_name in added: + if handle == self.self_handle: + # I've just joined - set my unique name + self.set_unique_name(bus_name) + self.participants[handle] = bus_name + self.bus_name_to_handle[bus_name] = handle + + # call the callback while the removed people are still in + # participants, so their bus names are available + for callback in self._mapping_watches: + callback(added, removed) + + for handle in removed: + bus_name = self.participants.pop(handle, None) + self.bus_name_to_handle.pop(bus_name, None) + + def watch_participants(self, callback): + self._mapping_watches.append(callback) + if self.participants: + # GetDBusNames already returned: fake a participant add event + # immediately + added = [] + for k, v in self.participants.iteritems(): + added.append((k, v)) + callback(added, []) |