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
|