Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/TurtleArt/collaboration.py
blob: 21cae17ac6d457d01da1bdb8e2cc4e2b27614370 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

from dbus.service import signal
from dbus.gobject_service import ExportedGObject
import logging
import telepathy
from sugar import profile
from sugar.presence import presenceservice
from sugar.presence.tubeconn import TubeConnection

SERVICE = 'org.laptop.TurtleArtActivity'
IFACE = SERVICE
PATH = '/org/laptop/TurtleArtActivity'
_logger = logging.getLogger('turtleart-activity')

class Collaboration():
    def __init__(self, tw, activity):
        """ A simplistic sharing model: the sharer is the master """
        self._tw = tw
        self._activity = activity

    def setup(self):
        # TODO: hand off role of master is sharer leaves
        self.pservice = presenceservice.get_instance()
        self.initiating = None # sharing (True) or joining (False)

        # Add my buddy object to the list
        owner = self.pservice.get_owner()
        self.owner = owner
        self.tw.buddies.append(self.owner)
        self._share = ""

        self._activity.connect('shared', self._shared_cb)
        self._activity.connect('joined', self._joined_cb)

    def _shared_cb(self, activity):
        self._shared_activity = self._activity._shared_activity
        if self._shared_activity is None:
            _logger.error("Failed to share or join activity ... \
                _shared_activity is null in _shared_cb()")
            return

        self.initiating = True
        self.waiting_for_turtles = False
        self.turtle_dictionary = self._get_dictionary()

        _logger.debug('I am sharing...')

        self.conn = self._shared_activity.telepathy_conn
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan
        self.text_chan = self._shared_activity.telepathy_text_chan

        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        _logger.debug('This is my activity: making a tube...')

        id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
            SERVICE, {})

    def _joined_cb(self, activity):
        self._shared_activity = self._activity._shared_activity
        if self._shared_activity is None:
            _logger.error("Failed to share or join activity ... \
                _shared_activity is null in _shared_cb()")
            return

        self.initiating = False
        self.conn = self._shared_activity.telepathy_conn
        self.tubes_chan = self._shared_activity.telepathy_tubes_chan
        self.text_chan = self._shared_activity.telepathy_text_chan

        # call back for "NewTube" signal
        self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(
            'NewTube', self._new_tube_cb)

        _logger.debug('I am joining an 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)

        # Joiner should request current state from sharer.
        self.waiting_for_turtles = True

    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 _new_tube_cb(self, id, initiator, type, service, params, state):
        """ Create a new tube. """
        _logger.debug('New tube: ID=%d initator=%d type=%d service=%s '
                     'params=%r state=%d', id, initiator, type, service,
                     params, state)

        if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE):
            if state == telepathy.TUBE_STATE_LOCAL_PENDING:
                self.tubes_chan[ \
                              telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)

            tube_conn = TubeConnection(self.conn,
                self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \
                group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP])

            # We'll use a chat tube to send serialized stacks back and forth.
            self.chattube = ChatTube(tube_conn, self.initiating, \
                self.event_received_cb)

            # Now that we have the tube, we can ask for the turtle dictionary.
            if self.waiting_for_turtles:
                _logger.debug("Sending a request for the turtle dictionary")
                # we need to send our own nick and colors
                colors = self._get_colors()
                event = "t|" + data_to_string([self._get_nick(), colors])
                _logger.debug(event)
                self.send_event(event)

    def event_received_cb(self, text):
        """
        Events are sent as a tuple, nick|cmd, where nick is a turle name
        and cmd is a turtle event. Everyone gets the turtle dictionary from
        the sharer and watches for 't' events, which indicate that a new
        turtle has joined.
        """
        if len(text) == 0:
            return
        # Save active Turtle
        save_active_turtle = self.tw.active_turtle
        e = text.split("|", 2)
        text = e[1]
        if e[0] == 't': # request for turtle dictionary
            if text > 0:
                [nick, colors] = data_from_string(text)
                if nick != self.tw.nick:
                    # There may not be a turtle dictionary.
                    if hasattr(self, "turtle_dictionary"):
                        self.turtle_dictionary[nick] = colors
                    else:
                        self.turtle_dictionary = {nick: colors}
                    # Add new turtle for the joiner.
                    self.tw.canvas.set_turtle(nick, colors)
            # Sharer should send turtle dictionary.
            if self.initiating:
                text = data_to_string(self.turtle_dictionary)
                self.send_event("T|" + text)
        elif e[0] == 'T': # Receiving the turtle dictionary.
            if self.waiting_for_turtles:
                if len(text) > 0:
                    self.turtle_dictionary = data_from_string(text)
                    for nick in self.turtle_dictionary:
                        if nick != self.tw.nick:
                            colors = self.turtle_dictionary[nick]
                            # add new turtle for the joiner
                            self.tw.canvas.set_turtle(nick, colors)
                self.waiting_for_turtles = False
        elif e[0] == 'f': # move a turtle forward
            if len(text) > 0:
                [nick, x] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.forward(x, False)
        elif e[0] == 'a': # move a turtle in an arc
            if len(text) > 0:
                [nick, [a, r]] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.arc(a, r, False)
        elif e[0] == 'r': # rotate turtle
            if len(text) > 0:
                [nick, h] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.seth(h, False)
        elif e[0] == 'x': # set turtle xy position
            if len(text) > 0:
                [nick, [x, y]] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.setxy(x, y, False)
        elif e[0] == 'c': # set turtle pen color
            if len(text) > 0:
                [nick, x] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.setcolor(x, False)
        elif e[0] == 'g': # set turtle pen gray level
            if len(text) > 0:
                [nick, x] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.setgray(x, False)
        elif e[0] == 's': # set turtle pen shade
            if len(text) > 0:
                [nick, x] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.setshade(x, False)
        elif e[0] == 'w': # set turtle pen width
            if len(text) > 0:
                [nick, x] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.setpensize(x, False)
        elif e[0] == 'p': # set turtle pen state
            if len(text) > 0:
                [nick, x] = data_from_string(text)
                if nick != self.tw.nick:
                    self.tw.canvas.set_turtle(nick)
                    self.tw.canvas.setpen(x, False)
        # Restore active Turtle
        self.tw.canvas.set_turtle(self.tw.turtles.get_turtle_key(
                save_active_turtle))

    def send_event(self, entry):
        """ Send event through the tube. """
        if hasattr(self, 'chattube') and self.chattube is not None:
            self.chattube.SendText(entry)

    def _get_dictionary(self):
        d = { self._get_nick(): self._get_colors()}
        return d

    def _get_nick(self):
        return self.tw.nick

    def _get_colors(self):
        return profile.get_color().to_string()

class ChatTube(ExportedGObject):

    def __init__(self, tube, is_initiator, stack_received_cb):
        """Class for setting up tube for sharing."""
        super(ChatTube, self).__init__(tube, PATH)
        self.tube = tube
        self.is_initiator = is_initiator # Are we sharing or joining activity?
        self.stack_received_cb = stack_received_cb
        self.stack = ''

        self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, \
            path=PATH, sender_keyword='sender')

    def send_stack_cb(self, text, sender=None):
        if sender == self.tube.get_unique_name():
            return
        self.stack = text
        self.stack_received_cb(text)

    @signal(dbus_interface=IFACE, signature='s')
    def SendText(self, text):
        self.stack = text