Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tests/sketch/sketchactivity.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/sketch/sketchactivity.py')
-rw-r--r--tests/sketch/sketchactivity.py234
1 files changed, 234 insertions, 0 deletions
diff --git a/tests/sketch/sketchactivity.py b/tests/sketch/sketchactivity.py
new file mode 100644
index 0000000..007d96e
--- /dev/null
+++ b/tests/sketch/sketchactivity.py
@@ -0,0 +1,234 @@
+# 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
+
+import gtk
+import gobject
+import os
+import logging
+
+from sugar.p2p import MostlyReliablePipe
+from sugar.p2p.Stream import Stream
+
+from sugar.presence import PresenceService
+from sugar.activity.Activity import Activity
+from sugar.chat.sketchpad import SketchPad
+from sugar.chat.sketchpad import Sketch
+from sugar.graphics.iconcolor import IconColor
+from sugar import profile
+
+class NetworkController(gobject.GObject):
+ __gsignals__ = {
+ 'new-path':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
+ }
+
+ def __init__(self, parent, ps_owner):
+ gobject.GObject.__init__(self)
+ self._parent = parent
+ self._parent.connect('buddy-joined', self._buddy_joined)
+ self._parent.connect('buddy-left', self._buddy_left)
+ self._stream = None
+ self._stream_writer = None
+ self._joined_buddies = {} # IP address -> buddy
+ self._ps_owner = ps_owner
+
+ def init_stream(self, service):
+ self._stream = Stream.new_from_service(service)
+ self._stream.set_data_listener(self._recv_message)
+ self._stream_writer = self._stream.new_writer()
+
+ def _recv_message(self, address, msg):
+ # Ignore multicast messages from ourself
+ if self._ps_owner and address == self._ps_owner.get_ip4_address():
+ return
+
+ # Ensure the message comes from somebody in this activity
+ if not self._joined_buddies.has_key(address):
+ logging.debug("Message from unjoined buddy.")
+ return
+
+ # Convert the points to an array and send to the sketchpad
+ points = []
+ msg = msg.strip()
+ split_coords = msg.split(" ")
+ for item in split_coords:
+ x = 0
+ y = 0
+ try:
+ (x, y) = item.split(",")
+ x = float(x)
+ y = float(y)
+ except ValueError:
+ continue
+ if x < 0 or y < 0:
+ continue
+ points.append((x, y))
+
+ buddy = self._joined_buddies[address]
+ self.emit("new-path", buddy, points)
+
+ def _buddy_joined(self, widget, activity, buddy, activity_type):
+ activity_service = buddy.get_service_of_type(activity_type, activity)
+ if not activity_service:
+ logging.debug("Buddy Joined, but could not get activity service " \
+ "of %s" % activity_type)
+ return
+
+ address = activity_service.get_source_address()
+ port = activity_service.get_port()
+ if not address or not port:
+ logging.debug("Buddy Joined, but could not get address/port from" \
+ " activity service %s" % activity_type)
+ return
+ if not self._joined_buddies.has_key(address):
+ logging.debug("Buddy joined: %s (%s)" % (address, port))
+ self._joined_buddies[address] = buddy
+
+ def _buddy_left(self, widget, activity, buddy, activity_type):
+ buddy_key = None
+ for (key, value) in self._joined_buddies.items():
+ if value == buddy:
+ buddy_key = key
+ break
+ if buddy_key:
+ del self._joined_buddies[buddy_key]
+
+ def new_local_sketch(self, path):
+ """ Receive an array of point tuples the local user created """
+ cmd = ""
+ # Convert points into the wire format
+ for point in path:
+ cmd = cmd + "%d,%d " % (point[0], point[1])
+
+ # If there were no points, or we aren't in a shared activity yet,
+ # don't send anything
+ if not len(cmd) or not self._stream_writer:
+ return
+
+ # Send the points to other buddies
+ self._stream_writer.write(cmd)
+
+def _html_to_rgb_color(colorstring):
+ """ converts #RRGGBB to cairo-suitable floats"""
+ colorstring = colorstring.strip()
+ while colorstring[0] == '#':
+ colorstring = colorstring[1:]
+ r = int(colorstring[:2], 16)
+ g = int(colorstring[2:4], 16)
+ b = int(colorstring[4:6], 16)
+ color = ((float(r) / 255.0), (float(g) / 255.0), (float(b) / 255.0))
+ return color
+
+
+class SharedSketchPad(SketchPad.SketchPad):
+ def __init__(self, net_controller, color):
+ SketchPad.SketchPad.__init__(self, bgcolor=(1.0, 0.984313725, 0.560784314))
+ self._net_controller = net_controller
+ self._user_color = _html_to_rgb_color(color)
+ self.set_color(self._user_color)
+
+ # Receive notifications when our buddies send us new sketches
+ self._net_controller.connect('new-path', self._new_buddy_path)
+
+ self.connect('new-user-sketch', self._new_local_sketch_cb)
+
+ def _new_buddy_path(self, net_controller, buddy, path):
+ """ Called whenever a buddy on the mesh sends us a new sketch path """
+ str_color = buddy.get_color()
+ if not str_color:
+ str_color = "#348798" # FIXME
+ color = IconColor(str_color)
+ stroke_color = _html_to_rgb_color(color.get_stroke_color())
+ sketch = Sketch.Sketch(stroke_color)
+ for item in path:
+ sketch.add_point(item[0], item[1])
+ self.add_sketch(sketch)
+
+ def _new_local_sketch_cb(self, widget, sketch):
+ """ Send the sketch the user just made to the network """
+ self._net_controller.new_local_sketch(sketch.get_points())
+
+
+class SketchActivity(Activity):
+ __gsignals__ = {
+ 'buddy-joined':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])),
+ 'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT]))
+ }
+
+ def __init__(self):
+ Activity.__init__(self)
+ self.connect('destroy', self._cleanup_cb)
+
+ self.set_title("Sketch")
+
+ self._ps = PresenceService.get_instance()
+ self._ps_activity = None
+ self._owner = self._ps.get_owner()
+
+ self._net_controller = NetworkController(self, self._owner)
+ self._sketchpad = SharedSketchPad(self._net_controller,
+ profile.get_color().get_stroke_color())
+ self.add(self._sketchpad)
+ self.show_all()
+
+ def get_ps(self):
+ return self._ps
+
+ def _cleanup_cb(self):
+ del self._net_controller
+
+ def share(self):
+ Activity.share(self)
+ self._net_controller.init_stream(self._service)
+ self._ps.connect('activity-appeared', self._activity_appeared_cb)
+
+ def join(self, activity_ps):
+ Activity.join(self, activity_ps)
+ self._net_controller.init_stream(self._service)
+ self._ps.connect('activity-appeared', self._activity_appeared_cb)
+ self._activity_appeared_cb(self._ps, activity_ps)
+
+ def _activity_appeared_cb(self, ps, activity):
+ # Only care about our own activity
+ if activity.get_id() != self.get_id():
+ return
+
+ # If we already have found our shared activity, do nothing
+ if self._ps_activity:
+ return
+
+ self._ps_activity = activity
+
+ # Connect signals to the shared activity so we are notified when
+ # buddies join and leave
+ self._ps_activity.connect('buddy-joined', self._add_buddy)
+ self._ps_activity.connect('buddy-left', self._remove_buddy)
+
+ # Get the list of buddies already in this shared activity so we can
+ # connect to them
+ buddies = self._ps_activity.get_joined_buddies()
+ for buddy in buddies:
+ self._add_buddy(self._ps_activity, buddy)
+
+ def _add_buddy(self, ps_activity, buddy):
+ service_type = self._ps_activity
+ self.emit('buddy-joined', ps_activity, buddy, self.get_default_type())
+
+ def _remove_buddy(self, ps_activity, buddy):
+ self.emit('buddy-left', ps_activity, buddy, self.get_default_type())
+