Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJonas Smedegaard <dr@jones.dk>2008-08-16 14:06:28 (GMT)
committer Jonas Smedegaard <dr@jones.dk>2008-08-16 14:06:28 (GMT)
commite9a6b3a879bcae54e16c0ea10d103a4aca19bb4d (patch)
tree0ed8f67382de33ef64090f33cb7f6ab8c76f06e1
parent1180e8e81dba1ede1cefdad817e64f194c15d4f4 (diff)
Imported Upstream version 23upstream/23
-rw-r--r--sharedstate.git/MANIFEST1
-rw-r--r--sharedstate.git/SharingTest.py113
-rw-r--r--sharedstate.git/activity/activity-shtest.svg8
-rw-r--r--sharedstate.git/activity/activity.info7
-rw-r--r--sharedstate.git/setup.py3
-rw-r--r--sharedstate.git/sharedstate/README10
-rw-r--r--sharedstate.git/sharedstate/__init__.py1
-rw-r--r--sharedstate.git/sharedstate/shareddict.py107
-rw-r--r--sharedstate.git/sharedstate/sharedobject.py436
-rw-r--r--sharedstate.git/sharedstate/sharedobjects.py25
-rw-r--r--sharedstate.git/sharedstate/sharedpython.py234
-rw-r--r--sharedstate.git/sharedstate/sharedstate.py532
-rw-r--r--sharedstate.git/sharedstate/sharedtext.py181
-rw-r--r--sharedstate.git/sharedstate/tubeconn.py107
14 files changed, 0 insertions, 1765 deletions
diff --git a/sharedstate.git/MANIFEST b/sharedstate.git/MANIFEST
deleted file mode 100644
index a81177f..0000000
--- a/sharedstate.git/MANIFEST
+++ /dev/null
@@ -1 +0,0 @@
-SharingTest.py
diff --git a/sharedstate.git/SharingTest.py b/sharedstate.git/SharingTest.py
deleted file mode 100644
index c8e0a0c..0000000
--- a/sharedstate.git/SharingTest.py
+++ /dev/null
@@ -1,113 +0,0 @@
-from sugar.activity.activity import Activity, ActivityToolbox
-
-import logging
-_logger = logging.getLogger('shtest-activity')
-
-from sharedstate.sharedstate import SharingHelper
-
-from gettext import gettext as _
-
-import pygtk
-pygtk.require('2.0')
-import gtk
-
-class SharingTest(Activity):
- def __init__(self, handle):
- Activity.__init__(self, handle)
-
- _logger.info('Starting SharingHelper test...')
-
- toolbox = ActivityToolbox(self)
- self.set_toolbox(toolbox)
- toolbox.show()
-
- self.set_title('SharingTest')
-
- hc = gtk.HBox(spacing=20)
- hc.set_border_width(20)
- vc1 = gtk.VBox()
- self.dict_box = gtk.TextView() # where the current dict is shown
- self.dict_box.set_editable(False)
- vc1.add(self.dict_box)
- hc2 = gtk.HBox(spacing=20)
- hc2.set_border_width(20)
-
- self.dict_key_box = gtk.Entry() # new key => val boxes
- self.dict_key_box.set_size_request(100, 40)
- hc2.add(self.dict_key_box)
- self.dict_val_box = gtk.Entry()
- self.dict_val_box.set_size_request(100, 40)
- hc2.add(self.dict_val_box)
- vc1.add(hc2)
- b2 = gtk.Button('add to dict') # add button
- b2.connect('clicked', self.add_button_clicked_cb)
- vc1.add(b2)
- hc.add(vc1)
-
- vc2 = gtk.VBox()
- b1 = gtk.Button('Counter++') # counter button
- b1.connect('clicked', self.counter_button_clicked_cb)
- vc2.add(b1)
- self.CounterLabel = gtk.Label('0') # counter label
- vc2.add(self.CounterLabel)
-
- self.edit_box = gtk.TextView() # shared editable text
- self.edit_box.set_editable(True)
- self.changed_signal = self.edit_box.get_buffer().connect('changed', self.edit_box_changed_cb)
- vc2.add(self.edit_box)
-
- hc.add(vc2)
- self.get_child().add(hc)
-
-# Setup the shared objects
- self.helper = SharingHelper(self)
- self.helper.create_shared_object('counter', {
- 'changed': self.update_counter_cb
- }, iv=0)
-
- self.helper.create_shared_object('sdict', {
- 'changed': self.update_dict_cb
- }, iv={'key1': 'first key/val'})
-
- self.helper.create_shared_object('stext', {
- 'changed': self.update_text_cb,
- 'type': 'python' # or nothing to autotype to SharedText
- }, iv='start of shared text')
-
-# And show everything
- self.show_all()
-
- def counter_button_clicked_cb(self, info):
- _logger.debug('Counter clicked')
- self.helper['counter'] = self.helper['counter'] + 1
-
- def update_counter_cb(self, val):
- _logger.debug('update_counter_cb(): %r', val)
- self.CounterLabel.set_text('%d' % (val))
-
- def add_button_clicked_cb(self, info):
- _logger.debug('Add clicked')
- self.helper['sdict'][self.dict_key_box.get_text()] = self.dict_val_box.get_text()
- self.dict_key_box.set_text('')
- self.dict_val_box.set_text('')
-
- def update_dict_cb(self, val):
- _logger.debug('update_dict_cb(): %r', val)
- str = ''
- for k, v in val.iteritems():
- str += "%s => %s\n" % (k, v)
- self.dict_box.get_buffer().set_text(str)
-
- def edit_box_changed_cb(self, tbuf):
- _logger.debug('text_changed_cb(): changed')
- self.helper['stext'] = tbuf.get_text(tbuf.get_start_iter(), tbuf.get_end_iter())
-
- def update_text_cb(self, val):
- _logger.debug('update_text_cb(): %s', val)
- buf = self.edit_box.get_buffer()
-# pos = buf.get_iter_at_offset(buf.get_property('cursor_position'))
- buf.handler_block(self.changed_signal)
- buf.set_text(val)
- buf.handler_unblock(self.changed_signal)
-# buf.place_cursor(pos)
-
diff --git a/sharedstate.git/activity/activity-shtest.svg b/sharedstate.git/activity/activity-shtest.svg
deleted file mode 100644
index 8c79234..0000000
--- a/sharedstate.git/activity/activity-shtest.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
- <!ENTITY fill_color "#FFFFFF">
- <!ENTITY stroke_color "#000000">
-]>
-<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50">
- <rect x="11" y="11" width="26" height="26" style="fill:&fill_color;;stroke:&stroke_color;;stroke-width:2"/>
-</svg>
diff --git a/sharedstate.git/activity/activity.info b/sharedstate.git/activity/activity.info
deleted file mode 100644
index 1c37f71..0000000
--- a/sharedstate.git/activity/activity.info
+++ /dev/null
@@ -1,7 +0,0 @@
-[Activity]
-name = SharingTest
-service_name = org.laptop.SharingTest
-class = SharingTest.SharingTest
-icon = activity-shtest
-activity_version = 1
-show_launcher = yes
diff --git a/sharedstate.git/setup.py b/sharedstate.git/setup.py
deleted file mode 100644
index ec0f64e..0000000
--- a/sharedstate.git/setup.py
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env python
-from sugar.activity import bundlebuilder
-bundlebuilder.start()
diff --git a/sharedstate.git/sharedstate/README b/sharedstate.git/sharedstate/README
deleted file mode 100644
index 229cefd..0000000
--- a/sharedstate.git/sharedstate/README
+++ /dev/null
@@ -1,10 +0,0 @@
-class SharedObject
-
-options:
- -incremental (boolean):
- default encoding style
- -contiguous (boolean): [NOT YET IMPLEMENTED]
- whether only contiguous updates are allowed
- -dynamic (boolean): [PARTIALLY IMPLEMENTED]
- whether this is a dynamically created object
-
diff --git a/sharedstate.git/sharedstate/__init__.py b/sharedstate.git/sharedstate/__init__.py
deleted file mode 100644
index 8b13789..0000000
--- a/sharedstate.git/sharedstate/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/sharedstate.git/sharedstate/shareddict.py b/sharedstate.git/sharedstate/shareddict.py
deleted file mode 100644
index 1a51a12..0000000
--- a/sharedstate.git/sharedstate/shareddict.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# shareddict.py, classes to aid activities in sharing a state
-# Reinier Heeres, reinier@heeres.eu
-#
-# 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
-#
-# Change log:
-# 2007-06-21: rwh, first version
-
-import logging
-_logger = logging.getLogger('sharinghelper')
-
-from sharedobject import SharedObject
-
-class SharedDict(SharedObject):
- """Shared dictionary object, generates key-based difference objects"""
-
- def __init__(self, name, helper, opt={}):
- SharedObject.__init__(self, name, helper, opt=opt)
- self._value = {}
-
- def __getitem__(self, key):
-# _logger.debug('SharedDict.__getitem__(%s)', key)
- return self.get_key(key)
-
- def __setitem__(self, key, val):
-# _logger.debug('SharedDict.__setitem__(%s, %s)', key, val)
- return self.set_key(key, val)
-
- def __delitem__(self, key):
- self.delete_key(key)
-
- def get_key(self, key, val):
- return self._value[key]
-
- def set_key(self, key, val):
- if key in self._value:
- d = {'change': {key: val}}
- else:
- d = {'add': {key: val}}
- self._value[key] = val
- self.changed(d, True)
-
- def delete_key(self, key):
- if key in self._value:
- del self._value[key]
- d = {'remove': key}
- self.changed(d, True)
-
- def _compatible_diffs(self, diffa, diffb):
- for key in diffa:
- if key in diffb:
- return False
- return True
-
- def diff(self, cur, old):
- ret = {'remove': [], 'add': {}, 'change': {}}
- if old is None:
- return ret
-
- for key in set(old.keys()).difference(cur.keys()):
- ret['remove'].append(key)
-
- for key in set(cur.keys()).difference(old.keys()):
- ret['add'][key] = cur[key]
-
- for key in set(cur.keys()).intersection(old.keys()):
- if cur[key] is not old[key]:
- ret['change'][key] = cur[key]
-
- return ret
-
- def _apply_diff_to(self, obj, diffobj):
- """Apply a diff and return an object that describes the inverse diff"""
-
- if diffobj is None:
- return (None, None)
-
- ret = {'add':{}, 'remove':[], 'change':{}}
-
- if 'remove' in diffobj:
- for key in diffobj['remove']:
- ret['add'][key] = obj[key]
- del self._value[key]
-
- if 'add' in diffobj:
- for key, val in diffobj['add'].iteritems():
- ret['remove'].append(key)
- obj[key] = val
-
- if 'change' in diffobj:
- for key, val in diffobj['change'].iteritems():
- ret[key] = self._value[key]
- obj[key] = val
-
- return (obj, ret)
diff --git a/sharedstate.git/sharedstate/sharedobject.py b/sharedstate.git/sharedstate/sharedobject.py
deleted file mode 100644
index 97472fc..0000000
--- a/sharedstate.git/sharedstate/sharedobject.py
+++ /dev/null
@@ -1,436 +0,0 @@
-# sharedobject.py, classes to aid activities in sharing a state
-# Reinier Heeres, reinier@heeres.eu
-# Miguel Alvarez, miguel@laptop.org
-#
-# 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
-#
-# Change log:
-# 2007-07-14: rwh, the big merge. Old function are kept with _nc
-# (non-contiguous) appended
-# 2007-07-07: miguel, conflict resolution added
-# 2007-06-21: rwh, first version
-
-import copy
-import pickle
-import base64
-import difflib
-import time
-
-import logging
-_logger = logging.getLogger('sharinghelper')
-
-class DiffRec:
- def __init__(self, versionid, sender, incremental, objstr):
- self.version_id = versionid
- self.sender = sender
- self.incremental = incremental
- self.obj = objstr
-
- def is_newer(self, v2):
- if self.version_id > v2.version_id or \
- (self.version_id == v2.version_id and self.sender > v2.sender):
- return True
- else:
- return False
-
- def __str__(self):
- return "[%d]%s" % (self.version_id, str(self.obj))
-
- def __repr__(self):
- return self.__str__()
-
-class SharedObject:
- """Base class for shared objects, able to share python objects the
- dumb way"""
-
- def __init__(self, name, helper, opt = {}):
- self._name = name
- self._options = opt
- self._helper = helper
- self._value = None
- self._version_id = 0
- self._received_diffs = []
- self._inverse_diffs = []
- self._pending_diffs = {}
- self._cached_versions = 8
- self._locked = False
- self._locked_by = None
- self._locked_time = 0
-
- def encode(self, obj):
- return base64.b64encode(pickle.dumps(obj))
-
- def decode(self, obj):
- return pickle.loads(base64.b64decode(obj))
-
- def _should_encode_incremental(self, incremental):
- """Decide whether to encode an object incrementally. If nothing is
- specified (incremental=None) determine from self._options, else
- return what is specified."""
-
- if incremental is None:
- if 'incremental' in self._options and self._options['incremental'] is True:
- return True
- else:
- return False
- return incremental
-
- def changed_nc(self, diffobj, incremental):
- """This function should be called when the object has changed."""
-
- self._version_id += 1
- try:
- diff = DiffRec(self._version_id, self._helper._own_bus_name, incremental, diffobj)
- self.insert_diff(diff)
- enc = self.encode(diff.obj)
- if self._helper.tube_connected():
- self._helper.SendObject(self._name, self._version_id, incremental, enc)
- except Exception, inst:
- _logger.error('changed(): %s', inst)
-
- self.do_changed_callback()
-
- def changed(self, diffobj, incremental=False):
- """This function should be called when the object has change
- If diffobj is None the whole object will be sent
- If defined, only this difference will be sent
- A diff entry is also added to our received_diffs so that
- we can properly sequence and undo/redo this change.
- """
-
- try:
- self.insert_diff(DiffRec(self._version_id+1,
- self._helper._own_bus_name, incremental, diffobj))
- if not incremental:
- enc = self.encode(self._value)
- else:
- enc = self.encode(diffobj)
- self._helper.SendObject(self._name, self._version_id, incremental, enc)
- _logger.debug("Modification sent")
- except Exception, inst:
- _logger.error('changed(): %s, currval: %s', inst, self._value)
- self.do_changed_callback()
-
- def set_value_nc(self, v, incremental=None):
- """Function to set value of this object. Specifying incremental
- (a boolean) allows forcing of either incremental/full encoding.
- If not specified, the default will be used from self._options"""
-
- incremental = self._should_encode_incremental(incremental)
- _logger.debug('Setting value of %s to %s, incremental=%s', self._name, v, incremental)
- if incremental:
- old = copy.deepcopy(self._value)
- self._value = v
- d = self.diff(v, old)
- _logger.debug('set_value(): generated diff:\n%r', d)
- del old
- if d is not None:
- self.changed(d, True)
- else:
- self._value = v
- self.changed_nc(v, False)
-
- def set_value(self, v, incremental=False):
- incremental = self._should_encode_incremental(incremental)
- _logger.debug('Setting value of %s to %s, incremental=%s', self._name, v, incremental)
- old = copy.deepcopy(self._value)
- d = self.diff(v, old)
- _logger.debug("Diff= %s", d)
- self.changed(d, incremental)
-
- def get_value(self):
- return self._value
-
- def do_changed_callback(self):
- if 'changed' in self._options:
- self._options['changed'](self._value)
-
- def output_diff_stack(self):
- _logger.debug('Diff stack:')
- for versionid, incremental, objstr, sender in self._received_diffs:
- _logger.debug('\tv:%d, inc:%d, sender:%d', versionid,
- incremental, sender)
-
- def get_version(self, versionid):
- if versionid == self._version_id:
- return self._value
- if versionid > self._version_id or versionid < 0:
- return None
- target_index = self._get_diff_index(versionid + 1) #versionid of a diff== the version it leads _up_ to
- if 0 > target_index or index > len(self._received_diffs):
- return None
- object = self._value
- for i in range(len(self._received_diffs)-1, target_index -1, -1):
- d = self._received_diffs[i]
- object = self._apply_diff_to(object, self.inverse_diff(diff))
- return object
-
- def get_version(self, versionid):
- if versionid == self._version_id:
- return self._value
- if versionid > self._version_id or versionid < 0:
- return None
- if versionid < self._received_diffs[0].version_id:
- return None
-
- i = len(self._received_diffs) - 1
- while self._version_id > versionid:
- obj = self._apply_diff_to(obj, self.inverse_diff(diff))
- i -= 1
-
- return obj
-
- def inverse_diff(self, diff):
- """Invert a diff object, so that if was the result of an o -> n comparison, the
- result is associated to a n -> o comparison."""
- index = self._get_diff_index(diff)
- if 0 < index < len(self._inverse_diffs): #found (?)
- return self._inverse_diffs[index]
- return None
-
- def _get_diff_index(self, vi):
- for i in xrange(len(self._received_diffs)-1, -1 , -1):
- if self._received_diffs[i].version_id == vi:
- return i
- return -1
-
- def insert_diff_nc(self, d):
- if len(self._received_diffs) > 0 and self._received_diffs[0].is_newer(d):
- return -1
-
- if len(self._received_diffs) >= self._cached_versions:
- del(self._received_diffs[0])
-
- i = len(self._received_diffs) - 1
- while i > -1:
- if d.is_newer(self._received_diffs[i]):
- break
- i -= 1
-
- self._received_diffs.insert(i + 1, d)
- self._inverse_diffs.insert(i + 1, DiffRec(d.version_id, d.sender, d.incremental, None))
-
- return i + 1
-
- def insert_diff(self, recv_diff, old=None):
- """ Places a new and compatible change of incremental type in its correct place, and
- actualizes thevalue of the shared object accordingly."""
- _logger.debug("insert_diff(): Current versionid:%s, recv_diff:%s", self._version_id, recv_diff)
- if not recv_diff.incremental:
- _logger.debug("insert_diff(): Applying non-incremental diff")
- if recv_diff in self._received_diffs:
- _logger.debug("Dupe")
- return True
- recvi = recv_diff.version_id
- index = self._get_diff_index(recvi)
- if old == None:
- old = self.get_version(recvi - 1)
- #if old == None: #can't find the father, doesn't use
- # self._discarded_diff = recv_diff
- # #TODO: signal that we have a discarded diff --> return False
- # return False
-
- _logger.debug("insert_diff(): old=%s", old)
- if index < 0:
- diffs = []
- else:
- diffs = self._received_diffs[index:]
- i = 0
- while len(diffs) > 0 and recv_diff.is_newer(diffs[0]):
- diffs = diffs[1:]
- index += 1
- i += 1
- recv_diff.version_id += i
- for d in diffs:
- d.version_id += 1
- res = [recv_diff] + diffs
-
- ret = old
- invdiffs = []
- for d in res:
- _logger.debug("[insert_diff] Applying %s to %s", d.obj, ret)
- ret, id = self._apply_diff_to(ret, d.obj)
- invdiffs.append(id)
- self._value = ret
- if index >= 0:
- self._received_diffs = self._received_diffs[:index] + res
- self._inverse_diffs = self._inverse_diffs[:index] + invdiffs
- _logger.info("insert_diff(): recv_diff inserted at %d", index)
- else:
- self._received_diffs = self._received_diffs + res
- self._inverse_diffs = self._inverse_diffs + invdiffs
- _logger.debug("insert_diff(): recv_diff inserted at %d", len(self._received_diffs))
- self._version_id += 1
- if not self._version_id == self._received_diffs[-1].version_id:
- _logger.debug("Version inconsistency: expected %s, got %s", self._received_diffs[-1], self._version_id)
- self._version_id = self._received_diffs[-1].version_id
- _logger.debug("new version_id: %s, actualized received_diffs: %s", self._version_id, self._received_diffs)
- return True
-
- def process_update_nc(self, versionid, incremental, objstr, sender, force=False):
- """Process an update:
- -Undo all newer diffs
- -Apply the just added one
- -Redo all newer diffs
- """
-
- obj = self.decode(objstr)
- d = DiffRec(versionid, sender, incremental, obj)
-
- i = self.insert_diff(d)
- _logger.debug('Inserted diff at position %d', i)
- if i == -1:
- return False
-
- j = len(self._received_diffs) - 1 # Don't include ourselve
- while j > i:
- if not self._received_diffs[j].incremental:
- break # not necessary to continue beyond replacement object
- self.apply_diff(self._inverse_diffs[j].obj)
- j -= 1
-
- while j < len(self._received_diffs):
- if not self._received_diffs[j].incremental:
- self._value = self._received_diffs[j].obj
- else:
- self._inverse_diffs[j].obj = self.apply_diff(self._received_diffs[j].obj)
- self._version_id = self._received_diffs[j].version_id
- j += 1
-
- self.do_changed_callback()
-
- return True
-
- def process_update(self, versionid, incremental, objstr, sender, force=False):
- """Process an update:
- -Undo all newer diffs to get to the common version
- - Check for compatability between the received update and the 'combined' newer diffs
- - If compatible, insert the received one
- - If not, apply the 'winning' one, and pass the other to the 'rejected' folder
- We return 'True' if there is no conflict, and 'False' if there was one
- """
- if (versionid == self._version_id):
- _logger.debug("Maybe dupe? our versionid:%d, received:%d", self._version_id, versionid)
- a = len(self._received_diffs) > 0
- b = (sender == self._received_diffs[-1].sender)
- _logger.debug("Two tests: %s, %s", a, b)
- if a and b:
- return True #Dupe
- obj = self.decode(objstr)
- _logger.debug( "process_update: version: %s, obj: %s" % (versionid, obj))
- if versionid > self._version_id + 1 and incremental and not force:
- #Disordered diffs. Store for later use
- self._pending_diffs[versionid] = DiffRec(versionid, sender, incremental, obj)
- for i in range(self._version_id+1, versionid):
- #TODO : Call for missing diffs:
- pass
- _logger.debug("Disordered diffs. returning")
- return True
- old = self.get_version(versionid - 1) #supposed common ancestor
- if not incremental: #we get the incremental version maually
- obj2 = self.diff(obj, old)
- else:
- obj2 = obj
- db = DiffRec(versionid, sender, True, obj2)
- if versionid > self._version_id or force: #expected version number
- if not incremental:
- _logger.debug("Update in expected range, non incremental")
- self._value = obj
- self._received_diffs.append(db)
- self._version_id = versionid
- else:
- _logger.debug("Update in expected range, incremental")
- self.insert_diff(db, old)
- while self._version_id + 1 in self._pending_diffs:
- #We get the pending diff and apply it
- pd = self._pending_diffs[self._version_id + 1]
- if not pd.incremental:
- self._value = pd.obj
- pd.obj = self.diff(pd.obj, self._value)
- pd.incremental = True
- else:
- self.insert_diff(pd, self._value)
- self._received_diffs.append(pd)
- self._version_id += 1
- del self._pending_diffs[self._version_id +1]
- _logger.debug("Updated value: %s", self._value)
- self.do_changed_callback()
- return True
- else:
- _logger.debug("Conflicting versionids: mine:%d, his:%d" %(self._version_id, versionid))
- obj_da = self.diff(self._value, old)
- if len(self._received_diffs)>0:
- sender_a = self._received_diffs[-1].sender
- else:
- sender_a = None
- da = DiffRec(self._version_id, sender_a, True, obj_da)
- _logger.debug("Verifying compatibility")
- if self._compatible_diffs(da.obj, db.obj):
- _logger.debug("compatible diff")
- if self.insert_diff(db, old):
- #insert does take care of version actualization and updates self._version_id
- _logger.debug("changed without error")
- self.do_changed_callback()
- return True
- else:
- _logger.debug("Error with insertion")
- return False
-
- def _compatible_diffs(self, da, db):
- return True
-
- def diff(self, new, old):
- return None
-
- def apply_diff_to(self, obj, diffobj):
- return (obj, diffobj)
-
- def apply_diff(self, diffobj):
- (newobj, idiff) = self.apply_diff_to(self._value, diffobj)
- self._value = newobj
- return idiff
-
- def is_locked(self):
- return self._locked
-
- def lock(self):
- if not self._locked:
- self._locked = True
- self._locked_by = "me"
- self._locked_time = time.time()
- self._helper.LockObject(self._name, self._locked_time)
-
- def receive_lock(self, sender, when):
- if not self._locked or \
- (self._locked and self._locked_time > when):
- if self._locked and 'locklost' in self._options:
- self._options['locklost']()
- self._locked = True
- self._locked_by = sender
- self._locked_time = when
- if 'locked' in self._options:
- self._options['locked'](sender)
-
- def unlock(self):
- if self._locked and self._locked_by is 'me':
- self._helper.UnlockObject(self._name)
-
- def receive_unlock(self, sender):
- if self._locked:
- self._locked = False
- self._locked_by = None
- self._locked_time = 0
- if 'unlocked' in self._options:
- self._options['unlocked'](sender)
diff --git a/sharedstate.git/sharedstate/sharedobjects.py b/sharedstate.git/sharedstate/sharedobjects.py
deleted file mode 100644
index 341005a..0000000
--- a/sharedstate.git/sharedstate/sharedobjects.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# sharedobjects.py, wrapper for importing SharedObject types
-# Reinier Heeres, reinier@heeres.eu
-#
-# 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
-#
-# Change log:
-# 2007-06-22: rwh, first version
-
-from sharedobject import SharedObject
-from sharedpython import SharedPython
-from shareddict import SharedDict
-from sharedtext import SharedText
-
diff --git a/sharedstate.git/sharedstate/sharedpython.py b/sharedstate.git/sharedstate/sharedpython.py
deleted file mode 100644
index 78a622a..0000000
--- a/sharedstate.git/sharedstate/sharedpython.py
+++ /dev/null
@@ -1,234 +0,0 @@
-# sharedpython.py, classes to aid activities in sharing a state
-# @author: Miguel Angel Alvarez, miguel@laptop.org
-# @author: Reinier Heeres
-#
-# 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
-#
-# Change log:
-#
-
-import pickle
-import difflib
-import logging
-from sharedobject import DiffRec
-_logger = logging.getLogger('sharedpython')
-
-from sharedobject import SharedObject
-
-class SharedPython(SharedObject):
-
- def __init__(self, name, helper, opt={}):
- SharedObject.__init__(self, name, helper, opt=opt)
- self._value = None
- self._picklestr = ''
-
- def _divide_change(self, c):
- """Separate the index and the string parts of the chage string"""
- if ' ' in c:
- i = c.index(' ')
- return (c[:i], c[i+1:])
- else:
- print c
- return None
-
- def inverse_diff(self, diff):
- """Return the inverse diff object"""
- d = SharedObject.inverse_diff(self, diff)
- if d == None:
- obj = self._generate_inverse_diffobj(diff.obj)
- return DiffRec(diff.version_id, diff.sender, True, obj)
-
- def _generate_inverse_diffobj(self, changes):
- ret = []
- delta = 0
- last_num = -1
- last_n_in = -1
-
- for c in changes:
- n, s = self._divide_change(c)
- l = len(s)
- num = abs(int(n))
- if num == last_num:
- n_in = last_n_in
- else:
- n_in = num + delta
- last_num = num
- last_n_in = n_in
-
- if c[0] == '+':
- ret.append('-'+str(n_in)+' '+s)
- delta += l
- elif c[0] == '-':
- ret.append('+'+str(n_in)+' '+s)
- delta -= l
- else:
- print "Unknown line type:", c
- ret = self._format_changes(ret)
- return ret
-
- def _update_interval(self, i, cs):
- """Get an 'exclusion interval' (where no other different edits are accepted) for the change cs[i]"""
- c = cs[i]
- n, d = self._divide_change(c)
- n = int(n)
- if n >= 0:
- return (n+1, n+1)
- else:
- return (abs(n), abs(n) + len(d))
-
- def _intersect(self, ia, ib):
- """function that takes 2 intervals (2 element ordered int lists) and returns whether they
- intersect and, if not, which one is bigger:
- ret > 0 => ia bigger
- ret < 0 => ib bigger
- ret = 0 => intersection"""
- assert ia[1] >= ia[0] and len(ia) == 2
- assert ib[1] >= ib[0] and len(ib) == 2
- if ia[0] > ib[1]:
- return 1 #No _intersect, ia bigger
- if ia[1] < ib[0]:
- return -1 #No _intersect, ia smaller
- return 0 #Intersect
-
- def _compatible_diffs(self, diff_a, diff_b):
- """ It returns whether two change arrays act upon the same positions, and
- cannot therefore be automatically merged without risking conflict"""
- _logger.debug("_compatible_diffs(): a=%s, b=%s", diff_a, diff_b)
- index_a = index_b = 0
- while index_a < len(diff_a) and index_b < len(diff_b):
- interval_a = self._update_interval(index_a, diff_a)
- interval_b = self._update_interval(index_b, diff_b)
- d = self._intersect(interval_a, interval_b)
- if d == 0 :
-## if diff_a[index_a] not in diff_b and diff_b[index_b] not in diff_a:
-## print "change a:'%s'\tchange_b:'%s'" % (diff_a[index_a], diff_b[index_b])
-## return False
-## elif interval_a[1] > interval_b[1]:
-## index_b += 1
-## else:
-## index_a += 1
-## ATT: More restrictive version of compatible_diffs used right now
- return False
- elif d == 1:
- index_b += 1
- elif d == -1:
- index_a += 1
- else:
- index_a += 1
- index_b += 1
- return True
-
- def _format_changes(self, changes):
- last_sign = None
- last_num = -1
- foreseen_index = -1
- res=[]
- for c in changes:
- sign = c[0]
- n, s = self._divide_change(c)
- number = abs(int(n))
- if sign == "+" and last_sign == "-" and (number == foreseen_index or number == last_num) :
- res = res[:-1]+[sign +str(last_num)+" "+s, res[-1]]
- last_sign = sign
- foreseen_index = number + len(s)
- last_num = number
- else:
- res.append(c)
- last_sign = sign
- foreseen_index = number + len(s)
- last_num = number
- return res
-
- def diff(self, new_object, old_object):
- """Generate a change array from two python objects"""
- _logger.debug("Diffing old:%s (type: %s), new: %s (type: %s)", old_object, type(old_object),
- new_object, type(new_object))
- differ = difflib.Differ()
- old = pickle.dumps(old_object)
- new = pickle.dumps(new_object)
-## _logger.debug('Old text: %s', old)
-## _logger.debug(' New text:%s', new)
- ret = []
- raw_delta = list(differ.compare(old, new))
-## _logger.debug('raw delta: %s', raw_delta)
- pos = 0
- continuous = False
- for r in raw_delta:
- if r[:2] == "+ ":
- if len(ret) > 0 and ret[-1][0] == "+" and continuous:
- ret[-1] += r[2:]
- #if 2 continuous additions occur, append at the end
- else:
- string = "+"+str(pos)+" "+r[2:]
- ret.append(string)
- continuous = True
- elif r[:2] == "- ":
- if len(ret) > 0 and ret[-1][0] == "-" and continuous:
- ret[-1] += r[2:] #append at the end
- else:
- string = "-"+str(pos)+" "+r[2:]
- ret.append(string)
- continuous = True
- pos += 1 # TODO: important change; verify
- elif r[:2] == "? ":
- pass
- else:
- continuous = False
- pos += len(r) - 2
-## _logger.debug('Ret before format changes: %s', ret)
- return self._format_changes(ret)
-
- def _apply_diff_to(self, object, diffobj):
- """ Apply a diff to a given object and return the new version
- In this case, the provided object gets modified, too."""
- old = pickle.dumps(object)
- _logger.debug("_apply_diff_to(): old=%r, diffobj=%s", old, diffobj)
- new = ""
- pos = old_pos = 0
- for c in diffobj:
- id, st = self._divide_change(c)
- pos = abs(int(id))
- new += old[old_pos:pos]
- if id[0] == "+":
-## _logger.debug("_apply_diff_to(): adding %r", st)
- new += st
- elif id[0] == "-":
-## _logger.debug("_apply_diff_to(): deleting %r", st)
- if old[pos:pos+len(st)] != st:
- exc = "Bad delete at %d, expected %s, got %r" % (pos,
- st, old[pos:pos+len(st)])
- raise Exception(exc)
- pos += len(st)
- old_pos = pos
- if pos < len(old):
- new += old[pos:]
- res = pickle.loads(new)
- _logger.debug("_apply_diff_to(): new=%r, res=%s", new, res)
- idiff = self._generate_inverse_diffobj(diffobj)
- return (res, idiff)
-
- def get_version(self, versionid):
- _logger.debug("get_version(): called with versionid = %d", versionid)
- if versionid == self._version_id:
- return self._value
- ret = self._value
- if versionid > self._version_id:
- return None
- for i in range(len(self._received_diffs)-1, self._get_diff_index(versionid), -1):
- d = self._received_diffs[i]
- idobj = self._generate_inverse_diffobj(d.obj)
- ret = self._apply_diff_to(ret, idobj)[0]
- _logger.debug("get_version(): return value: %s", ret)
- return ret
diff --git a/sharedstate.git/sharedstate/sharedstate.py b/sharedstate.git/sharedstate/sharedstate.py
deleted file mode 100644
index c69ace0..0000000
--- a/sharedstate.git/sharedstate/sharedstate.py
+++ /dev/null
@@ -1,532 +0,0 @@
-# sharedstate.py, classes to aid activities in sharing a state
-# Reinier Heeres, reinier@heeres.eu
-#
-# 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
-#
-# Change log:
-# 2007-05-22: rwh, first version
-
-"""General imports"""
-import types
-import copy
-
-import logging
-_logger = logging.getLogger('sharinghelper')
-
-"""DBus imorts"""
-import dbus
-import dbus.service
-import dbus.glib
-import gobject
-
-"""Sugar imports"""
-from sugar.presence import presenceservice
-from sugar.presence import activity
-from sugar.activity.activity import Activity
-
-"""Telepathy imports"""
-import telepathy
-import telepathy.client
-from tubeconn import TubeConnection
-
-from sharedobjects import *
-
-IFACE = "org.laptop.SharingHelper"
-
-class SharingHelper(dbus.service.Object):
- """Class to help activities with state sharing"""
-
- def __init__(self, actparent, opt={}):
- self._activity = actparent
- self._options = opt
- self._shared_types = {}
- self._shared_objects = {}
- self._buddy_list = {}
- self._service_name = ''
- self._object_path = ''
- self._tube = None
- self._own_bus_name = None
-
- self.register_shared_type('int', SharedPython, inc=False, autotype=int)
- self.register_shared_type('long', SharedPython, inc=False, autotype=long)
- self.register_shared_type('float', SharedPython, inc=False, autotype=float)
- self.register_shared_type('string', SharedText, inc=True, autotype=str)
- self.register_shared_type('ustring', SharedText, inc=True, autotype=unicode)
- self.register_shared_type('dict', SharedDict, inc=True, autotype=dict)
- self.register_shared_type('python', SharedPython, inc=True)
-
- self._tp_support = TubePresenceSupport(self)
- self._tp_support.connect_to_ps()
-
- return
-
- def __getitem__(self, key):
- if type(key) != types.StringType:
- raise TypeError, "SharingHelper.__getitem()__ only accepts string keys"
-
- if isinstance(self._shared_objects[key], SharedDict):
- return self._shared_objects[key]
- else:
- return self._shared_objects[key].get_value()
-
- def __setitem__(self, key, val):
- if type(key) != types.StringType:
- raise TypeError, "SharingHelper.__setitem()__ only accepts string keys"
- self._shared_objects[key].set_value(val)
-
- def get_object(self, key):
- if type(key) != types.StringType:
- raise TypeError, "SharingHelper.get_object() only accepts string keys"
- self._shared_objects[key]
-
- def _tube_created_cb(self, tube, reqsync):
- self._tube = tube
- self._object_path = '/org/laptop/SharingHelper/%s' % (self._activity._shared_activity._id)
-
- if self._tube is None:
- _logger.error('setup_shared_tube(): no tube connection yet!')
- return False
-
- dbus.service.Object.__init__(self, self._tube, self._object_path)
-
- _logger.info('Connected to bus as %s, object path %s', self._service_name, self._object_path)
- self._activity._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
- self._activity._shared_activity.connect('buddy-left', self._buddy_left_cb)
-
- self._tube.add_signal_receiver(self._receive_object, 'SendObject', \
- IFACE, path=self._object_path, sender_keyword='sender')
- self._tube.add_signal_receiver(self._receive_new_object, 'SendNewObject', \
- IFACE, path=self._object_path, sender_keyword='sender')
- self._tube.add_signal_receiver(self._receive_sync_request, 'RequestSync', \
- IFACE, path=self._object_path, sender_keyword='sender')
- self._tube.add_signal_receiver(self._receive_message, 'SendMessage', \
- IFACE, path=self._object_path, sender_keyword='sender')
-
- _logger.info('Tube setup successful!')
-
- if reqsync:
- self.synchronize()
- if 'on_connect' in self._options:
- self._options['on_connect']()
-
- return True
-
- def _buddy_joined_cb(self, activity, buddy):
- """Callback for buddy joining"""
- _logger.info('Buddy %s joined', buddy._properties["nick"])
-
- key = buddy._properties["key"]
- if key not in self._buddy_list:
- self._buddy_list[key] = buddy
-
- def _buddy_left_cb(self, activity, buddy):
- """Callback for buddy leaving"""
- _logger.info('Buddy %s left', buddy)
-
- key = buddy._properties["key"]
- if buddy in self._buddy_list:
- del self._buddy_list[key]
-
- def get_buddy_list(self):
- return self._buddy_list
-
- def tube_connected(self):
- if self._tube is None:
- return False
-
- return True
-
-##########################################
-# Shared object collection managing functions
-##########################################
-
- def register_shared_type(self, name, oclass, inc=1, autotype=None):
- self._shared_types[name] = (oclass, inc, autotype)
-
- def add_shared_object(self, o):
- """Add shared object to list of objects to process"""
- self._shared_objects[o._name] = o
- return True
-
- def create_shared_object(self, name, opt, iv=None):
- """Create a new shared object"""
-
-# Auto-detect object type
- if 'type' not in opt:
- for key, (oclass, inc, autotype) in self._shared_types.iteritems():
- if autotype is not None and isinstance(iv, autotype):
- opt['type'] = key
- opt['incremental'] = inc
-
- _logger.debug("Shared object %s of type %s requested", name, opt['type'])
-
- if opt['type'] not in self._shared_types:
- _logger.error("Shared object type %s unknown", opt['type'])
- return None
-
- (oclass, inc, autotype) = self._shared_types[opt['type']]
- obj = oclass(name, opt=opt, helper=self)
-
- if obj is None:
- return None
-
- self.add_shared_object(obj)
-
-# Tell other instances about dynamically created objects
- if 'dynamic' in opt and opt['dynamic'] is True:
- self.SendNewObject(name, opt)
-
- if iv is not None:
- obj.set_value(iv)
-
- return obj
-
-##########################################
-# Sending and receiving objects
-##########################################
-
- def is_remote_sender(self, sender):
- if self._own_bus_name is None:
- self._own_bus_name = self._tube.get_unique_name()
- _logger.info('Acquired unique name: %s', self._own_bus_name)
-
- if sender == self._own_bus_name:
- return False
- else:
- return True
-
- @dbus.service.signal(dbus_interface=IFACE, signature='subs')
- def SendObject(self, name, versionid, incremental, objstr):
- """Signal proxy to send an object"""
- _logger.info('Sending object %s v%d on bus (inc=%d)', name, versionid, incremental)
-
- def _receive_object(self, name, versionid, incremental, objstr, sender=None):
- """Response to SendObject() signal; updates the state of the shared object.
- If an update is requested for an object that does not exist yet we
- must be in a confused state, so ask for a sync.
- """
- if not self.is_remote_sender(sender):
- return True
-
- _logger.info('receive_object(): Received object: %s v%d (inc=%d) from %s, %s', \
- name, versionid, incremental, sender, objstr)
- if name in self._shared_objects:
- self._shared_objects[name].process_update(versionid, incremental, objstr, sender)
- else:
- _logger.error('receive_object(): Unknown object %s; requesting sync', name)
- self.synchronize()
-
- @dbus.service.signal(dbus_interface=IFACE, signature='sa{sv}')
- def SendNewObject(self, name, opts):
- """Signal proxy to create a new object"""
- _logger.info('Sending new object %s on bus', name)
-
- def _receive_new_object(self, name, opts, sender=None):
- """Response to SendNewObject() signal; creates a new shared object.
- If creation is requested for an object that exists already, leave
- it untouched.
- If the 'objectcreated' option is set call that function
- """
- if not self.is_remote_sender(sender):
- return True
-
- _logger.info('receive_new_object(): Received new object: %s from %s', name, sender)
- if name in self._shared_objects:
- _logger.error('receive_new_object(): object already exists; leaving untouched')
- else:
- obj = self.create_shared_object(name, opts)
- if 'objectcreated' in self._options:
- self._options['objectcreated'](obj)
-
- def synchronize(self):
- self._in_sync = False
- #self.RequestSync()
- #gobject.timeout_add(1000, self._verify_sync)
-
- def _verify_sync(self):
- if not self._in_sync:
- self.RequestSync()
- return True
- return False
-
- @dbus.service.signal(dbus_interface=IFACE, signature='')
- def RequestSync(self):
- """Signal proxy to request synchronization"""
- _logger.info('Sending synchronization request...')
-
- def _receive_sync_request(self, sender=None):
- """Called when somebody sends a SyncRequest."""
- if not self.is_remote_sender(sender):
- return True
-
- _logger.info('Received sync request from %s, sending objects', sender)
- for name, obj in self._shared_objects.iteritems():
-# Sending options is only necessary for dynamically created objects; implement this later
-# sendopt = copy.deepcopy(obj.Options)
-# if 'changed' in sendopt:
-# del sendopt['changed']
- self._tube.get_object(sender, self._object_path).ReceiveSyncObject( \
- name, {'empty': True}, obj._version_id, obj.encode(obj._value))
-
- @dbus.service.method(dbus_interface=IFACE, in_signature='sa{sv}us', out_signature='', sender_keyword='sender')
- def ReceiveSyncObject(self, name, opt, versionid, objstr, sender=None):
- """Function to receive full synchronisation. Used when joining an
- existing activity or when in a confused state.
- If an object does not exists yet it is created; the version is forced
- """
- _logger.debug('Receiving sync object %s from %s', name, sender)
-
- if not self.is_remote_sender(sender):
- return True
-
- self._in_sync = True
-
- if name not in self._shared_objects:
- obj = self.create_shared_object(name, opts)
-
- self._shared_objects[name].process_update(versionid, False, objstr, sender, force=True)
-
- @dbus.service.signal(dbus_interface=IFACE, signature='sd')
- def LockObject(self, name, when):
- """Signal proxy to request object lock"""
- _logger.debug('Sending lock signal for %s', name)
-
- def _receive_lock_object(self, name, when, sender=None):
- """Called when somebody tries to lock an object"""
- if name in self._shared_objects:
- self._shared_objects[name].receive_lock(sender, when)
- else:
- _logger.error('Received lock signal for non-existing object %s', name)
-
- @dbus.service.signal(dbus_interface=IFACE, signature='s')
- def UnlockObject(self, name):
- """Signal proxy to signal release of an object lock"""
- _logger.debug('Sending unlock signal for %s', name)
-
- def _receive_unlock_object(self, name, sender=None):
- """Called when somebody signals unlocking of an object"""
- if name in self._shared_objects:
- self._shared_objects[name].receive_unlock()
- else:
- _logger.error('Received unlock signal for non-existing object %s', name)
-
-##########################################
-# Simple message sending between apps
-##########################################
-
- @dbus.service.signal(dbus_interface=IFACE, signature='sv')
- def SendMessage(self, msg, val):
- """Signal proxy to send simple messages"""
-
- def send_message(self, msg, val, to=None):
- _logger.debug('send_message(msg=%s, val=%r)', msg, val)
- try:
- if to is None:
- self.SendMessage(msg, val)
- except Exception, inst:
- _logger.error('send_message: %s', inst)
-
- def _receive_message(self, msg, val, sender=None):
- if not self.is_remote_sender(sender):
- return True
-
- _logger.info('_receive_message(%s, %r)', msg, val)
-
- if 'receive_message' in self._options:
- self._options['receive_message'](msg, val)
-
-##########################################
-# Functions for turn-based activities
-##########################################
-
-class TurnBased:
- def __init__(self, helper):
- self._helper = helper
- self._helper.create_shared_object('_turntoken', {
- 'locked': self.my_turn,
- 'unlocked': self.turn_changed,
- 'locklost': self.turn_problem,
- })
- self._playing = False
- self._playing_buddies = []
- self._watching_buddies = []
-
- def set_number_of_players(self, minp, maxp=None):
- self._started = False
- self._min_players = minp
- self._max_players = maxp
- self.check_ready()
-
- def set_turn_callbacks(self, d):
- """Set callbacks for turn-based functions:
- ready: called when enough players to start
- myturn: called when it's this instance's turn
- """
- self._turn_callbacks = d
-
- def check_ready(self):
- if len(self._helper.get_buddy_list()) >= self._min_players:
- self._turn_callbacks['ready']()
- self._started = True
-
- def start(self):
- self._playing = True
- self._playing_buddies = self._helper.get_buddy_list()
- self._current_player = -1
- self.turn_changed(self, None)
-
- def who_is_next_player(self):
- self._current_player = (self._current_player + 1) % len(self._playing_buddies)
- return self._playing_buddies[self._current_player]
-
- def turn_changed(self, sender):
- if sender is not None and 'processturn' in self._turn_callbacks:
- self._turn_callbacks['processturn'](sender, self.get_turn_data())
- if self.who_is_next_player() == self._helper._own_dbus_name:
- self._helper.get_object('_turntoken').lock()
- if 'myturn' in self._turn_callbacks:
- self._turn_callbacks['myturn']()
-
- def release_turn(self, turndata):
- self._helper['_turndata'] = turndata
- self._helper.get_object('_turntoken').unlock()
-
- return
-
- def turn_problem(self, sender):
- return
-
-
-
-##########################################
-# Functions for Tube/Presence support
-##########################################
-
-class TubePresenceSupport:
-
- def __init__(self, parent):
- self._parent = parent
- self._activity = parent._activity
-
- self._request_sync = False
-
- return
-
- def connect_to_ps(self):
- """Connect to the presence service"""
-
- self._activity.connect('shared', self._shared_cb)
-
- self._ps = presenceservice.get_instance()
- if self._activity._shared_activity:
- # We are trying to join, call this if it worked
- self._activity.connect('joined', self._joined_cb)
- if self._activity.get_shared():
- self.joined_cb()
-
- def setup_shared_activity(self):
- """Setup things to talk to other SharingHelpers """
-
- if self._activity._shared_activity is None:
- _logger.error('setup_shared_activity(): no _shared_activity yey!')
- return False
-
- self._service_name = 'org.laptop.SharingHelper'
-
-# Get basic telepathy stuff
- name, path = self._ps.get_preferred_connection()
- _logger.info('Preferred connection: name %s, path %s', name, path)
- self._tp_conn_name = name
- self._tp_conn_path = path
- self._tp_conn = telepathy.client.Connection(name, path)
-
-# Setup tubes channel
- self._tube_service_name = '%s.Tube' % (self._service_name)
- bus_name, conn_path, channel_paths = self._activity._shared_activity.get_channels()
- room = None
- self._text_channel = None
- self._tube_channel = 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.info('Found room with handle %d', handle)
- room = handle
- ctype = channel.GetChannelType()
- if ctype == telepathy.CHANNEL_TYPE_TUBES:
- _logger.info('Found Tubes channel %s', channel_path)
- self._tube_channel = channel
- elif ctype == telepathy.CHANNEL_TYPE_TEXT:
- _logger.info('Found Text channel %s', channel_path)
- self._text_channel = channel
-
- if room is None:
- _logger.error('Didn\'t find room')
-
- if self._tube_channel is None:
- self._tube_channel = self._tp_conn.request_channel( \
- telepathy.CHANNEL_TYPE_TUBES, \
- telepathy.HANDLE_TYPE_ROOM, room, True)
-
- self._tube_channel[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube', self._new_tube_cb)
-
- def _shared_cb(self, activity):
- """Callback for when our activity is shared"""
- _logger.info('Activity shared')
- self.setup_shared_activity()
- self._tube_channel[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( \
- self._tube_service_name, {})
-
- def _joined_cb(self, activity):
- """Callback for when we join an existing activity"""
- _logger.info('Joined existing activity')
- self._request_sync = True
- self.setup_shared_activity()
-
- _logger.info('Getting tubes list...')
- self._tube_channel[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):
- """Callback for when a new tube is created"""
- _logger.info('new_tube_cb(): id=%d, init=%d, type=%d, svc=%s, state=%d, _request_sync=%r', id, initiator, type, service, state, self._request_sync)
- _logger.info('Expected: type=%d, svc=%s, state=%d', telepathy.TUBE_TYPE_DBUS, self._tube_service_name, telepathy.TUBE_STATE_LOCAL_PENDING)
- if type == telepathy.TUBE_TYPE_DBUS and service == self._tube_service_name:
- if state == telepathy.TUBE_STATE_LOCAL_PENDING:
- self._tube_channel[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
- self._tube = TubeConnection(self._tp_conn, \
- self._tube_channel[telepathy.CHANNEL_TYPE_TUBES], id, \
- group_iface=self._text_channel[telepathy.CHANNEL_INTERFACE_GROUP])
-
- if self._tube is None:
- _logger.error('Don\'t have a tube channel, not connecting')
- return False
-
- self._parent._tube_created_cb(self._tube, self._request_sync)
-
- def _list_tubes_reply_cb(self, tubes):
- """Callback for when requesting an existing tube"""
- _logger.debug('_list_tubes_reply_cb(): %r', tubes)
- for tube_info in tubes:
- _logger.debug('tube_info: %r', tube_info)
- self._new_tube_cb(*tube_info)
-
- def _list_tubes_error_cb(self, e):
- _logger.error('list_tubes() failed: %s', e)
-
- def _tube_participant_change_cb(self, added, removed):
- _logger.info('Adding participants: %r', added)
- _logger.info('Removing participants: %r', removed)
diff --git a/sharedstate.git/sharedstate/sharedtext.py b/sharedstate.git/sharedstate/sharedtext.py
deleted file mode 100644
index 384f0a8..0000000
--- a/sharedstate.git/sharedstate/sharedtext.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# sharedobjects.py, classes to aid activities in sharing a state
-# Reinier Heeres, reinier@heeres.eu
-#
-# 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
-#
-# Change log:
-# 2007-06-21: rwh, first version
-
-import types
-import difflib
-import logging
-_logger = logging.getLogger('sharinghelper')
-
-from sharedobject import SharedObject
-
-def my_joinlines(list, addsep=False, separator='\n'):
- if len(list) == 0:
- return ''
- string = list[0]
- for s in list[1:]:
- if addsep:
- string += separator
- string += s
- return string
-
-def my_splitlines(str, keepsep=False, separators=['\n']):
- list = []
- if str is None:
- return list
- ofs = 0
- startofs = 0
- while ofs < len(str):
- if str[ofs] in separators:
- if keepsep:
- list.append(str[startofs:ofs+1])
- else:
- if startofs != ofs:
- list.append(str[startofs:ofs])
- else:
- list.append('')
- startofs = ofs + 1
- ofs += 1
- return list
-
-def string_to_list(str):
- list = []
- if str is not None:
- for i in str:
- list.append(i)
- return list
-
-def list_to_string(l):
- str = ""
- for i in l:
- str += i
- return str
-
-class SharedText(SharedObject):
- """Shared text object, generates line-by-line difference objects"""
-
- REPLACE = 0
- DELETE = 1
- INSERT = 2
-
- BY_CHAR = 0
- BY_WORD = 1
- BY_LINE = 2
-
- def __init__(self, name, helper, opt={}, by_what=BY_CHAR):
- SharedObject.__init__(self, name, helper, opt=opt)
- self._value = ''
- self._by_what = by_what
-
- def split(self, s):
- if self._by_what == SharedText.BY_CHAR:
- return string_to_list(s)
- elif self._by_what == SharedText.BY_WORD:
- return my_splitlines(s, keepsep=True, separators=['\n', ' '])
- elif self._by_what == SharedText.BY_LINE:
- return my_splitlines(s, keepsep=True)
- else:
- _logger.error('SharedText.split(): unknown splitting type')
-
- def join(self, l):
- if self._by_what == SharedText.BY_CHAR:
- return list_to_string(l)
- elif self._by_what == SharedText.BY_WORD:
- return my_joinlines(l)
- elif self._by_what == SharedText.BY_LINE:
- return my_joinlines(l)
- else:
- _logger.error('SharedText.split(): unknown splitting type')
-
- def _compatible_diffs(self, da, db):
- return True
-
- def diff(self, cur, old):
- """Generate a difference object between to text objects"""
-
- _logger.debug('Generating diff between %r and %r', cur, old)
-
- l1 = self.split(cur)
- l2 = self.split(old)
- sm = difflib.SequenceMatcher(None, l2, l1)
- ret = []
- for (tag, i1, i2, j1, j2) in sm.get_opcodes():
- if tag is 'replace':
- ret.append((SharedText.REPLACE, (i1, i2, j1, j2), l1[j1:j2]))
- elif tag is 'delete':
- ret.append((SharedText.DELETE, (i1, i2, j1, j2), None))
- elif tag is 'insert':
- ret.append((SharedText.INSERT, (i1, i2, j1, j2), l1[j1:j2]))
- elif tag is 'equal':
- pass
- else:
- _logger.warning('SharedText.diff(): unkown tag: %s', tag)
-
- if len(ret) is 0:
- return None
- else:
- return ret
-
- def _apply_diff_to(self, obj, diffobj):
- """Apply a diff and return an object that describes the inverse diff"""
-
- if diffobj is None:
- _logger.error('SharedText.apply_diff_to(): no diffobj given')
- return (None, None)
-
- _logger.debug('Applying %r to %r', diffobj, obj)
- ret = []
- d = 0
- l2 = self.split(obj)
- for (tag, (i1, i2, j1, j2), val) in diffobj:
- i1 -= d
- i2 -= d
-# print 'd: %r' % (d)
- if tag is SharedText.REPLACE:
- ret.append((SharedText.REPLACE, (j1, j2, i1, i2), l2[i1:i2]))
- l2[i1:i2] = val
- d -= (j2 - j1) - (i2 - i1)
- elif tag is SharedText.DELETE:
- ret.append((SharedText.INSERT, (j1, j2, i1, i2), l2[i1:i2]))
- del(l2[i1:i2])
- d += i2 - i1
- elif tag is SharedText.INSERT:
- ret.append((SharedText.DELETE, (j1, j2, i1, i2), None))
- l2[i1:i1] = val
- d -= j2 - j1
-
- obj = self.join(l2)
- return (obj, ret)
-
-
- def insert(self, ofs, str):
- self._value = self._value[:ofs] + str + self._value[ofs:]
- dobj = {"type": 'chars', "data": (SharedText.INSERT, ofs, str)}
- self.changed(dobj, True)
-
- def remove(self, ofs, len):
- del self._value[ofs:ofs+len]
- dobj = {"type": 'chars', "data": (SharedText.REMOVE, ofs, len)}
- self.changed(dobj, True)
-
- def replace(self, ofs, str):
- self._value[ofs:ofs+len(str)] = str
- dobj = {"type": 'chars', "data": (SharedText.REPLACE, ofs, str)}
- self.changed(dobj, True)
-
diff --git a/sharedstate.git/sharedstate/tubeconn.py b/sharedstate.git/sharedstate/tubeconn.py
deleted file mode 100644
index b487391..0000000
--- a/sharedstate.git/sharedstate/tubeconn.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# 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.GetDBusTubeAddress(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, [])