From b9f9ef0fe9e36cf6e5de59700154b16f2dae15cd Mon Sep 17 00:00:00 2001 From: Justin Gallardo Date: Mon, 04 Dec 2006 19:12:24 +0000 Subject: Changed all tabs to 4 spaces for python style --- (limited to 'sugar') diff --git a/sugar/TracebackUtils.py b/sugar/TracebackUtils.py index 86e28f9..940381e 100644 --- a/sugar/TracebackUtils.py +++ b/sugar/TracebackUtils.py @@ -22,33 +22,33 @@ import signal haveThreadframe = True try: - import threadframe + import threadframe except ImportError: - haveThreadframe = False + haveThreadframe = False class TracebackHelper(object): - def __init__(self): - fname = "%s-%d" % (os.path.basename(sys.argv[0]), os.getpid()) - self._fpath = os.path.join("/tmp", fname) - print "Tracebacks will be written to %s on SIGUSR1" % self._fpath - signal.signal(signal.SIGUSR1, self._handler) + def __init__(self): + fname = "%s-%d" % (os.path.basename(sys.argv[0]), os.getpid()) + self._fpath = os.path.join("/tmp", fname) + print "Tracebacks will be written to %s on SIGUSR1" % self._fpath + signal.signal(signal.SIGUSR1, self._handler) - def __del__(self): - try: - os.remove(self._fpath) - except OSError: - pass + def __del__(self): + try: + os.remove(self._fpath) + except OSError: + pass - def _handler(self, signum, pframe): - f = open(self._fpath, "a") - if not haveThreadframe: - f.write("Threadframe not installed. No traceback available.\n") - else: - frames = threadframe.dict() - for thread_id, frame in frames.iteritems(): - f.write(('-' * 79) + '\n') - f.write('[Thread %s] %d' % (thread_id, sys.getrefcount(frame)) + '\n') - traceback.print_stack(frame, limit=None, file=f) - f.write("\n") - f.write('\n') - f.close() + def _handler(self, signum, pframe): + f = open(self._fpath, "a") + if not haveThreadframe: + f.write("Threadframe not installed. No traceback available.\n") + else: + frames = threadframe.dict() + for thread_id, frame in frames.iteritems(): + f.write(('-' * 79) + '\n') + f.write('[Thread %s] %d' % (thread_id, sys.getrefcount(frame)) + '\n') + traceback.print_stack(frame, limit=None, file=f) + f.write("\n") + f.write('\n') + f.close() diff --git a/sugar/activity/Activity.py b/sugar/activity/Activity.py index 2f27303..215ee85 100644 --- a/sugar/activity/Activity.py +++ b/sugar/activity/Activity.py @@ -32,133 +32,133 @@ ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" ACTIVITY_INTERFACE = "org.laptop.Activity" def get_service_name(xid): - return ACTIVITY_SERVICE_NAME + '%d' % xid + return ACTIVITY_SERVICE_NAME + '%d' % xid def get_object_path(xid): - return ACTIVITY_SERVICE_PATH + "/%s" % xid + return ACTIVITY_SERVICE_PATH + "/%s" % xid class ActivityDbusService(dbus.service.Object): - """Base dbus service object that each Activity uses to export dbus methods. - - The dbus service is separate from the actual Activity object so that we can - tightly control what stuff passes through the dbus python bindings.""" - - def start(self, pservice, activity): - self._activity = activity - self._pservice = pservice - - @dbus.service.method(ACTIVITY_INTERFACE) - def share(self): - """Called by the shell to request the activity to share itself on the network.""" - self._activity.share() - - @dbus.service.method(ACTIVITY_INTERFACE) - def join(self, activity_ps_path): - """Join the activity specified by its presence service path""" - activity_ps = self._pservice.get(activity_ps_path) - return self._activity.join(activity_ps) - - @dbus.service.method(ACTIVITY_INTERFACE) - def get_id(self): - """Get the activity identifier""" - return self._activity.get_id() - - @dbus.service.method(ACTIVITY_INTERFACE) - def get_type(self): - """Get the activity type""" - return self._activity.get_type() - - @dbus.service.method(ACTIVITY_INTERFACE) - def get_shared(self): - """Returns True if the activity is shared on the mesh.""" - return self._activity.get_shared() - - @dbus.service.method(ACTIVITY_INTERFACE, - in_signature="sas", out_signature="") - def execute(self, command, args): - self._activity.execute(command, args) + """Base dbus service object that each Activity uses to export dbus methods. + + The dbus service is separate from the actual Activity object so that we can + tightly control what stuff passes through the dbus python bindings.""" + + def start(self, pservice, activity): + self._activity = activity + self._pservice = pservice + + @dbus.service.method(ACTIVITY_INTERFACE) + def share(self): + """Called by the shell to request the activity to share itself on the network.""" + self._activity.share() + + @dbus.service.method(ACTIVITY_INTERFACE) + def join(self, activity_ps_path): + """Join the activity specified by its presence service path""" + activity_ps = self._pservice.get(activity_ps_path) + return self._activity.join(activity_ps) + + @dbus.service.method(ACTIVITY_INTERFACE) + def get_id(self): + """Get the activity identifier""" + return self._activity.get_id() + + @dbus.service.method(ACTIVITY_INTERFACE) + def get_type(self): + """Get the activity type""" + return self._activity.get_type() + + @dbus.service.method(ACTIVITY_INTERFACE) + def get_shared(self): + """Returns True if the activity is shared on the mesh.""" + return self._activity.get_shared() + + @dbus.service.method(ACTIVITY_INTERFACE, + in_signature="sas", out_signature="") + def execute(self, command, args): + self._activity.execute(command, args) class Activity(gtk.Window): - """Base Activity class that all other Activities derive from.""" - - def __init__(self): - gtk.Window.__init__(self) - - self.connect('destroy', self.__destroy_cb) - - self._shared = False - self._activity_id = None - self._default_type = None - self._service = None - self._pservice = PresenceService() - - self.present() - - group = gtk.Window() - group.realize() - self.window.set_group(group.window) - - bus = dbus.SessionBus() - xid = self.window.xid - - bus_name = dbus.service.BusName(get_service_name(xid), bus=bus) - self._bus = ActivityDbusService(bus_name, get_object_path(xid)) - self._bus.start(self._pservice, self) - - def set_type(self, activity_type): - """Sets the activity type.""" - self._activity_type = activity_type - self._default_type = activity.get_default_type(activity_type) - - def get_type(self): - """Gets the activity type.""" - return self._activity_type - - def get_default_type(self): - return self._default_type - - def get_shared(self): - """Returns TRUE if the activity is shared on the mesh.""" - return self._shared - - def get_id(self): - """Get the unique activity identifier.""" - if self._activity_id == None: - self._activity_id = sugar.util.unique_id() - return self._activity_id - - def join(self, activity_ps): - """Join an activity shared on the network.""" - self._shared = True - self._activity_id = activity_ps.get_id() - - # Publish the default service, it's a copy of - # one of those we found on the network. - services = activity_ps.get_services_of_type(self._default_type) - if len(services) > 0: - service = services[0] - addr = service.get_address() - port = service.get_port() - properties = service.get_published_values() - self._service = self._pservice.share_activity( - self, self._default_type, properties, addr, port) - else: - logging.error('Cannot join the activity') - - def share(self): - """Share the activity on the network.""" - logging.debug('Share activity %s on the network.' % self.get_id()) - - self._service = self._pservice.share_activity(self, self._default_type) - self._shared = True - - def execute(self, command, args): - """Execute the given command with args""" - pass - - def __destroy_cb(self, window): - if self._bus: - del self._bus - self._bus = None - if self._service: - self._pservice.unregister_service(self._service) + """Base Activity class that all other Activities derive from.""" + + def __init__(self): + gtk.Window.__init__(self) + + self.connect('destroy', self.__destroy_cb) + + self._shared = False + self._activity_id = None + self._default_type = None + self._service = None + self._pservice = PresenceService() + + self.present() + + group = gtk.Window() + group.realize() + self.window.set_group(group.window) + + bus = dbus.SessionBus() + xid = self.window.xid + + bus_name = dbus.service.BusName(get_service_name(xid), bus=bus) + self._bus = ActivityDbusService(bus_name, get_object_path(xid)) + self._bus.start(self._pservice, self) + + def set_type(self, activity_type): + """Sets the activity type.""" + self._activity_type = activity_type + self._default_type = activity.get_default_type(activity_type) + + def get_type(self): + """Gets the activity type.""" + return self._activity_type + + def get_default_type(self): + return self._default_type + + def get_shared(self): + """Returns TRUE if the activity is shared on the mesh.""" + return self._shared + + def get_id(self): + """Get the unique activity identifier.""" + if self._activity_id == None: + self._activity_id = sugar.util.unique_id() + return self._activity_id + + def join(self, activity_ps): + """Join an activity shared on the network.""" + self._shared = True + self._activity_id = activity_ps.get_id() + + # Publish the default service, it's a copy of + # one of those we found on the network. + services = activity_ps.get_services_of_type(self._default_type) + if len(services) > 0: + service = services[0] + addr = service.get_address() + port = service.get_port() + properties = service.get_published_values() + self._service = self._pservice.share_activity( + self, self._default_type, properties, addr, port) + else: + logging.error('Cannot join the activity') + + def share(self): + """Share the activity on the network.""" + logging.debug('Share activity %s on the network.' % self.get_id()) + + self._service = self._pservice.share_activity(self, self._default_type) + self._shared = True + + def execute(self, command, args): + """Execute the given command with args""" + pass + + def __destroy_cb(self, window): + if self._bus: + del self._bus + self._bus = None + if self._service: + self._pservice.unregister_service(self._service) diff --git a/sugar/activity/ActivityFactory.py b/sugar/activity/ActivityFactory.py index 66eba74..ee131ef 100644 --- a/sugar/activity/ActivityFactory.py +++ b/sugar/activity/ActivityFactory.py @@ -27,72 +27,72 @@ from sugar.presence.PresenceService import PresenceService from sugar.activity import Activity def get_path(activity_name): - """Returns the activity path""" - return '/' + activity_name.replace('.', '/') + """Returns the activity path""" + return '/' + activity_name.replace('.', '/') class ActivityFactory(dbus.service.Object): - """Dbus service that takes care of creating new instances of an activity""" + """Dbus service that takes care of creating new instances of an activity""" - def __init__(self, activity_type, activity_class): - self._activity_type = activity_type - self._activities = [] + def __init__(self, activity_type, activity_class): + self._activity_type = activity_type + self._activities = [] - splitted_module = activity_class.rsplit('.', 1) - module_name = splitted_module[0] - class_name = splitted_module[1] + splitted_module = activity_class.rsplit('.', 1) + module_name = splitted_module[0] + class_name = splitted_module[1] - module = __import__(module_name) - for comp in module_name.split('.')[1:]: - module = getattr(module, comp) - if hasattr(module, 'start'): - module.start() + module = __import__(module_name) + for comp in module_name.split('.')[1:]: + module = getattr(module, comp) + if hasattr(module, 'start'): + module.start() - self._module = module - self._constructor = getattr(module, class_name) - - bus = dbus.SessionBus() - factory = activity_type - bus_name = dbus.service.BusName(factory, bus = bus) - dbus.service.Object.__init__(self, bus_name, get_path(factory)) + self._module = module + self._constructor = getattr(module, class_name) + + bus = dbus.SessionBus() + factory = activity_type + bus_name = dbus.service.BusName(factory, bus = bus) + dbus.service.Object.__init__(self, bus_name, get_path(factory)) - @dbus.service.method("com.redhat.Sugar.ActivityFactory") - def create(self): - activity = self._constructor() - activity.set_type(self._activity_type) + @dbus.service.method("com.redhat.Sugar.ActivityFactory") + def create(self): + activity = self._constructor() + activity.set_type(self._activity_type) - self._activities.append(activity) - activity.connect('destroy', self._activity_destroy_cb) + self._activities.append(activity) + activity.connect('destroy', self._activity_destroy_cb) - return activity.window.xid + return activity.window.xid - def _activity_destroy_cb(self, activity): - self._activities.remove(activity) + def _activity_destroy_cb(self, activity): + self._activities.remove(activity) - if hasattr(self._module, 'stop'): - self._module.stop() + if hasattr(self._module, 'stop'): + self._module.stop() - if len(self._activities) == 0: - gtk.main_quit() + if len(self._activities) == 0: + gtk.main_quit() def create(activity_name): - """Create a new activity from his name.""" - bus = dbus.SessionBus() + """Create a new activity from his name.""" + bus = dbus.SessionBus() - factory_name = activity_name - factory_path = get_path(factory_name) + factory_name = activity_name + factory_path = get_path(factory_name) - proxy_obj = bus.get_object(factory_name, factory_path) - factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory") + proxy_obj = bus.get_object(factory_name, factory_path) + factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory") - xid = factory.create() + xid = factory.create() - bus = dbus.SessionBus() - proxy_obj = bus.get_object(Activity.get_service_name(xid), - Activity.get_object_path(xid)) - activity = dbus.Interface(proxy_obj, Activity.ACTIVITY_INTERFACE) + bus = dbus.SessionBus() + proxy_obj = bus.get_object(Activity.get_service_name(xid), + Activity.get_object_path(xid)) + activity = dbus.Interface(proxy_obj, Activity.ACTIVITY_INTERFACE) - return activity + return activity def register_factory(name, activity_class): - """Register the activity factory.""" - factory = ActivityFactory(name, activity_class) + """Register the activity factory.""" + factory = ActivityFactory(name, activity_class) diff --git a/sugar/activity/__init__.py b/sugar/activity/__init__.py index 959c0a1..1e606d5 100644 --- a/sugar/activity/__init__.py +++ b/sugar/activity/__init__.py @@ -9,10 +9,10 @@ sizes = 'gtk-large-toolbar=%d, %d' % (grid.dimension(1), grid.dimension(1)) settings.set_string_property('gtk-icon-sizes', sizes, '') def get_default_type(activity_type): - """Get the activity default type. + """Get the activity default type. - It's the type of the main network service which tracks presence + It's the type of the main network service which tracks presence and provides info about the activity, for example the title.""" - splitted_id = activity_type.split('.') - splitted_id.reverse() - return '_' + '_'.join(splitted_id) + '._udp' + splitted_id = activity_type.split('.') + splitted_id.reverse() + return '_' + '_'.join(splitted_id) + '._udp' diff --git a/sugar/activity/bundle.py b/sugar/activity/bundle.py index f9263a3..00d2d52 100644 --- a/sugar/activity/bundle.py +++ b/sugar/activity/bundle.py @@ -4,83 +4,83 @@ import os from ConfigParser import ConfigParser class Bundle: - """Info about an activity bundle. Wraps the activity.info file.""" - def __init__(self, path): - self._name = None - self._icon = None - self._service_name = None - self._show_launcher = True - self._valid = True - self._path = path - self._activity_version = 0 - - info_path = os.path.join(path, 'activity', 'activity.info') - if os.path.isfile(info_path): - self._parse_info(info_path) - else: - self._valid = False - - def _parse_info(self, info_path): - cp = ConfigParser() - cp.read([info_path]) - - section = 'Activity' - - if cp.has_option(section, 'service_name'): - self._service_name = cp.get(section, 'service_name') - else: - self._valid = False - logging.error('%s must specify a service name' % self._path) - - if cp.has_option(section, 'name'): - self._name = cp.get(section, 'name') - else: - self._valid = False - logging.error('%s must specify a name' % self._path) - - if cp.has_option(section, 'exec'): - self._exec = cp.get(section, 'exec') - else: - self._valid = False - logging.error('%s must specify an exec' % self._path) - - if cp.has_option(section, 'show_launcher'): - if cp.get(section, 'show_launcher') == 'no': - self._show_launcher = False - - if cp.has_option(section, 'icon'): - self._icon = cp.get(section, 'icon') - - if cp.has_option(section, 'activity_version'): - self._activity_version = int(cp.get(section, 'activity_version')) - - def is_valid(self): - return self._valid - - def get_path(self): - """Get the activity bundle path.""" - return self._path - - def get_name(self): - """Get the activity user visible name.""" - return self._name - - def get_service_name(self): - """Get the activity service name""" - return self._service_name - - def get_icon(self): - """Get the activity icon name""" - return self._icon - - def get_activity_version(self): - """Get the activity version""" - return self._activity_version - - def get_exec(self): - """Get the command to execute to launch the activity factory""" - return self._exec - - def get_show_launcher(self): - """Get whether there should be a visible launcher for the activity""" - return self._show_launcher + """Info about an activity bundle. Wraps the activity.info file.""" + def __init__(self, path): + self._name = None + self._icon = None + self._service_name = None + self._show_launcher = True + self._valid = True + self._path = path + self._activity_version = 0 + + info_path = os.path.join(path, 'activity', 'activity.info') + if os.path.isfile(info_path): + self._parse_info(info_path) + else: + self._valid = False + + def _parse_info(self, info_path): + cp = ConfigParser() + cp.read([info_path]) + + section = 'Activity' + + if cp.has_option(section, 'service_name'): + self._service_name = cp.get(section, 'service_name') + else: + self._valid = False + logging.error('%s must specify a service name' % self._path) + + if cp.has_option(section, 'name'): + self._name = cp.get(section, 'name') + else: + self._valid = False + logging.error('%s must specify a name' % self._path) + + if cp.has_option(section, 'exec'): + self._exec = cp.get(section, 'exec') + else: + self._valid = False + logging.error('%s must specify an exec' % self._path) + + if cp.has_option(section, 'show_launcher'): + if cp.get(section, 'show_launcher') == 'no': + self._show_launcher = False + + if cp.has_option(section, 'icon'): + self._icon = cp.get(section, 'icon') + + if cp.has_option(section, 'activity_version'): + self._activity_version = int(cp.get(section, 'activity_version')) + + def is_valid(self): + return self._valid + + def get_path(self): + """Get the activity bundle path.""" + return self._path + + def get_name(self): + """Get the activity user visible name.""" + return self._name + + def get_service_name(self): + """Get the activity service name""" + return self._service_name + + def get_icon(self): + """Get the activity icon name""" + return self._icon + + def get_activity_version(self): + """Get the activity version""" + return self._activity_version + + def get_exec(self): + """Get the command to execute to launch the activity factory""" + return self._exec + + def get_show_launcher(self): + """Get whether there should be a visible launcher for the activity""" + return self._show_launcher diff --git a/sugar/activity/bundlebuilder.py b/sugar/activity/bundlebuilder.py index c313523..4ab4f7d 100644 --- a/sugar/activity/bundlebuilder.py +++ b/sugar/activity/bundlebuilder.py @@ -25,71 +25,71 @@ import shutil from sugar.activity.bundle import Bundle class _SvnFileList(list): - def __init__(self): - f = os.popen('svn list -R') - for line in f.readlines(): - filename = line.strip() - if os.path.isfile(filename): - self.append(filename) - f.close() + def __init__(self): + f = os.popen('svn list -R') + for line in f.readlines(): + filename = line.strip() + if os.path.isfile(filename): + self.append(filename) + f.close() class _GitFileList(list): - def __init__(self): - f = os.popen('git-ls-files') - for line in f.readlines(): - filename = line.strip() - if not filename.startswith('.'): - self.append(filename) - f.close() + def __init__(self): + f = os.popen('git-ls-files') + for line in f.readlines(): + filename = line.strip() + if not filename.startswith('.'): + self.append(filename) + f.close() def _extract_bundle(source_file, dest_dir): - if not os.path.exists(dest_dir): - os.mkdir(dest_dir) + if not os.path.exists(dest_dir): + os.mkdir(dest_dir) - zf = zipfile.ZipFile(source_file) + zf = zipfile.ZipFile(source_file) - for i, name in enumerate(zf.namelist()): - path = os.path.join(dest_dir, name) - - if not os.path.exists(os.path.dirname(path)): - os.makedirs(os.path.dirname(path)) + for i, name in enumerate(zf.namelist()): + path = os.path.join(dest_dir, name) + + if not os.path.exists(os.path.dirname(path)): + os.makedirs(os.path.dirname(path)) - outfile = open(path, 'wb') - outfile.write(zf.read(name)) - outfile.flush() - outfile.close() + outfile = open(path, 'wb') + outfile.write(zf.read(name)) + outfile.flush() + outfile.close() def _get_source_path(): - return os.getcwd() + return os.getcwd() def _get_activities_path(): - path = os.path.expanduser('~/Activities') - if not os.path.isdir(path): - os.mkdir(path) - return path + path = os.path.expanduser('~/Activities') + if not os.path.isdir(path): + os.mkdir(path) + return path def _get_bundle_dir(): - bundle_name = os.path.basename(_get_source_path()) - return bundle_name + '.activity' + bundle_name = os.path.basename(_get_source_path()) + return bundle_name + '.activity' def _get_install_dir(prefix): - return os.path.join(prefix, 'share/activities') + return os.path.join(prefix, 'share/activities') def _get_bundle_path(): - return os.path.join(_get_activities_path(), _get_bundle_dir()) + return os.path.join(_get_activities_path(), _get_bundle_dir()) def _get_package_name(): - bundle = Bundle(_get_source_path()) - zipname = '%s-%d.xo' % (bundle.get_name(), bundle.get_activity_version()) - return zipname + bundle = Bundle(_get_source_path()) + zipname = '%s-%d.xo' % (bundle.get_name(), bundle.get_activity_version()) + return zipname def _delete_backups(arg, dirname, names): - for name in names: - if name.endswith('~') or name.endswith('pyc'): - os.remove(os.path.join(dirname, name)) + for name in names: + if name.endswith('~') or name.endswith('pyc'): + os.remove(os.path.join(dirname, name)) def cmd_help(): - print 'Usage: \n\ + print 'Usage: \n\ setup.py dev - setup for development \n\ setup.py dist - create a bundle package \n\ setup.py install - install the bundle \n\ @@ -98,59 +98,59 @@ setup.py help - print this message \n\ ' def cmd_dev(): - bundle_path = get_bundle_path() - try: - os.symlink(_get_source_path(), bundle_path) - except OSError: - if os.path.islink(bundle_path): - print 'ERROR - The bundle has been already setup for development.' - else: - print 'ERROR - A bundle with the same name is already installed.' + bundle_path = get_bundle_path() + try: + os.symlink(_get_source_path(), bundle_path) + except OSError: + if os.path.islink(bundle_path): + print 'ERROR - The bundle has been already setup for development.' + else: + print 'ERROR - A bundle with the same name is already installed.' def cmd_dist(): - if os.path.isdir('.git'): - file_list = _GitFileList() - elif os.path.isdir('.svn'): - file_list = _SvnFileList() - else: - print 'ERROR - The command works only with git or svn repositories.' - - zipname = _get_package_name() - bundle_zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED) - - for filename in file_list: - arcname = os.path.join(_get_bundle_dir(), filename) - bundle_zip.write(filename, arcname) - - bundle_zip.close() + if os.path.isdir('.git'): + file_list = _GitFileList() + elif os.path.isdir('.svn'): + file_list = _SvnFileList() + else: + print 'ERROR - The command works only with git or svn repositories.' + + zipname = _get_package_name() + bundle_zip = zipfile.ZipFile(zipname, 'w', zipfile.ZIP_DEFLATED) + + for filename in file_list: + arcname = os.path.join(_get_bundle_dir(), filename) + bundle_zip.write(filename, arcname) + + bundle_zip.close() def cmd_install(prefix): - cmd_dist() - cmd_uninstall(prefix) - _extract_bundle(_get_package_name(), _get_install_dir(prefix)) + cmd_dist() + cmd_uninstall(prefix) + _extract_bundle(_get_package_name(), _get_install_dir(prefix)) def cmd_uninstall(prefix): - path = os.path.join(_get_install_dir(prefix), _get_bundle_dir()) - if os.path.isdir(path): - shutil.rmtree(path) + path = os.path.join(_get_install_dir(prefix), _get_bundle_dir()) + if os.path.isdir(path): + shutil.rmtree(path) def cmd_clean(): - os.path.walk('.', delete_backups, None) + os.path.walk('.', delete_backups, None) def start(): - if len(sys.argv) < 2: - cmd_help() - elif sys.argv[1] == 'build': - pass - elif sys.argv[1] == 'dev': - cmd_dev() - elif sys.argv[1] == 'dist': - cmd_dist() - elif sys.argv[1] == 'install' and len(sys.argv) == 3: - cmd_install(sys.argv[2]) - elif sys.argv[1] == 'uninstall' and len(sys.argv) == 3: - cmd_uninstall(sys.argv[2]) - elif sys.argv[1] == 'clean': - cmd_clean() - else: - cmd_help() + if len(sys.argv) < 2: + cmd_help() + elif sys.argv[1] == 'build': + pass + elif sys.argv[1] == 'dev': + cmd_dev() + elif sys.argv[1] == 'dist': + cmd_dist() + elif sys.argv[1] == 'install' and len(sys.argv) == 3: + cmd_install(sys.argv[2]) + elif sys.argv[1] == 'uninstall' and len(sys.argv) == 3: + cmd_uninstall(sys.argv[2]) + elif sys.argv[1] == 'clean': + cmd_clean() + else: + cmd_help() diff --git a/sugar/activity/bundleregistry.py b/sugar/activity/bundleregistry.py index f9adbca..aa7d70a 100644 --- a/sugar/activity/bundleregistry.py +++ b/sugar/activity/bundleregistry.py @@ -6,51 +6,51 @@ from sugar import env from sugar import util class _ServiceManager(object): - def __init__(self): - self._path = env.get_user_service_dir() + def __init__(self): + self._path = env.get_user_service_dir() - def add(self, bundle): - name = bundle.get_service_name() + def add(self, bundle): + name = bundle.get_service_name() - # FIXME evil hack. Probably need to fix Exec spec - full_exec = env.get_shell_bin_dir() + '/' + bundle.get_exec() - full_exec += ' ' + bundle.get_path() + # FIXME evil hack. Probably need to fix Exec spec + full_exec = env.get_shell_bin_dir() + '/' + bundle.get_exec() + full_exec += ' ' + bundle.get_path() - util.write_service(name, full_exec, self._path) + util.write_service(name, full_exec, self._path) class BundleRegistry: - """Service that tracks the available activity bundles""" - - def __init__(self): - self._bundles = {} - self._search_path = [] - self._service_manager = _ServiceManager() - - def get_bundle(self, service_name): - """Returns an bundle given his service name""" - if self._bundles.has_key(service_name): - return self._bundles[service_name] - else: - return None - - def add_search_path(self, path): - """Add a directory to the bundles search path""" - self._search_path.append(path) - self._scan_directory(path) - - def __iter__(self): - return self._bundles.values().__iter__() - - def _scan_directory(self, path): - if os.path.isdir(path): - for f in os.listdir(path): - bundle_dir = os.path.join(path, f) - if os.path.isdir(bundle_dir) and \ - bundle_dir.endswith('.activity'): - self._add_bundle(bundle_dir) - - def _add_bundle(self, bundle_path): - bundle = Bundle(bundle_path) - if bundle.is_valid(): - self._bundles[bundle.get_service_name()] = bundle - self._service_manager.add(bundle) + """Service that tracks the available activity bundles""" + + def __init__(self): + self._bundles = {} + self._search_path = [] + self._service_manager = _ServiceManager() + + def get_bundle(self, service_name): + """Returns an bundle given his service name""" + if self._bundles.has_key(service_name): + return self._bundles[service_name] + else: + return None + + def add_search_path(self, path): + """Add a directory to the bundles search path""" + self._search_path.append(path) + self._scan_directory(path) + + def __iter__(self): + return self._bundles.values().__iter__() + + def _scan_directory(self, path): + if os.path.isdir(path): + for f in os.listdir(path): + bundle_dir = os.path.join(path, f) + if os.path.isdir(bundle_dir) and \ + bundle_dir.endswith('.activity'): + self._add_bundle(bundle_dir) + + def _add_bundle(self, bundle_path): + bundle = Bundle(bundle_path) + if bundle.is_valid(): + self._bundles[bundle.get_service_name()] = bundle + self._service_manager.add(bundle) diff --git a/sugar/chat/ActivityChat.py b/sugar/chat/ActivityChat.py index 1892672..ecba9af 100644 --- a/sugar/chat/ActivityChat.py +++ b/sugar/chat/ActivityChat.py @@ -20,48 +20,48 @@ import logging from sugar.chat.GroupChat import GroupChat class ActivityChat(GroupChat): - SERVICE_TYPE = "_olpc_activity_chat._udp" + SERVICE_TYPE = "_olpc_activity_chat._udp" - def __init__(self, activity): - GroupChat.__init__(self) - self._chat_service = None + def __init__(self, activity): + GroupChat.__init__(self) + self._chat_service = None - self.connect('destroy', self._destroy_cb) + self.connect('destroy', self._destroy_cb) - self._activity = activity - self._pservice.register_service_type(ActivityChat.SERVICE_TYPE) - self._pservice.connect('service-appeared', self._service_appeared_cb) + self._activity = activity + self._pservice.register_service_type(ActivityChat.SERVICE_TYPE) + self._pservice.connect('service-appeared', self._service_appeared_cb) - # Find an existing activity chat to latch onto - ps_activity = self._pservice.get_activity(activity.get_id()) - if ps_activity is not None: - services = ps_activity.get_services_of_type(ActivityChat.SERVICE_TYPE) - if len(services) > 0: - self._service_appeared_cb(self._pservice, services[0]) + # Find an existing activity chat to latch onto + ps_activity = self._pservice.get_activity(activity.get_id()) + if ps_activity is not None: + services = ps_activity.get_services_of_type(ActivityChat.SERVICE_TYPE) + if len(services) > 0: + self._service_appeared_cb(self._pservice, services[0]) - def _service_appeared_cb(self, pservice, service): - if service.get_activity_id() != self._activity.get_id(): - return - if service.get_type() != ActivityChat.SERVICE_TYPE: - return - if self._chat_service: - return + def _service_appeared_cb(self, pservice, service): + if service.get_activity_id() != self._activity.get_id(): + return + if service.get_type() != ActivityChat.SERVICE_TYPE: + return + if self._chat_service: + return - logging.debug('Activity chat service appeared, setup the stream.') - # Ok, there's an existing chat service that we copy - # parameters and such from - addr = service.get_address() - port = service.get_port() - self._chat_service = self._pservice.share_activity(self._activity, - stype=ActivityChat.SERVICE_TYPE, address=addr, port=port) - self._setup_stream(self._chat_service) + logging.debug('Activity chat service appeared, setup the stream.') + # Ok, there's an existing chat service that we copy + # parameters and such from + addr = service.get_address() + port = service.get_port() + self._chat_service = self._pservice.share_activity(self._activity, + stype=ActivityChat.SERVICE_TYPE, address=addr, port=port) + self._setup_stream(self._chat_service) - def share(self): - """Only called when we share the activity this chat is tied to.""" - self._chat_service = self._pservice.share_activity(self._activity, - stype=ActivityChat.SERVICE_TYPE) - self._setup_stream(self._chat_service) + def share(self): + """Only called when we share the activity this chat is tied to.""" + self._chat_service = self._pservice.share_activity(self._activity, + stype=ActivityChat.SERVICE_TYPE) + self._setup_stream(self._chat_service) - def _destroy_cb(self, widget): - if self._chat_service: - self._pservice.unregister_service(self._chat_service) + def _destroy_cb(self, widget): + if self._chat_service: + self._pservice.unregister_service(self._chat_service) diff --git a/sugar/chat/Chat.py b/sugar/chat/Chat.py index eb1b4da..aae6450 100644 --- a/sugar/chat/Chat.py +++ b/sugar/chat/Chat.py @@ -37,244 +37,244 @@ import richtext PANGO_SCALE = 1024 # Where is this defined? class Chat(gtk.VBox): - SERVICE_TYPE = "_olpc_chat._tcp" - SERVICE_PORT = 6100 - - TEXT_MODE = 0 - SKETCH_MODE = 1 - - def __init__(self): - gtk.VBox.__init__(self, False, 6) - - self._pservice = PresenceService.get_instance() - - self._stream_writer = None - self.set_border_width(12) - - chat_vbox = gtk.VBox() - chat_vbox.set_spacing(6) - - self._chat_sw = gtk.ScrolledWindow() - self._chat_sw.set_shadow_type(gtk.SHADOW_IN) - self._chat_sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) - self._chat_view = richtext.RichTextView() - self._chat_view.connect("link-clicked", self.__link_clicked_cb) - self._chat_view.set_editable(False) - self._chat_view.set_cursor_visible(False) - self._chat_view.set_pixels_above_lines(7) - self._chat_view.set_left_margin(5) - self._chat_sw.add(self._chat_view) - self._chat_view.show() - chat_vbox.pack_start(self._chat_sw) - self._chat_sw.show() - - self.pack_start(chat_vbox) - chat_vbox.show() - - self._mode = Chat.TEXT_MODE - self._editor = ChatEditor(self, ChatEditor.TEXT_MODE) - - toolbar = ChatToolbar(self._editor) - self.pack_start(toolbar, False) - toolbar.show() - - self.pack_start(self._editor, False) - self._editor.show() - - self.connect("key-press-event", self.__key_press_event_cb) - - def __key_press_event_cb(self, window, event): - if event.keyval == gtk.keysyms.s and \ - event.state & gtk.gdk.CONTROL_MASK: - if self.get_mode() == Chat.SKETCH_MODE: - self.set_mode(Chat.TEXT_MODE) - elif self.get_mode() == Chat.TEXT_MODE: - self.set_mode(Chat.SKETCH_MODE) - - def get_mode(self): - return self._mode - - def set_mode(self, mode): - self._mode = mode - if self._mode == Chat.TEXT_MODE: - self._editor.set_mode(ChatEditor.TEXT_MODE) - elif self._mode == Chat.SKETCH_MODE: - self._editor.set_mode(ChatEditor.SKETCH_MODE) - - def __get_browser_shell(self): - bus = dbus.SessionBus() - proxy_obj = bus.get_object('com.redhat.Sugar.Browser', '/com/redhat/Sugar/Browser') - self._browser_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.BrowserShell') - - def __link_clicked_cb(self, view, address): - self.__get_browser_shell().open_browser(address) - - def _scroll_chat_view_to_bottom(self): - # Only scroll to bottom if the view is already close to the bottom - vadj = self._chat_sw.get_vadjustment() - if vadj.value + vadj.page_size > vadj.upper * 0.8: - vadj.value = vadj.upper - vadj.page_size - self._chat_sw.set_vadjustment(vadj) - - def _message_inserted(self): - gobject.idle_add(self._scroll_chat_view_to_bottom) - - def _insert_buddy(self, buf, buddy): - # Stuff in the buddy icon, if we have one for this buddy - icon = buddy.get_icon_pixbuf() - if icon: - rise = int(icon.get_height() / 4) * -1 - - hash_string = "%s-%s" % (buddy.get_name(), buddy.get_ip4_address()) - sha_hash = sha.new() - sha_hash.update(hash_string) - tagname = "buddyicon-%s" % sha_hash.hexdigest() - - if not buf.get_tag_table().lookup(tagname): - buf.create_tag(tagname, rise=(rise * PANGO_SCALE)) - - aniter = buf.get_end_iter() - buf.insert_pixbuf(aniter, icon) - aniter.backward_char() - enditer = buf.get_end_iter() - buf.apply_tag_by_name(tagname, aniter, enditer) - - # Stick in the buddy's nickname - if not buf.get_tag_table().lookup("nickname"): - buf.create_tag("nickname", weight=pango.WEIGHT_BOLD) - aniter = buf.get_end_iter() - offset = aniter.get_offset() - buf.insert(aniter, " " + buddy.get_name() + ": ") - enditer = buf.get_iter_at_offset(offset) - buf.apply_tag_by_name("nickname", aniter, enditer) - - def _insert_rich_message(self, buddy, msg): - msg = Emoticons.get_instance().replace(msg) - - buf = self._chat_view.get_buffer() - self._insert_buddy(buf, buddy) - - serializer = richtext.RichTextSerializer() - serializer.deserialize(msg, buf) - aniter = buf.get_end_iter() - buf.insert(aniter, "\n") - - self._message_inserted() - - def _insert_sketch(self, buddy, svgdata): - """Insert a sketch object into the chat buffer.""" - pbl = gtk.gdk.PixbufLoader("svg") - pbl.write(svgdata) - pbl.close() - pbuf = pbl.get_pixbuf() - - buf = self._chat_view.get_buffer() - - self._insert_buddy(buf, buddy) - - rise = int(pbuf.get_height() / 3) * -1 - sha_hash = sha.new() - sha_hash.update(svgdata) - tagname = "sketch-%s" % sha_hash.hexdigest() - if not buf.get_tag_table().lookup(tagname): - buf.create_tag(tagname, rise=(rise * PANGO_SCALE)) - - aniter = buf.get_end_iter() - buf.insert_pixbuf(aniter, pbuf) - aniter.backward_char() - enditer = buf.get_end_iter() - buf.apply_tag_by_name(tagname, aniter, enditer) - aniter = buf.get_end_iter() - buf.insert(aniter, "\n") - - self._message_inserted() - - def _get_first_richtext_chunk(self, msg): - """Scan the message for the first richtext-tagged chunk and return it.""" - rt_last = -1 - tag_rt_start = "" - tag_rt_end = "" - rt_first = msg.find(tag_rt_start) - length = -1 - if rt_first >= 0: - length = len(msg) - rt_last = msg.find(tag_rt_end, rt_first) - if rt_first >= 0 and rt_last >= (rt_first + len(tag_rt_start)) and length > 0: - return msg[rt_first:rt_last + len(tag_rt_end)] - return None - - def _get_first_sketch_chunk(self, msg): - """Scan the message for the first SVG-tagged chunk and return it.""" - svg_last = -1 - tag_svg_start = "") - if desc_start < 0: - return None - ignore = msg.find("= 0: - length = len(msg) - svg_last = msg.find(tag_svg_end, svg_first) - if svg_first >= 0 and svg_last >= (svg_first + len(tag_svg_start)) and length > 0: - return msg[desc_start:svg_last + len(tag_svg_end)] - return None - - def recv_message(self, message): - """Insert a remote chat message into the chat buffer.""" - [nick, msg] = Chat.deserialize_message(message) - buddy = self._pservice.get_buddy_by_name(nick) - if not buddy: - logging.error('The buddy %s is not present.' % (nick)) - return - - # FIXME a better way to compare buddies? - owner = self._pservice.get_owner() - if buddy.get_name() == owner.get_name(): - return - - chunk = self._get_first_richtext_chunk(msg) - if chunk: - self._insert_rich_message(buddy, chunk) - return - - chunk = self._get_first_sketch_chunk(msg) - if chunk: - self._insert_sketch(buddy, chunk) - return - - def set_stream_writer(self, stream_writer): - self._stream_writer = stream_writer - - def send_sketch(self, svgdata): - if not svgdata or not len(svgdata): - return - if self._stream_writer: - self._stream_writer.write(self.serialize_message(svgdata)) - owner = self._pservice.get_owner() - if owner: - self._insert_sketch(owner, svgdata) - - def send_text_message(self, text): - """Send a chat message and insert it into the local buffer.""" - if len(text) <= 0: - return - if self._stream_writer: - self._stream_writer.write(self.serialize_message(text)) - else: - logging.warning("Cannot send message, there is no stream writer") - owner = self._pservice.get_owner() - if owner: - self._insert_rich_message(owner, text) - - def serialize_message(self, message): - owner = self._pservice.get_owner() - return owner.get_name() + '||' + message - - def deserialize_message(message): - return message.split('||', 1) - - deserialize_message = staticmethod(deserialize_message) + SERVICE_TYPE = "_olpc_chat._tcp" + SERVICE_PORT = 6100 + + TEXT_MODE = 0 + SKETCH_MODE = 1 + + def __init__(self): + gtk.VBox.__init__(self, False, 6) + + self._pservice = PresenceService.get_instance() + + self._stream_writer = None + self.set_border_width(12) + + chat_vbox = gtk.VBox() + chat_vbox.set_spacing(6) + + self._chat_sw = gtk.ScrolledWindow() + self._chat_sw.set_shadow_type(gtk.SHADOW_IN) + self._chat_sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) + self._chat_view = richtext.RichTextView() + self._chat_view.connect("link-clicked", self.__link_clicked_cb) + self._chat_view.set_editable(False) + self._chat_view.set_cursor_visible(False) + self._chat_view.set_pixels_above_lines(7) + self._chat_view.set_left_margin(5) + self._chat_sw.add(self._chat_view) + self._chat_view.show() + chat_vbox.pack_start(self._chat_sw) + self._chat_sw.show() + + self.pack_start(chat_vbox) + chat_vbox.show() + + self._mode = Chat.TEXT_MODE + self._editor = ChatEditor(self, ChatEditor.TEXT_MODE) + + toolbar = ChatToolbar(self._editor) + self.pack_start(toolbar, False) + toolbar.show() + + self.pack_start(self._editor, False) + self._editor.show() + + self.connect("key-press-event", self.__key_press_event_cb) + + def __key_press_event_cb(self, window, event): + if event.keyval == gtk.keysyms.s and \ + event.state & gtk.gdk.CONTROL_MASK: + if self.get_mode() == Chat.SKETCH_MODE: + self.set_mode(Chat.TEXT_MODE) + elif self.get_mode() == Chat.TEXT_MODE: + self.set_mode(Chat.SKETCH_MODE) + + def get_mode(self): + return self._mode + + def set_mode(self, mode): + self._mode = mode + if self._mode == Chat.TEXT_MODE: + self._editor.set_mode(ChatEditor.TEXT_MODE) + elif self._mode == Chat.SKETCH_MODE: + self._editor.set_mode(ChatEditor.SKETCH_MODE) + + def __get_browser_shell(self): + bus = dbus.SessionBus() + proxy_obj = bus.get_object('com.redhat.Sugar.Browser', '/com/redhat/Sugar/Browser') + self._browser_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.BrowserShell') + + def __link_clicked_cb(self, view, address): + self.__get_browser_shell().open_browser(address) + + def _scroll_chat_view_to_bottom(self): + # Only scroll to bottom if the view is already close to the bottom + vadj = self._chat_sw.get_vadjustment() + if vadj.value + vadj.page_size > vadj.upper * 0.8: + vadj.value = vadj.upper - vadj.page_size + self._chat_sw.set_vadjustment(vadj) + + def _message_inserted(self): + gobject.idle_add(self._scroll_chat_view_to_bottom) + + def _insert_buddy(self, buf, buddy): + # Stuff in the buddy icon, if we have one for this buddy + icon = buddy.get_icon_pixbuf() + if icon: + rise = int(icon.get_height() / 4) * -1 + + hash_string = "%s-%s" % (buddy.get_name(), buddy.get_ip4_address()) + sha_hash = sha.new() + sha_hash.update(hash_string) + tagname = "buddyicon-%s" % sha_hash.hexdigest() + + if not buf.get_tag_table().lookup(tagname): + buf.create_tag(tagname, rise=(rise * PANGO_SCALE)) + + aniter = buf.get_end_iter() + buf.insert_pixbuf(aniter, icon) + aniter.backward_char() + enditer = buf.get_end_iter() + buf.apply_tag_by_name(tagname, aniter, enditer) + + # Stick in the buddy's nickname + if not buf.get_tag_table().lookup("nickname"): + buf.create_tag("nickname", weight=pango.WEIGHT_BOLD) + aniter = buf.get_end_iter() + offset = aniter.get_offset() + buf.insert(aniter, " " + buddy.get_name() + ": ") + enditer = buf.get_iter_at_offset(offset) + buf.apply_tag_by_name("nickname", aniter, enditer) + + def _insert_rich_message(self, buddy, msg): + msg = Emoticons.get_instance().replace(msg) + + buf = self._chat_view.get_buffer() + self._insert_buddy(buf, buddy) + + serializer = richtext.RichTextSerializer() + serializer.deserialize(msg, buf) + aniter = buf.get_end_iter() + buf.insert(aniter, "\n") + + self._message_inserted() + + def _insert_sketch(self, buddy, svgdata): + """Insert a sketch object into the chat buffer.""" + pbl = gtk.gdk.PixbufLoader("svg") + pbl.write(svgdata) + pbl.close() + pbuf = pbl.get_pixbuf() + + buf = self._chat_view.get_buffer() + + self._insert_buddy(buf, buddy) + + rise = int(pbuf.get_height() / 3) * -1 + sha_hash = sha.new() + sha_hash.update(svgdata) + tagname = "sketch-%s" % sha_hash.hexdigest() + if not buf.get_tag_table().lookup(tagname): + buf.create_tag(tagname, rise=(rise * PANGO_SCALE)) + + aniter = buf.get_end_iter() + buf.insert_pixbuf(aniter, pbuf) + aniter.backward_char() + enditer = buf.get_end_iter() + buf.apply_tag_by_name(tagname, aniter, enditer) + aniter = buf.get_end_iter() + buf.insert(aniter, "\n") + + self._message_inserted() + + def _get_first_richtext_chunk(self, msg): + """Scan the message for the first richtext-tagged chunk and return it.""" + rt_last = -1 + tag_rt_start = "" + tag_rt_end = "" + rt_first = msg.find(tag_rt_start) + length = -1 + if rt_first >= 0: + length = len(msg) + rt_last = msg.find(tag_rt_end, rt_first) + if rt_first >= 0 and rt_last >= (rt_first + len(tag_rt_start)) and length > 0: + return msg[rt_first:rt_last + len(tag_rt_end)] + return None + + def _get_first_sketch_chunk(self, msg): + """Scan the message for the first SVG-tagged chunk and return it.""" + svg_last = -1 + tag_svg_start = "") + if desc_start < 0: + return None + ignore = msg.find("= 0: + length = len(msg) + svg_last = msg.find(tag_svg_end, svg_first) + if svg_first >= 0 and svg_last >= (svg_first + len(tag_svg_start)) and length > 0: + return msg[desc_start:svg_last + len(tag_svg_end)] + return None + + def recv_message(self, message): + """Insert a remote chat message into the chat buffer.""" + [nick, msg] = Chat.deserialize_message(message) + buddy = self._pservice.get_buddy_by_name(nick) + if not buddy: + logging.error('The buddy %s is not present.' % (nick)) + return + + # FIXME a better way to compare buddies? + owner = self._pservice.get_owner() + if buddy.get_name() == owner.get_name(): + return + + chunk = self._get_first_richtext_chunk(msg) + if chunk: + self._insert_rich_message(buddy, chunk) + return + + chunk = self._get_first_sketch_chunk(msg) + if chunk: + self._insert_sketch(buddy, chunk) + return + + def set_stream_writer(self, stream_writer): + self._stream_writer = stream_writer + + def send_sketch(self, svgdata): + if not svgdata or not len(svgdata): + return + if self._stream_writer: + self._stream_writer.write(self.serialize_message(svgdata)) + owner = self._pservice.get_owner() + if owner: + self._insert_sketch(owner, svgdata) + + def send_text_message(self, text): + """Send a chat message and insert it into the local buffer.""" + if len(text) <= 0: + return + if self._stream_writer: + self._stream_writer.write(self.serialize_message(text)) + else: + logging.warning("Cannot send message, there is no stream writer") + owner = self._pservice.get_owner() + if owner: + self._insert_rich_message(owner, text) + + def serialize_message(self, message): + owner = self._pservice.get_owner() + return owner.get_name() + '||' + message + + def deserialize_message(message): + return message.split('||', 1) + + deserialize_message = staticmethod(deserialize_message) diff --git a/sugar/chat/ChatEditor.py b/sugar/chat/ChatEditor.py index 4ee16ce..ee36688 100644 --- a/sugar/chat/ChatEditor.py +++ b/sugar/chat/ChatEditor.py @@ -22,83 +22,83 @@ from sugar.chat.sketchpad.SketchPad import SketchPad import richtext class ChatEditor(gtk.HBox): - TEXT_MODE = 0 - SKETCH_MODE = 1 + TEXT_MODE = 0 + SKETCH_MODE = 1 - def __init__(self, chat, mode): - gtk.HBox.__init__(self, False, 6) + def __init__(self, chat, mode): + gtk.HBox.__init__(self, False, 6) - self._chat = chat + self._chat = chat - self._notebook = gtk.Notebook() - self._notebook.set_show_tabs(False) - self._notebook.set_show_border(False) - self._notebook.set_size_request(-1, 70) - - chat_view_sw = gtk.ScrolledWindow() - chat_view_sw.set_shadow_type(gtk.SHADOW_IN) - chat_view_sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self._text_view = richtext.RichTextView() - self._text_view.connect("key-press-event", self.__key_press_event_cb) - chat_view_sw.add(self._text_view) - self._text_view.show() - - self._notebook.append_page(chat_view_sw) - chat_view_sw.show() - - self._sketchpad = SketchPad() - self._notebook.append_page(self._sketchpad) - self._sketchpad.show() - - self.pack_start(self._notebook) - self._notebook.show() - - send_button = gtk.Button(_("Send")) - send_button.set_size_request(60, -1) - send_button.connect('clicked', self.__send_button_clicked_cb) - self.pack_start(send_button, False, True) - send_button.show() - - self.set_mode(mode) + self._notebook = gtk.Notebook() + self._notebook.set_show_tabs(False) + self._notebook.set_show_border(False) + self._notebook.set_size_request(-1, 70) + + chat_view_sw = gtk.ScrolledWindow() + chat_view_sw.set_shadow_type(gtk.SHADOW_IN) + chat_view_sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self._text_view = richtext.RichTextView() + self._text_view.connect("key-press-event", self.__key_press_event_cb) + chat_view_sw.add(self._text_view) + self._text_view.show() + + self._notebook.append_page(chat_view_sw) + chat_view_sw.show() + + self._sketchpad = SketchPad() + self._notebook.append_page(self._sketchpad) + self._sketchpad.show() + + self.pack_start(self._notebook) + self._notebook.show() + + send_button = gtk.Button(_("Send")) + send_button.set_size_request(60, -1) + send_button.connect('clicked', self.__send_button_clicked_cb) + self.pack_start(send_button, False, True) + send_button.show() + + self.set_mode(mode) - def set_color(self, color): - self._sketchpad.set_color(color) - - def get_buffer(self): - return self._text_view.get_buffer() + def set_color(self, color): + self._sketchpad.set_color(color) + + def get_buffer(self): + return self._text_view.get_buffer() - def set_mode(self, mode): - self._mode = mode - if self._mode == ChatEditor.SKETCH_MODE: - self._notebook.set_current_page(1) - elif self._mode == ChatEditor.TEXT_MODE: - self._notebook.set_current_page(0) + def set_mode(self, mode): + self._mode = mode + if self._mode == ChatEditor.SKETCH_MODE: + self._notebook.set_current_page(1) + elif self._mode == ChatEditor.TEXT_MODE: + self._notebook.set_current_page(0) - def __send_button_clicked_cb(self, button): - self._send() + def __send_button_clicked_cb(self, button): + self._send() - def _send(self): - if self._mode == ChatEditor.SKETCH_MODE: - self._send_sketch() - elif self._mode == ChatEditor.TEXT_MODE: - self._send_text() + def _send(self): + if self._mode == ChatEditor.SKETCH_MODE: + self._send_sketch() + elif self._mode == ChatEditor.TEXT_MODE: + self._send_text() - def _send_sketch(self): - self._chat.send_sketch(self._sketchpad.to_svg()) - self._sketchpad.clear() + def _send_sketch(self): + self._chat.send_sketch(self._sketchpad.to_svg()) + self._sketchpad.clear() - def _send_text(self): - buf = self._text_view.get_buffer() - text = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) - if len(text.strip()) > 0: - serializer = richtext.RichTextSerializer() - text = serializer.serialize(buf) - self._chat.send_text_message(text) + def _send_text(self): + buf = self._text_view.get_buffer() + text = buf.get_text(buf.get_start_iter(), buf.get_end_iter()) + if len(text.strip()) > 0: + serializer = richtext.RichTextSerializer() + text = serializer.serialize(buf) + self._chat.send_text_message(text) - buf.set_text("") - buf.place_cursor(buf.get_start_iter()) - - def __key_press_event_cb(self, text_view, event): - if event.keyval == gtk.keysyms.Return: - self._send() - return True + buf.set_text("") + buf.place_cursor(buf.get_start_iter()) + + def __key_press_event_cb(self, text_view, event): + if event.keyval == gtk.keysyms.Return: + self._send() + return True diff --git a/sugar/chat/ChatToolbar.py b/sugar/chat/ChatToolbar.py index 8e88a68..d633138 100644 --- a/sugar/chat/ChatToolbar.py +++ b/sugar/chat/ChatToolbar.py @@ -22,129 +22,129 @@ from sugar.chat.sketchpad.Toolbox import Toolbox import richtext class ChatToolbar(gtk.HBox): - def __init__(self, editor): - gtk.HBox.__init__(self, False, 24) - - self._editor = editor - self._emt_popup = None - - spring = gtk.Label('') - self.pack_start(spring, True) - spring.show() - - toolbox = richtext.RichTextToolbox(editor.get_buffer()) - self.pack_start(toolbox, False) - toolbox.show() - - item = gtk.Button() - item.unset_flags(gtk.CAN_FOCUS) - - e_hbox = gtk.HBox(False, 6) - - e_image = gtk.Image() - e_image.set_from_icon_name('stock_smiley-1', gtk.ICON_SIZE_SMALL_TOOLBAR) - e_hbox.pack_start(e_image) - e_image.show() - - arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE) - e_hbox.pack_start(arrow) - arrow.show() - - item.set_image(e_hbox) - item.connect("clicked", self.__emoticons_button_clicked_cb) - toolbox.pack_start(item, False) - item.show() - -# separator = gtk.SeparatorToolItem() -# toolbar.insert(separator, -1) -# separator.show() - -# item = gtk.MenuToolButton(None, "Links") -# item.set_menu(gtk.Menu()) -# item.connect("show-menu", self.__show_link_menu_cb) -# toolbar.insert(item, -1) -# item.show() - - toolbox = Toolbox() - toolbox.connect('color-selected', self._color_selected) - self.pack_start(toolbox, False) - toolbox.show() - - spring = gtk.Label('') - self.pack_start(spring, True) - spring.show() - - def _color_selected(self, toolbox, color): - self._editor.set_color(color) - - def __link_activate_cb(self, item, link): - buf = self._editor.get_buffer() - buf.append_link(link['title'], link['address']) - - def __show_link_menu_cb(self, button): - menu = gtk.Menu() - - links = self.__get_browser_shell().get_links() - - for link in links: - item = gtk.MenuItem(link['title'], False) - item.connect("activate", self.__link_activate_cb, link) - menu.append(item) - item.show() - - button.set_menu(menu) - - def _create_emoticons_popup(self): - model = gtk.ListStore(gtk.gdk.Pixbuf, str) - - for name in Emoticons.get_instance().get_all(): - icon_theme = gtk.icon_theme_get_default() - try: - pixbuf = icon_theme.load_icon(name, 16, 0) - model.append([pixbuf, name]) - except gobject.GError: - pass - - icon_view = gtk.IconView(model) - icon_view.connect('selection-changed', self.__emoticon_selection_changed_cb) - icon_view.set_pixbuf_column(0) - icon_view.set_selection_mode(gtk.SELECTION_SINGLE) - - frame = gtk.Frame() - frame.set_shadow_type(gtk.SHADOW_ETCHED_IN) - frame.add(icon_view) - icon_view.show() - - window = gtk.Window(gtk.WINDOW_POPUP) - window.add(frame) - frame.show() - - return window - - def __emoticon_selection_changed_cb(self, icon_view): - items = icon_view.get_selected_items() - if items: - model = icon_view.get_model() - icon_name = model[items[0]][1] - self._editor.get_buffer().append_icon(icon_name) - self._emt_popup.hide() - - def __emoticons_button_clicked_cb(self, button): - # FIXME grabs... - if not self._emt_popup: - self._emt_popup = self._create_emoticons_popup() - - if self._emt_popup.get_property('visible'): - self._emt_popup.hide() - else: - width = 180 - height = 130 - - self._emt_popup.set_default_size(width, height) - - [x, y] = button.window.get_origin() - x += button.allocation.x - y += button.allocation.y - height - self._emt_popup.move(x, y) - - self._emt_popup.show() + def __init__(self, editor): + gtk.HBox.__init__(self, False, 24) + + self._editor = editor + self._emt_popup = None + + spring = gtk.Label('') + self.pack_start(spring, True) + spring.show() + + toolbox = richtext.RichTextToolbox(editor.get_buffer()) + self.pack_start(toolbox, False) + toolbox.show() + + item = gtk.Button() + item.unset_flags(gtk.CAN_FOCUS) + + e_hbox = gtk.HBox(False, 6) + + e_image = gtk.Image() + e_image.set_from_icon_name('stock_smiley-1', gtk.ICON_SIZE_SMALL_TOOLBAR) + e_hbox.pack_start(e_image) + e_image.show() + + arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE) + e_hbox.pack_start(arrow) + arrow.show() + + item.set_image(e_hbox) + item.connect("clicked", self.__emoticons_button_clicked_cb) + toolbox.pack_start(item, False) + item.show() + +# separator = gtk.SeparatorToolItem() +# toolbar.insert(separator, -1) +# separator.show() + +# item = gtk.MenuToolButton(None, "Links") +# item.set_menu(gtk.Menu()) +# item.connect("show-menu", self.__show_link_menu_cb) +# toolbar.insert(item, -1) +# item.show() + + toolbox = Toolbox() + toolbox.connect('color-selected', self._color_selected) + self.pack_start(toolbox, False) + toolbox.show() + + spring = gtk.Label('') + self.pack_start(spring, True) + spring.show() + + def _color_selected(self, toolbox, color): + self._editor.set_color(color) + + def __link_activate_cb(self, item, link): + buf = self._editor.get_buffer() + buf.append_link(link['title'], link['address']) + + def __show_link_menu_cb(self, button): + menu = gtk.Menu() + + links = self.__get_browser_shell().get_links() + + for link in links: + item = gtk.MenuItem(link['title'], False) + item.connect("activate", self.__link_activate_cb, link) + menu.append(item) + item.show() + + button.set_menu(menu) + + def _create_emoticons_popup(self): + model = gtk.ListStore(gtk.gdk.Pixbuf, str) + + for name in Emoticons.get_instance().get_all(): + icon_theme = gtk.icon_theme_get_default() + try: + pixbuf = icon_theme.load_icon(name, 16, 0) + model.append([pixbuf, name]) + except gobject.GError: + pass + + icon_view = gtk.IconView(model) + icon_view.connect('selection-changed', self.__emoticon_selection_changed_cb) + icon_view.set_pixbuf_column(0) + icon_view.set_selection_mode(gtk.SELECTION_SINGLE) + + frame = gtk.Frame() + frame.set_shadow_type(gtk.SHADOW_ETCHED_IN) + frame.add(icon_view) + icon_view.show() + + window = gtk.Window(gtk.WINDOW_POPUP) + window.add(frame) + frame.show() + + return window + + def __emoticon_selection_changed_cb(self, icon_view): + items = icon_view.get_selected_items() + if items: + model = icon_view.get_model() + icon_name = model[items[0]][1] + self._editor.get_buffer().append_icon(icon_name) + self._emt_popup.hide() + + def __emoticons_button_clicked_cb(self, button): + # FIXME grabs... + if not self._emt_popup: + self._emt_popup = self._create_emoticons_popup() + + if self._emt_popup.get_property('visible'): + self._emt_popup.hide() + else: + width = 180 + height = 130 + + self._emt_popup.set_default_size(width, height) + + [x, y] = button.window.get_origin() + x += button.allocation.x + y += button.allocation.y - height + self._emt_popup.move(x, y) + + self._emt_popup.show() diff --git a/sugar/chat/Emoticons.py b/sugar/chat/Emoticons.py index 3f0e09e..33e53eb 100644 --- a/sugar/chat/Emoticons.py +++ b/sugar/chat/Emoticons.py @@ -15,70 +15,70 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. -emoticons_table = [ \ -[ 'stock_smiley-10', [ ':P', ':p' ] ], \ -[ 'stock_smiley-19', None ], \ -[ 'stock_smiley-2', None ], \ -[ 'stock_smiley-11', None ], \ -[ 'stock_smiley-1', [ ':)' ] ], \ -[ 'stock_smiley-3', None ], \ -[ 'stock_smiley-12', None ], \ -[ 'stock_smiley-20', None ], \ -[ 'stock_smiley-4', [ ':(' ] ], \ -[ 'stock_smiley-13', None ], \ -[ 'stock_smiley-21', None ], \ -[ 'stock_smiley-5', None ], \ -[ 'stock_smiley-14', None ], \ -[ 'stock_smiley-22', None ], \ -[ 'stock_smiley-6', None ], \ -[ 'stock_smiley-15', None ], \ -[ 'stock_smiley-23', None ], \ -[ 'stock_smiley-7', None ], \ -[ 'stock_smiley-16', None ], \ -[ 'stock_smiley-24', None ], \ -[ 'stock_smiley-8', None ], \ -[ 'stock_smiley-17', None ], \ -[ 'stock_smiley-25', None ], \ -[ 'stock_smiley-9', None ], \ -[ 'stock_smiley-18', None ], \ -[ 'stock_smiley-26', None ], \ +emoticons_table = [ \ +[ 'stock_smiley-10', [ ':P', ':p' ] ], \ +[ 'stock_smiley-19', None ], \ +[ 'stock_smiley-2', None ], \ +[ 'stock_smiley-11', None ], \ +[ 'stock_smiley-1', [ ':)' ] ], \ +[ 'stock_smiley-3', None ], \ +[ 'stock_smiley-12', None ], \ +[ 'stock_smiley-20', None ], \ +[ 'stock_smiley-4', [ ':(' ] ], \ +[ 'stock_smiley-13', None ], \ +[ 'stock_smiley-21', None ], \ +[ 'stock_smiley-5', None ], \ +[ 'stock_smiley-14', None ], \ +[ 'stock_smiley-22', None ], \ +[ 'stock_smiley-6', None ], \ +[ 'stock_smiley-15', None ], \ +[ 'stock_smiley-23', None ], \ +[ 'stock_smiley-7', None ], \ +[ 'stock_smiley-16', None ], \ +[ 'stock_smiley-24', None ], \ +[ 'stock_smiley-8', None ], \ +[ 'stock_smiley-17', None ], \ +[ 'stock_smiley-25', None ], \ +[ 'stock_smiley-9', None ], \ +[ 'stock_smiley-18', None ], \ +[ 'stock_smiley-26', None ], \ ] class Emoticons: - instance = None + instance = None - def get_instance(): - if not Emoticons.instance: - Emoticons.instance = Emoticons() - return Emoticons.instance + def get_instance(): + if not Emoticons.instance: + Emoticons.instance = Emoticons() + return Emoticons.instance - get_instance = staticmethod(get_instance) + get_instance = staticmethod(get_instance) - def __init__(self): - self._table = {} + def __init__(self): + self._table = {} - for emoticon in emoticons_table: - [ name, text_emts ] = emoticon - self.add(name, text_emts) - - def add(self, icon_name, text=None): - self._table[icon_name] = text - - def get_all(self): - return self._table.keys() - - """Replace emoticons text with the icon name. - - Parse the provided text to find emoticons (in - textual form) and replace them with their xml - representation in the form: - - """ - def replace(self, text): - for icon_name in self._table.keys(): - text_emts = self._table[icon_name] - if text_emts: - for emoticon_text in text_emts: - xml = '' - text = text.replace(emoticon_text, xml) - return text + for emoticon in emoticons_table: + [ name, text_emts ] = emoticon + self.add(name, text_emts) + + def add(self, icon_name, text=None): + self._table[icon_name] = text + + def get_all(self): + return self._table.keys() + + """Replace emoticons text with the icon name. + + Parse the provided text to find emoticons (in + textual form) and replace them with their xml + representation in the form: + + """ + def replace(self, text): + for icon_name in self._table.keys(): + text_emts = self._table[icon_name] + if text_emts: + for emoticon_text in text_emts: + xml = '' + text = text.replace(emoticon_text, xml) + return text diff --git a/sugar/chat/GroupChat.py b/sugar/chat/GroupChat.py index d7580da..a1c6b65 100644 --- a/sugar/chat/GroupChat.py +++ b/sugar/chat/GroupChat.py @@ -23,15 +23,15 @@ from sugar.presence.PresenceService import PresenceService import sugar.env class GroupChat(Chat): - def __init__(self): - Chat.__init__(self) - self._group_stream = None + def __init__(self): + Chat.__init__(self) + self._group_stream = None - def _setup_stream(self, service): - self._group_stream = Stream.new_from_service(service) - self._group_stream.set_data_listener(self._group_recv_message) - self._stream_writer = self._group_stream.new_writer() + def _setup_stream(self, service): + self._group_stream = Stream.new_from_service(service) + self._group_stream.set_data_listener(self._group_recv_message) + self._stream_writer = self._group_stream.new_writer() - def _group_recv_message(self, address, msg): - logging.debug('Group chat received from %s message %s' % (address, msg)) - self.recv_message(msg) + def _group_recv_message(self, address, msg): + logging.debug('Group chat received from %s message %s' % (address, msg)) + self.recv_message(msg) diff --git a/sugar/chat/richtext.py b/sugar/chat/richtext.py index 7a6f179..ebd8b1c 100644 --- a/sugar/chat/richtext.py +++ b/sugar/chat/richtext.py @@ -21,431 +21,431 @@ import pango import xml.sax class RichTextView(gtk.TextView): - - __gsignals__ = { - 'link-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_STRING])) - } - - def __init__(self): - gtk.TextView.__init__(self, RichTextBuffer()) - self.connect("motion-notify-event", self.__motion_notify_cb) - self.connect("button-press-event", self.__button_press_cb) - self.__hover_link = False - - def _set_hover_link(self, hover_link): - if hover_link != self.__hover_link: - self.__hover_link = hover_link - display = self.get_toplevel().get_display() - child_window = self.get_window(gtk.TEXT_WINDOW_TEXT) - - if hover_link: - cursor = gtk.gdk.Cursor(display, gtk.gdk.HAND2) - else: - cursor = gtk.gdk.Cursor(display, gtk.gdk.XTERM) - - child_window.set_cursor(cursor) - gtk.gdk.flush() - - def __iter_is_link(self, it): - item = self.get_buffer().get_tag_table().lookup("link") - if item: - return it.has_tag(item) - return False - - def __get_event_iter(self, event): - return self.get_iter_at_location(int(event.x), int(event.y)) - - def __motion_notify_cb(self, widget, event): - if event.is_hint: - event.window.get_pointer(); - - it = self.__get_event_iter(event) - if it: - hover_link = self.__iter_is_link(it) - else: - hover_link = False - - self._set_hover_link(hover_link) - - def __button_press_cb(self, widget, event): - it = self.__get_event_iter(event) - if it and self.__iter_is_link(it): - buf = self.get_buffer() - address_tag = buf.get_tag_table().lookup("object-id") - - address_end = it.copy() - address_end.backward_to_tag_toggle(address_tag) - - address_start = address_end.copy() - address_start.backward_to_tag_toggle(address_tag) - - address = buf.get_text(address_start, address_end) - self.emit("link-clicked", address) + + __gsignals__ = { + 'link-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_STRING])) + } + + def __init__(self): + gtk.TextView.__init__(self, RichTextBuffer()) + self.connect("motion-notify-event", self.__motion_notify_cb) + self.connect("button-press-event", self.__button_press_cb) + self.__hover_link = False + + def _set_hover_link(self, hover_link): + if hover_link != self.__hover_link: + self.__hover_link = hover_link + display = self.get_toplevel().get_display() + child_window = self.get_window(gtk.TEXT_WINDOW_TEXT) + + if hover_link: + cursor = gtk.gdk.Cursor(display, gtk.gdk.HAND2) + else: + cursor = gtk.gdk.Cursor(display, gtk.gdk.XTERM) + + child_window.set_cursor(cursor) + gtk.gdk.flush() + + def __iter_is_link(self, it): + item = self.get_buffer().get_tag_table().lookup("link") + if item: + return it.has_tag(item) + return False + + def __get_event_iter(self, event): + return self.get_iter_at_location(int(event.x), int(event.y)) + + def __motion_notify_cb(self, widget, event): + if event.is_hint: + event.window.get_pointer(); + + it = self.__get_event_iter(event) + if it: + hover_link = self.__iter_is_link(it) + else: + hover_link = False + + self._set_hover_link(hover_link) + + def __button_press_cb(self, widget, event): + it = self.__get_event_iter(event) + if it and self.__iter_is_link(it): + buf = self.get_buffer() + address_tag = buf.get_tag_table().lookup("object-id") + + address_end = it.copy() + address_end.backward_to_tag_toggle(address_tag) + + address_start = address_end.copy() + address_start.backward_to_tag_toggle(address_tag) + + address = buf.get_text(address_start, address_end) + self.emit("link-clicked", address) class RichTextBuffer(gtk.TextBuffer): - def __init__(self): - gtk.TextBuffer.__init__(self) - - self.connect_after("insert-text", self.__insert_text_cb) - - self.__create_tags() - self.active_tags = [] - - def append_link(self, title, address): - it = self.get_iter_at_mark(self.get_insert()) - self.insert_with_tags_by_name(it, address, "link", "object-id") - self.insert_with_tags_by_name(it, title, "link") - - def append_icon(self, name, it = None): - if not it: - it = self.get_iter_at_mark(self.get_insert()) - - self.insert_with_tags_by_name(it, name, "icon", "object-id") - icon_theme = gtk.icon_theme_get_default() - try: - pixbuf = icon_theme.load_icon(name, 16, 0) - self.insert_pixbuf(it, pixbuf) - except gobject.GError: - pass - - def apply_tag(self, tag_name): - self.active_tags.append(tag_name) - - bounds = self.get_selection_bounds() - if bounds: - [start, end] = bounds - self.apply_tag_by_name(tag_name, start, end) - - def unapply_tag(self, tag_name): - self.active_tags.remove(tag_name) - - bounds = self.get_selection_bounds() - if bounds: - [start, end] = bounds - self.remove_tag_by_name(tag_name, start, end) - - def __create_tags(self): - tag = self.create_tag("icon") - - tag = self.create_tag("link") - tag.set_property("underline", pango.UNDERLINE_SINGLE) - tag.set_property("foreground", "#0000FF") - - tag = self.create_tag("object-id") - tag.set_property("invisible", True) - - tag = self.create_tag("bold") - tag.set_property("weight", pango.WEIGHT_BOLD) - - tag = self.create_tag("italic") - tag.set_property("style", pango.STYLE_ITALIC) - - tag = self.create_tag("font-size-xx-small") - tag.set_property("scale", pango.SCALE_XX_SMALL) - - tag = self.create_tag("font-size-x-small") - tag.set_property("scale", pango.SCALE_X_SMALL) - - tag = self.create_tag("font-size-small") - tag.set_property("scale", pango.SCALE_SMALL) - - tag = self.create_tag("font-size-large") - tag.set_property("scale", pango.SCALE_LARGE) - - tag = self.create_tag("font-size-x-large") - tag.set_property("scale", pango.SCALE_X_LARGE) - - tag = self.create_tag("font-size-xx-large") - tag.set_property("scale", pango.SCALE_XX_LARGE) - - def __insert_text_cb(self, widget, pos, text, length): - for tag in self.active_tags: - pos_end = pos.copy() - pos_end.backward_chars(length) - self.apply_tag_by_name(tag, pos, pos_end) - + def __init__(self): + gtk.TextBuffer.__init__(self) + + self.connect_after("insert-text", self.__insert_text_cb) + + self.__create_tags() + self.active_tags = [] + + def append_link(self, title, address): + it = self.get_iter_at_mark(self.get_insert()) + self.insert_with_tags_by_name(it, address, "link", "object-id") + self.insert_with_tags_by_name(it, title, "link") + + def append_icon(self, name, it = None): + if not it: + it = self.get_iter_at_mark(self.get_insert()) + + self.insert_with_tags_by_name(it, name, "icon", "object-id") + icon_theme = gtk.icon_theme_get_default() + try: + pixbuf = icon_theme.load_icon(name, 16, 0) + self.insert_pixbuf(it, pixbuf) + except gobject.GError: + pass + + def apply_tag(self, tag_name): + self.active_tags.append(tag_name) + + bounds = self.get_selection_bounds() + if bounds: + [start, end] = bounds + self.apply_tag_by_name(tag_name, start, end) + + def unapply_tag(self, tag_name): + self.active_tags.remove(tag_name) + + bounds = self.get_selection_bounds() + if bounds: + [start, end] = bounds + self.remove_tag_by_name(tag_name, start, end) + + def __create_tags(self): + tag = self.create_tag("icon") + + tag = self.create_tag("link") + tag.set_property("underline", pango.UNDERLINE_SINGLE) + tag.set_property("foreground", "#0000FF") + + tag = self.create_tag("object-id") + tag.set_property("invisible", True) + + tag = self.create_tag("bold") + tag.set_property("weight", pango.WEIGHT_BOLD) + + tag = self.create_tag("italic") + tag.set_property("style", pango.STYLE_ITALIC) + + tag = self.create_tag("font-size-xx-small") + tag.set_property("scale", pango.SCALE_XX_SMALL) + + tag = self.create_tag("font-size-x-small") + tag.set_property("scale", pango.SCALE_X_SMALL) + + tag = self.create_tag("font-size-small") + tag.set_property("scale", pango.SCALE_SMALL) + + tag = self.create_tag("font-size-large") + tag.set_property("scale", pango.SCALE_LARGE) + + tag = self.create_tag("font-size-x-large") + tag.set_property("scale", pango.SCALE_X_LARGE) + + tag = self.create_tag("font-size-xx-large") + tag.set_property("scale", pango.SCALE_XX_LARGE) + + def __insert_text_cb(self, widget, pos, text, length): + for tag in self.active_tags: + pos_end = pos.copy() + pos_end.backward_chars(length) + self.apply_tag_by_name(tag, pos, pos_end) + class RichTextToolbox(gtk.HBox): - def __init__(self, buf): - gtk.HBox.__init__(self, False, 6) - - self.buf = buf - - self._font_size = "normal" - self._font_scales = [ "xx-small", "x-small", "small", \ - "normal", \ - "large", "x-large", "xx-large" ] - - image = gtk.Image() - image.set_from_stock(gtk.STOCK_BOLD, gtk.ICON_SIZE_SMALL_TOOLBAR) - - item = gtk.ToggleButton() - item.set_image(image) - item.connect("toggled", self.__toggle_style_cb, "bold") - item.unset_flags(gtk.CAN_FOCUS) - self.pack_start(item, False) - item.show() - - image.show() - - image = gtk.Image() - image.set_from_stock(gtk.STOCK_ITALIC, gtk.ICON_SIZE_SMALL_TOOLBAR) - - item = gtk.ToggleButton() - item.set_image(image) - item.unset_flags(gtk.CAN_FOCUS) - item.connect("toggled", self.__toggle_style_cb, "italic") - self.pack_start(item, False) - item.show() - - image = gtk.Image() - image.set_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_SMALL_TOOLBAR) - - self._font_size_up = gtk.Button() - self._font_size_up.set_image(image) - self._font_size_up.unset_flags(gtk.CAN_FOCUS) - self._font_size_up.connect("clicked", self.__font_size_up_cb) - self.pack_start(self._font_size_up, False) - self._font_size_up.show() - - image.show() - - image = gtk.Image() - image.set_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_SMALL_TOOLBAR) - - self._font_size_down = gtk.Button() - self._font_size_down.set_image(image) - self._font_size_down.unset_flags(gtk.CAN_FOCUS) - self._font_size_down.connect("clicked", self.__font_size_down_cb) - self.pack_start(self._font_size_down, False) - self._font_size_down.show() - - image.show() - - def _get_font_size_index(self): - return self._font_scales.index(self._font_size); - - def __toggle_style_cb(self, toggle, tag_name): - if toggle.get_active(): - self.buf.apply_tag(tag_name) - else: - self.buf.unapply_tag(tag_name) - - def _set_font_size(self, font_size): - if self._font_size != "normal": - self.buf.unapply_tag("font-size-" + self._font_size) - if font_size != "normal": - self.buf.apply_tag("font-size-" + font_size) - - self._font_size = font_size - - can_up = self._get_font_size_index() < len(self._font_scales) - 1 - can_down = self._get_font_size_index() > 0 - self._font_size_up.set_sensitive(can_up) - self._font_size_down.set_sensitive(can_down) - - def __font_size_up_cb(self, button): - index = self._get_font_size_index() - if index + 1 < len(self._font_scales): - self._set_font_size(self._font_scales[index + 1]) - - def __font_size_down_cb(self, button): - index = self._get_font_size_index() - if index > 0: - self._set_font_size(self._font_scales[index - 1]) - + def __init__(self, buf): + gtk.HBox.__init__(self, False, 6) + + self.buf = buf + + self._font_size = "normal" + self._font_scales = [ "xx-small", "x-small", "small", \ + "normal", \ + "large", "x-large", "xx-large" ] + + image = gtk.Image() + image.set_from_stock(gtk.STOCK_BOLD, gtk.ICON_SIZE_SMALL_TOOLBAR) + + item = gtk.ToggleButton() + item.set_image(image) + item.connect("toggled", self.__toggle_style_cb, "bold") + item.unset_flags(gtk.CAN_FOCUS) + self.pack_start(item, False) + item.show() + + image.show() + + image = gtk.Image() + image.set_from_stock(gtk.STOCK_ITALIC, gtk.ICON_SIZE_SMALL_TOOLBAR) + + item = gtk.ToggleButton() + item.set_image(image) + item.unset_flags(gtk.CAN_FOCUS) + item.connect("toggled", self.__toggle_style_cb, "italic") + self.pack_start(item, False) + item.show() + + image = gtk.Image() + image.set_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_SMALL_TOOLBAR) + + self._font_size_up = gtk.Button() + self._font_size_up.set_image(image) + self._font_size_up.unset_flags(gtk.CAN_FOCUS) + self._font_size_up.connect("clicked", self.__font_size_up_cb) + self.pack_start(self._font_size_up, False) + self._font_size_up.show() + + image.show() + + image = gtk.Image() + image.set_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_SMALL_TOOLBAR) + + self._font_size_down = gtk.Button() + self._font_size_down.set_image(image) + self._font_size_down.unset_flags(gtk.CAN_FOCUS) + self._font_size_down.connect("clicked", self.__font_size_down_cb) + self.pack_start(self._font_size_down, False) + self._font_size_down.show() + + image.show() + + def _get_font_size_index(self): + return self._font_scales.index(self._font_size); + + def __toggle_style_cb(self, toggle, tag_name): + if toggle.get_active(): + self.buf.apply_tag(tag_name) + else: + self.buf.unapply_tag(tag_name) + + def _set_font_size(self, font_size): + if self._font_size != "normal": + self.buf.unapply_tag("font-size-" + self._font_size) + if font_size != "normal": + self.buf.apply_tag("font-size-" + font_size) + + self._font_size = font_size + + can_up = self._get_font_size_index() < len(self._font_scales) - 1 + can_down = self._get_font_size_index() > 0 + self._font_size_up.set_sensitive(can_up) + self._font_size_down.set_sensitive(can_down) + + def __font_size_up_cb(self, button): + index = self._get_font_size_index() + if index + 1 < len(self._font_scales): + self._set_font_size(self._font_scales[index + 1]) + + def __font_size_down_cb(self, button): + index = self._get_font_size_index() + if index > 0: + self._set_font_size(self._font_scales[index - 1]) + class RichTextHandler(xml.sax.handler.ContentHandler): - def __init__(self, serializer, buf): - xml.sax.handler.ContentHandler.__init__(self) - self.buf = buf - self.serializer = serializer - self.tags = [] - self._in_richtext = False - self._done = False - - def startElement(self, name, attrs): - # Look for, and only start parsing after 'richtext' - if not self._in_richtext and name == "richtext": - self._in_richtext = True - if not self._in_richtext: - return - - if name != "richtext": - tag = self.serializer.deserialize_element(name, attrs) - self.tags.append(tag) - if name == "link": - self.href = attrs['href'] - elif name == "icon": - self.buf.append_icon(attrs['name'], self.buf.get_end_iter()) + def __init__(self, serializer, buf): + xml.sax.handler.ContentHandler.__init__(self) + self.buf = buf + self.serializer = serializer + self.tags = [] + self._in_richtext = False + self._done = False + + def startElement(self, name, attrs): + # Look for, and only start parsing after 'richtext' + if not self._in_richtext and name == "richtext": + self._in_richtext = True + if not self._in_richtext: + return + + if name != "richtext": + tag = self.serializer.deserialize_element(name, attrs) + self.tags.append(tag) + if name == "link": + self.href = attrs['href'] + elif name == "icon": + self.buf.append_icon(attrs['name'], self.buf.get_end_iter()) - def characters(self, data): - start_it = it = self.buf.get_end_iter() - mark = self.buf.create_mark(None, start_it, True) - self.buf.insert(it, data) - start_it = self.buf.get_iter_at_mark(mark) - - for tag in self.tags: - self.buf.apply_tag_by_name(tag, start_it, it) - if tag == "link": - self.buf.insert_with_tags_by_name(start_it, self.href, - "link", "object-id") - - def endElement(self, name): - if not self._done and self._in_richtext: - if name != "richtext": - self.tags.pop() - if name == "richtext": - self._done = True - self._in_richtext = False + def characters(self, data): + start_it = it = self.buf.get_end_iter() + mark = self.buf.create_mark(None, start_it, True) + self.buf.insert(it, data) + start_it = self.buf.get_iter_at_mark(mark) + + for tag in self.tags: + self.buf.apply_tag_by_name(tag, start_it, it) + if tag == "link": + self.buf.insert_with_tags_by_name(start_it, self.href, + "link", "object-id") + + def endElement(self, name): + if not self._done and self._in_richtext: + if name != "richtext": + self.tags.pop() + if name == "richtext": + self._done = True + self._in_richtext = False class RichTextSerializer: - def __init__(self): - self._open_tags = [] - - def deserialize_element(self, el_name, attributes): - if el_name == "bold": - return "bold" - elif el_name == "italic": - return "italic" - elif el_name == "font": - return "font-size-" + attributes["size"] - elif el_name == "link": - return "link" - elif el_name == "icon": - return "icon" - else: - return None - - def _parse_object_id(self, it): - object_id_tag = self.buf.get_tag_table().lookup("object-id") - end = it.copy() - end.forward_to_tag_toggle(object_id_tag) - return self.buf.get_text(it, end) - - def serialize_tag_start(self, tag, it): - name = tag.get_property("name") - if name == "bold": - return "" - elif name == "italic": - return "" - elif name == "link": - address = self._parse_object_id(it) - return "" - elif name == "icon": - name = self._parse_object_id(it) - return "" - elif name == "object-id": - return "" - elif name.startswith("font-size-"): - tag_name = name.replace("font-size-", "", 1) - return "" - else: - return "" - - def serialize_tag_end(self, tag): - name = tag.get_property("name") - if name == "bold": - return "" - elif name == "italic": - return "" - elif name == "link": - return "" - elif name == "icon": - return "" - elif name == "object-id": - return "" - elif name.startswith("font-size-"): - return "" - else: - return "" - - def serialize(self, buf): - self.buf = buf - - res = "" - - next_it = buf.get_start_iter() - while not next_it.is_end(): - it = next_it.copy() - if not next_it.forward_to_tag_toggle(None): - next_it = buf.get_end_iter() - - tags_to_reopen = [] - - for tag in it.get_toggled_tags(False): - while 1: - open_tag = self._open_tags.pop() - res += self.serialize_tag_end(tag) - if open_tag == tag: - break - tags_to_reopen.append(open_tag) - - for tag in tags_to_reopen: - self._open_tags.append(tag) - res += self.serialize_tag_start(tag, it) - - for tag in it.get_toggled_tags(True): - self._open_tags.append(tag) - res += self.serialize_tag_start(tag, it) - - res += buf.get_text(it, next_it, False) - - if next_it.is_end(): - self._open_tags.reverse() - for tag in self._open_tags: - res += self.serialize_tag_end(tag) - - res += "" - - return res - - def deserialize(self, xml_string, buf): - parser = xml.sax.make_parser() - handler = RichTextHandler(self, buf) - parser.setContentHandler(handler) - parser.feed(xml_string) - parser.close() + def __init__(self): + self._open_tags = [] + + def deserialize_element(self, el_name, attributes): + if el_name == "bold": + return "bold" + elif el_name == "italic": + return "italic" + elif el_name == "font": + return "font-size-" + attributes["size"] + elif el_name == "link": + return "link" + elif el_name == "icon": + return "icon" + else: + return None + + def _parse_object_id(self, it): + object_id_tag = self.buf.get_tag_table().lookup("object-id") + end = it.copy() + end.forward_to_tag_toggle(object_id_tag) + return self.buf.get_text(it, end) + + def serialize_tag_start(self, tag, it): + name = tag.get_property("name") + if name == "bold": + return "" + elif name == "italic": + return "" + elif name == "link": + address = self._parse_object_id(it) + return "" + elif name == "icon": + name = self._parse_object_id(it) + return "" + elif name == "object-id": + return "" + elif name.startswith("font-size-"): + tag_name = name.replace("font-size-", "", 1) + return "" + else: + return "" + + def serialize_tag_end(self, tag): + name = tag.get_property("name") + if name == "bold": + return "" + elif name == "italic": + return "" + elif name == "link": + return "" + elif name == "icon": + return "" + elif name == "object-id": + return "" + elif name.startswith("font-size-"): + return "" + else: + return "" + + def serialize(self, buf): + self.buf = buf + + res = "" + + next_it = buf.get_start_iter() + while not next_it.is_end(): + it = next_it.copy() + if not next_it.forward_to_tag_toggle(None): + next_it = buf.get_end_iter() + + tags_to_reopen = [] + + for tag in it.get_toggled_tags(False): + while 1: + open_tag = self._open_tags.pop() + res += self.serialize_tag_end(tag) + if open_tag == tag: + break + tags_to_reopen.append(open_tag) + + for tag in tags_to_reopen: + self._open_tags.append(tag) + res += self.serialize_tag_start(tag, it) + + for tag in it.get_toggled_tags(True): + self._open_tags.append(tag) + res += self.serialize_tag_start(tag, it) + + res += buf.get_text(it, next_it, False) + + if next_it.is_end(): + self._open_tags.reverse() + for tag in self._open_tags: + res += self.serialize_tag_end(tag) + + res += "" + + return res + + def deserialize(self, xml_string, buf): + parser = xml.sax.make_parser() + handler = RichTextHandler(self, buf) + parser.setContentHandler(handler) + parser.feed(xml_string) + parser.close() def test_quit(w, rb): - print RichTextSerializer().serialize(rb) - gtk.main_quit() - + print RichTextSerializer().serialize(rb) + gtk.main_quit() + def link_clicked(v, address): - print "Link clicked " + address + print "Link clicked " + address if __name__ == "__main__": - window = gtk.Window() - window.set_default_size(400, 300) - - vbox = gtk.VBox() - - view = RichTextView() - view.connect("link-clicked", link_clicked) - vbox.pack_start(view) - view.show() - - rich_buf = view.get_buffer() - - test_xml = "" - - test_xml += "Testone\n" - test_xml += "Test two" - test_xml += "Test three" - test_xml += "Test link" - test_xml += "" - test_xml += "" - - RichTextSerializer().deserialize(test_xml, rich_buf) - - toolbar = RichTextToolbar(rich_buf) - vbox.pack_start(toolbar, False) - toolbar.show() - - window.add(vbox) - vbox.show() - - window.show() - - window.connect("destroy", test_quit, rich_buf) - - gtk.main() + window = gtk.Window() + window.set_default_size(400, 300) + + vbox = gtk.VBox() + + view = RichTextView() + view.connect("link-clicked", link_clicked) + vbox.pack_start(view) + view.show() + + rich_buf = view.get_buffer() + + test_xml = "" + + test_xml += "Testone\n" + test_xml += "Test two" + test_xml += "Test three" + test_xml += "Test link" + test_xml += "" + test_xml += "" + + RichTextSerializer().deserialize(test_xml, rich_buf) + + toolbar = RichTextToolbar(rich_buf) + vbox.pack_start(toolbar, False) + toolbar.show() + + window.add(vbox) + vbox.show() + + window.show() + + window.connect("destroy", test_quit, rich_buf) + + gtk.main() diff --git a/sugar/chat/sketchpad/Sketch.py b/sugar/chat/sketchpad/Sketch.py index 0ddfb3c..ac0a865 100644 --- a/sugar/chat/sketchpad/Sketch.py +++ b/sugar/chat/sketchpad/Sketch.py @@ -18,37 +18,37 @@ from SVGdraw import path class Sketch: - def __init__(self, rgb): - self._points = [] - self._rgb = (float(rgb[0]), float(rgb[1]), float(rgb[2])) - - def add_point(self, x, y): - self._points.append((x, y)) + def __init__(self, rgb): + self._points = [] + self._rgb = (float(rgb[0]), float(rgb[1]), float(rgb[2])) + + def add_point(self, x, y): + self._points.append((x, y)) - def get_points(self): - return self._points - - def draw(self, ctx): - start = True - for (x, y) in self._points: - if start: - ctx.move_to(x, y) - start = False - else: - ctx.line_to(x, y) - ctx.set_source_rgb(self._rgb[0], self._rgb[1], self._rgb[2]) - ctx.stroke() - - def draw_to_svg(self): - i = 0 - for (x, y) in self._points: - coords = str(x) + ' ' + str(y) + ' ' - if i == 0: - path_data = 'M ' + coords - elif i == 1: - path_data += 'L ' + coords - else: - path_data += coords - i += 1 - color = "#%02X%02X%02X" % (255 * self._rgb[0], 255 * self._rgb[1], 255 * self._rgb[2]) - return path(path_data, fill = 'none', stroke = color) + def get_points(self): + return self._points + + def draw(self, ctx): + start = True + for (x, y) in self._points: + if start: + ctx.move_to(x, y) + start = False + else: + ctx.line_to(x, y) + ctx.set_source_rgb(self._rgb[0], self._rgb[1], self._rgb[2]) + ctx.stroke() + + def draw_to_svg(self): + i = 0 + for (x, y) in self._points: + coords = str(x) + ' ' + str(y) + ' ' + if i == 0: + path_data = 'M ' + coords + elif i == 1: + path_data += 'L ' + coords + else: + path_data += coords + i += 1 + color = "#%02X%02X%02X" % (255 * self._rgb[0], 255 * self._rgb[1], 255 * self._rgb[2]) + return path(path_data, fill = 'none', stroke = color) diff --git a/sugar/chat/sketchpad/SketchPad.py b/sugar/chat/sketchpad/SketchPad.py index 33327f0..e15e435 100644 --- a/sugar/chat/sketchpad/SketchPad.py +++ b/sugar/chat/sketchpad/SketchPad.py @@ -23,101 +23,101 @@ from SVGdraw import drawing from SVGdraw import svg class SketchPad(gtk.DrawingArea): - __gsignals__ = { - 'new-user-sketch':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } + __gsignals__ = { + 'new-user-sketch':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } - def __init__(self, bgcolor=(0.6, 1, 0.4)): - gtk.DrawingArea.__init__(self) + def __init__(self, bgcolor=(0.6, 1, 0.4)): + gtk.DrawingArea.__init__(self) - self._active_sketch = None - self._rgb = (0.0, 0.0, 0.0) - self._bgcolor = bgcolor - self._sketches = [] + self._active_sketch = None + self._rgb = (0.0, 0.0, 0.0) + self._bgcolor = bgcolor + self._sketches = [] - self.add_events(gtk.gdk.BUTTON_PRESS_MASK | - gtk.gdk.BUTTON_RELEASE_MASK | - gtk.gdk.BUTTON1_MOTION_MASK) - self.connect("button-press-event", self._button_press_cb) - self.connect("button-release-event", self._button_release_cb) - self.connect("motion-notify-event", self._motion_notify_cb) - self.connect('expose_event', self.expose) - - def clear(self): - self._sketches = [] - self.window.invalidate_rect(None, False) + self.add_events(gtk.gdk.BUTTON_PRESS_MASK | + gtk.gdk.BUTTON_RELEASE_MASK | + gtk.gdk.BUTTON1_MOTION_MASK) + self.connect("button-press-event", self._button_press_cb) + self.connect("button-release-event", self._button_release_cb) + self.connect("motion-notify-event", self._motion_notify_cb) + self.connect('expose_event', self.expose) + + def clear(self): + self._sketches = [] + self.window.invalidate_rect(None, False) - def expose(self, widget, event): - """Draw the background of the sketchpad.""" - rect = self.get_allocation() - ctx = widget.window.cairo_create() - - ctx.set_source_rgb(self._bgcolor[0], self._bgcolor[1], self._bgcolor[2]) - ctx.rectangle(0, 0, rect.width, rect.height) - ctx.fill_preserve() - - ctx.set_source_rgb(0, 0.3, 0.2) - ctx.stroke() - - for sketch in self._sketches: - sketch.draw(ctx) - - return False + def expose(self, widget, event): + """Draw the background of the sketchpad.""" + rect = self.get_allocation() + ctx = widget.window.cairo_create() + + ctx.set_source_rgb(self._bgcolor[0], self._bgcolor[1], self._bgcolor[2]) + ctx.rectangle(0, 0, rect.width, rect.height) + ctx.fill_preserve() + + ctx.set_source_rgb(0, 0.3, 0.2) + ctx.stroke() + + for sketch in self._sketches: + sketch.draw(ctx) + + return False - def set_color(self, color): - """Sets the current drawing color of the sketchpad. - color agument should be 3-item tuple of rgb values between 0 and 1.""" - self._rgb = color + def set_color(self, color): + """Sets the current drawing color of the sketchpad. + color agument should be 3-item tuple of rgb values between 0 and 1.""" + self._rgb = color - def add_sketch(self, sketch): - """Add a sketch to the the pad. Mostly for subclasses and clients.""" - self._sketches.append(sketch) - self.window.invalidate_rect(None, False) + def add_sketch(self, sketch): + """Add a sketch to the the pad. Mostly for subclasses and clients.""" + self._sketches.append(sketch) + self.window.invalidate_rect(None, False) - def add_point(self, event): - if not self._active_sketch: - return - self._active_sketch.add_point(event.x, event.y) - self.window.invalidate_rect(None, False) - - def _button_press_cb(self, widget, event): - self._active_sketch = Sketch(self._rgb) - self._sketches.append(self._active_sketch) - self.add_point(event) - - def _button_release_cb(self, widget, event): - self.add_point(event) - self.emit('new-user-sketch', self._active_sketch) - self._active_sketch = None - - def _motion_notify_cb(self, widget, event): - self.add_point(event) - - def to_svg(self): - """Return a string containing an SVG representation of this sketch.""" - d = drawing() - s = svg() - for sketch in self._sketches: - s.addElement(sketch.draw_to_svg()) - d.setSVG(s) - return d.toXml() + def add_point(self, event): + if not self._active_sketch: + return + self._active_sketch.add_point(event.x, event.y) + self.window.invalidate_rect(None, False) + + def _button_press_cb(self, widget, event): + self._active_sketch = Sketch(self._rgb) + self._sketches.append(self._active_sketch) + self.add_point(event) + + def _button_release_cb(self, widget, event): + self.add_point(event) + self.emit('new-user-sketch', self._active_sketch) + self._active_sketch = None + + def _motion_notify_cb(self, widget, event): + self.add_point(event) + + def to_svg(self): + """Return a string containing an SVG representation of this sketch.""" + d = drawing() + s = svg() + for sketch in self._sketches: + s.addElement(sketch.draw_to_svg()) + d.setSVG(s) + return d.toXml() def test_quit(w, skpad): - print skpad.to_svg() - gtk.main_quit() + print skpad.to_svg() + gtk.main_quit() if __name__ == "__main__": - window = gtk.Window() - window.set_default_size(400, 300) - window.connect("destroy", lambda w: gtk.main_quit()) + window = gtk.Window() + window.set_default_size(400, 300) + window.connect("destroy", lambda w: gtk.main_quit()) - sketchpad = SketchPad() - window.add(sketchpad) - sketchpad.show() - - window.show() - - window.connect("destroy", test_quit, sketchpad) + sketchpad = SketchPad() + window.add(sketchpad) + sketchpad.show() + + window.show() + + window.connect("destroy", test_quit, sketchpad) - gtk.main() + gtk.main() diff --git a/sugar/chat/sketchpad/Toolbox.py b/sugar/chat/sketchpad/Toolbox.py index 0ae4e29..b142f98 100644 --- a/sugar/chat/sketchpad/Toolbox.py +++ b/sugar/chat/sketchpad/Toolbox.py @@ -19,59 +19,59 @@ import gtk import gobject class ColorButton(gtk.RadioButton): - def __init__(self, group, rgb): - gtk.RadioButton.__init__(self, group) - - self._rgb = rgb - - self.set_mode(False) - self.set_relief(gtk.RELIEF_NONE) - - drawing_area = gtk.DrawingArea() - drawing_area.set_size_request(24, 24) - drawing_area.connect_after('expose_event', self.expose) - self.add(drawing_area) - drawing_area.show() + def __init__(self, group, rgb): + gtk.RadioButton.__init__(self, group) + + self._rgb = rgb + + self.set_mode(False) + self.set_relief(gtk.RELIEF_NONE) + + drawing_area = gtk.DrawingArea() + drawing_area.set_size_request(24, 24) + drawing_area.connect_after('expose_event', self.expose) + self.add(drawing_area) + drawing_area.show() - def color(self): - return self._rgb + def color(self): + return self._rgb - def expose(self, widget, event): - rect = widget.get_allocation() - ctx = widget.window.cairo_create() + def expose(self, widget, event): + rect = widget.get_allocation() + ctx = widget.window.cairo_create() - ctx.set_source_rgb(self._rgb[0], self._rgb[1] , self._rgb[2]) - ctx.rectangle(4, 4, rect.width - 8, rect.height - 8) - ctx.fill() - - return False + ctx.set_source_rgb(self._rgb[0], self._rgb[1] , self._rgb[2]) + ctx.rectangle(4, 4, rect.width - 8, rect.height - 8) + ctx.fill() + + return False class Toolbox(gtk.HBox): - __gsignals__ = { - 'color-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } + __gsignals__ = { + 'color-selected': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } - def __init__(self): - gtk.HBox.__init__(self, False, 6) - - self._colors_group = None - - self._add_color([0, 0, 0]) - self._add_color([1, 0, 0]) - self._add_color([0, 1, 0]) - self._add_color([0, 0, 1]) - - def _add_color(self, rgb): - color = ColorButton(self._colors_group, rgb) - color.unset_flags(gtk.CAN_FOCUS) - color.connect('clicked', self.__color_clicked_cb, rgb) - self.pack_start(color, False) + def __init__(self): + gtk.HBox.__init__(self, False, 6) + + self._colors_group = None + + self._add_color([0, 0, 0]) + self._add_color([1, 0, 0]) + self._add_color([0, 1, 0]) + self._add_color([0, 0, 1]) + + def _add_color(self, rgb): + color = ColorButton(self._colors_group, rgb) + color.unset_flags(gtk.CAN_FOCUS) + color.connect('clicked', self.__color_clicked_cb, rgb) + self.pack_start(color, False) - if self._colors_group == None: - self._colors_group = color + if self._colors_group == None: + self._colors_group = color - color.show() + color.show() - def __color_clicked_cb(self, button, rgb): - self.emit("color-selected", button.color()) + def __color_clicked_cb(self, button, rgb): + self.emit("color-selected", button.color()) diff --git a/sugar/clipboard/ClipboardService.py b/sugar/clipboard/ClipboardService.py index 7c9dd1f..254d6b2 100644 --- a/sugar/clipboard/ClipboardService.py +++ b/sugar/clipboard/ClipboardService.py @@ -7,69 +7,69 @@ DBUS_PATH = "/org/laptop/Clipboard" class ClipboardService(gobject.GObject): - __gsignals__ = { - 'object-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str, str, str])), - 'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str])), - 'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([str, int])), - } - - def __init__(self): - gobject.GObject.__init__(self) - - self._dbus_service = None - bus = dbus.SessionBus() - bus.add_signal_receiver(self._name_owner_changed_cb, - signal_name="NameOwnerChanged", - dbus_interface="org.freedesktop.DBus") - # Try to register to ClipboardService, if we fail, we'll try later. - try: - self._connect_clipboard_signals() - except dbus.DBusException, exception: - pass - - def _connect_clipboard_signals(self): - bus = dbus.SessionBus() - proxy_obj = bus.get_object(DBUS_SERVICE, DBUS_PATH) - self._dbus_service = dbus.Interface(proxy_obj, DBUS_SERVICE) - self._dbus_service.connect_to_signal('object_added', - self._object_added_cb) - self._dbus_service.connect_to_signal('object_deleted', - self._object_deleted_cb) - self._dbus_service.connect_to_signal('object_state_changed', - self._object_state_changed_cb) + __gsignals__ = { + 'object-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([str, str, str])), + 'object-deleted': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([str])), + 'object-state-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([str, int])), + } + + def __init__(self): + gobject.GObject.__init__(self) + + self._dbus_service = None + bus = dbus.SessionBus() + bus.add_signal_receiver(self._name_owner_changed_cb, + signal_name="NameOwnerChanged", + dbus_interface="org.freedesktop.DBus") + # Try to register to ClipboardService, if we fail, we'll try later. + try: + self._connect_clipboard_signals() + except dbus.DBusException, exception: + pass + + def _connect_clipboard_signals(self): + bus = dbus.SessionBus() + proxy_obj = bus.get_object(DBUS_SERVICE, DBUS_PATH) + self._dbus_service = dbus.Interface(proxy_obj, DBUS_SERVICE) + self._dbus_service.connect_to_signal('object_added', + self._object_added_cb) + self._dbus_service.connect_to_signal('object_deleted', + self._object_deleted_cb) + self._dbus_service.connect_to_signal('object_state_changed', + self._object_state_changed_cb) - def _name_owner_changed_cb(self, name, old, new): - if name != DBUS_SERVICE: - return - - if (not old and not len(old)) and (new and len(new)): - # ClipboardService started up - self._connect_clipboard_signals() - - def _object_added_cb(self, name, mimeType, fileName): - self.emit('object-added', name, mimeType, fileName) + def _name_owner_changed_cb(self, name, old, new): + if name != DBUS_SERVICE: + return + + if (not old and not len(old)) and (new and len(new)): + # ClipboardService started up + self._connect_clipboard_signals() + + def _object_added_cb(self, name, mimeType, fileName): + self.emit('object-added', name, mimeType, fileName) - def _object_deleted_cb(self, fileName): - self.emit('object-deleted', fileName) + def _object_deleted_cb(self, fileName): + self.emit('object-deleted', fileName) - def _object_state_changed_cb(self, fileName, percent): - self.emit('object-state-changed', fileName, percent) - - def add_object(self, name, mimeType, fileName): - self._dbus_service.add_object(name, mimeType, fileName) + def _object_state_changed_cb(self, fileName, percent): + self.emit('object-state-changed', fileName, percent) + + def add_object(self, name, mimeType, fileName): + self._dbus_service.add_object(name, mimeType, fileName) - def delete_object(self, fileName): - self._dbus_service.delete_object(fileName) - - def set_object_state(self, fileName, percent): - self._dbus_service.set_object_state(fileName, percent) + def delete_object(self, fileName): + self._dbus_service.delete_object(fileName) + + def set_object_state(self, fileName, percent): + self._dbus_service.set_object_state(fileName, percent) _clipboard_service = None def get_instance(): - global _clipboard_service - if not _clipboard_service: - _clipboard_service = ClipboardService() - return _clipboard_service + global _clipboard_service + if not _clipboard_service: + _clipboard_service = ClipboardService() + return _clipboard_service diff --git a/sugar/emulator.py b/sugar/emulator.py index 638029e..b37dae6 100644 --- a/sugar/emulator.py +++ b/sugar/emulator.py @@ -24,108 +24,108 @@ import gobject from sugar import env def get_display_number(): - """Find a free display number trying to connect to 6000+ ports""" - retries = 20 - display_number = 1 - display_is_free = False - - while not display_is_free and retries > 0: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - try: - s.connect(('127.0.0.1', 6000 + display_number)) - s.close() - - display_number += 1 - retries -= 1 - except: - display_is_free = True - - if display_is_free: - return display_number - else: - logging.error('Cannot find a free display.') - sys.exit(0) + """Find a free display number trying to connect to 6000+ ports""" + retries = 20 + display_number = 1 + display_is_free = False + + while not display_is_free and retries > 0: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + s.connect(('127.0.0.1', 6000 + display_number)) + s.close() + + display_number += 1 + retries -= 1 + except: + display_is_free = True + + if display_is_free: + return display_number + else: + logging.error('Cannot find a free display.') + sys.exit(0) class Process: - """Object representing one of the session processes""" - - def __init__(self, command): - self._command = command - - def get_name(self): - return self._command - - def start(self, standard_output=False): - args = self._command.split() - flags = gobject.SPAWN_SEARCH_PATH - result = gobject.spawn_async(args, flags=flags, - standard_output=standard_output) - self.pid = result[0] - self._stdout = result[2] + """Object representing one of the session processes""" + + def __init__(self, command): + self._command = command + + def get_name(self): + return self._command + + def start(self, standard_output=False): + args = self._command.split() + flags = gobject.SPAWN_SEARCH_PATH + result = gobject.spawn_async(args, flags=flags, + standard_output=standard_output) + self.pid = result[0] + self._stdout = result[2] class MatchboxProcess(Process): - def __init__(self): - kbd_config = os.path.join(env.get_data_dir(), 'kbdconfig') - options = '-kbdconfig %s ' % kbd_config + def __init__(self): + kbd_config = os.path.join(env.get_data_dir(), 'kbdconfig') + options = '-kbdconfig %s ' % kbd_config - options += '-use_titlebar no ' - options += '-theme olpc ' + options += '-use_titlebar no ' + options += '-theme olpc ' - command = 'matchbox-window-manager %s ' % options - Process.__init__(self, command) - - def get_name(self): - return 'Matchbox' + command = 'matchbox-window-manager %s ' % options + Process.__init__(self, command) + + def get_name(self): + return 'Matchbox' class XephyrProcess(Process): - def __init__(self, fullscreen): - self._display = get_display_number() - cmd = 'Xephyr :%d -ac ' % (self._display) - if fullscreen: - cmd += '-fullscreen ' - else: - cmd += '-screen 800x600 ' - Process.__init__(self, cmd) - - def get_name(self): - return 'Xephyr' - - def start(self): - Process.start(self) - os.environ['DISPLAY'] = ":%d" % (self._display) - os.environ['SUGAR_XEPHYR_PID'] = '%d' % self.pid + def __init__(self, fullscreen): + self._display = get_display_number() + cmd = 'Xephyr :%d -ac ' % (self._display) + if fullscreen: + cmd += '-fullscreen ' + else: + cmd += '-screen 800x600 ' + Process.__init__(self, cmd) + + def get_name(self): + return 'Xephyr' + + def start(self): + Process.start(self) + os.environ['DISPLAY'] = ":%d" % (self._display) + os.environ['SUGAR_XEPHYR_PID'] = '%d' % self.pid class XnestProcess(Process): - def __init__(self): - self._display = get_display_number() - cmd = 'Xnest :%d -ac -geometry 800x600' % (self._display) - Process.__init__(self, cmd) - - def get_name(self): - return 'Xnest' - - def start(self): - Process.start(self) - os.environ['DISPLAY'] = ":%d" % (self._display) + def __init__(self): + self._display = get_display_number() + cmd = 'Xnest :%d -ac -geometry 800x600' % (self._display) + Process.__init__(self, cmd) + + def get_name(self): + return 'Xnest' + + def start(self): + Process.start(self) + os.environ['DISPLAY'] = ":%d" % (self._display) class Emulator(object): - """The OLPC emulator""" - def __init__(self, fullscreen): - self._fullscreen = fullscreen - - def start(self): - try: - process = XephyrProcess(self._fullscreen) - process.start() - except: - try: - process = XnestProcess() - process.start() - except: - print 'Cannot run the emulator. You need to install \ - Xephyr or Xnest.' - sys.exit(0) - - process = MatchboxProcess() - process.start() + """The OLPC emulator""" + def __init__(self, fullscreen): + self._fullscreen = fullscreen + + def start(self): + try: + process = XephyrProcess(self._fullscreen) + process.start() + except: + try: + process = XnestProcess() + process.start() + except: + print 'Cannot run the emulator. You need to install \ + Xephyr or Xnest.' + sys.exit(0) + + process = MatchboxProcess() + process.start() diff --git a/sugar/env.py b/sugar/env.py index 7c63731..391c7cd 100644 --- a/sugar/env.py +++ b/sugar/env.py @@ -20,43 +20,43 @@ import sys import pwd try: - from sugar.__uninstalled__ import * + from sugar.__uninstalled__ import * except ImportError: - from sugar.__installed__ import * + from sugar.__installed__ import * def get_profile_path(): - if os.environ.has_key('SUGAR_PROFILE'): - profile_id = os.environ['SUGAR_PROFILE'] - else: - profile_id = 'default' + if os.environ.has_key('SUGAR_PROFILE'): + profile_id = os.environ['SUGAR_PROFILE'] + else: + profile_id = 'default' - path = os.path.join(os.path.expanduser('~/.sugar'), profile_id) - if not os.path.isdir(path): - try: - os.makedirs(path) - except OSError, exc: - print "Could not create user directory." + path = os.path.join(os.path.expanduser('~/.sugar'), profile_id) + if not os.path.isdir(path): + try: + os.makedirs(path) + except OSError, exc: + print "Could not create user directory." - return path + return path def get_data_dir(): - return sugar_data_dir + return sugar_data_dir def get_services_dir(): - return sugar_services_dir + return sugar_services_dir def get_shell_bin_dir(): - return sugar_shell_bin_dir + return sugar_shell_bin_dir # http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html def get_data_dirs(): - if os.environ.has_key('XDG_DATA_DIRS'): - return os.environ['XDG_DATA_DIRS'].split(':') - else: - return [ '/usr/local/share/', '/usr/share/' ] + if os.environ.has_key('XDG_DATA_DIRS'): + return os.environ['XDG_DATA_DIRS'].split(':') + else: + return [ '/usr/local/share/', '/usr/share/' ] def get_user_service_dir(): - service_dir = os.path.expanduser('~/.local/share/dbus-1/services') - if not os.path.isdir(service_dir): - os.makedirs(service_dir) - return service_dir + service_dir = os.path.expanduser('~/.local/share/dbus-1/services') + if not os.path.isdir(service_dir): + os.makedirs(service_dir) + return service_dir diff --git a/sugar/graphics/ClipboardBubble.py b/sugar/graphics/ClipboardBubble.py index b94fc26..1947fd5 100644 --- a/sugar/graphics/ClipboardBubble.py +++ b/sugar/graphics/ClipboardBubble.py @@ -24,108 +24,108 @@ import gtk import hippo class ClipboardBubble(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'ClipboardBubble' - - __gproperties__ = { - 'fill-color': (object, None, None, - gobject.PARAM_READWRITE), - 'stroke-color': (object, None, None, - gobject.PARAM_READWRITE), - 'progress-color': (object, None, None, - gobject.PARAM_READWRITE), - 'percent' : (object, None, None, - gobject.PARAM_READWRITE), - } - - def __init__(self, **kwargs): - self._stroke_color = 0xFFFFFFFF - self._fill_color = 0xFFFFFFFF - self._progress_color = 0x000000FF - self._percent = 0 - self._radius = 8 - - hippo.CanvasBox.__init__(self, **kwargs) - - def do_set_property(self, pspec, value): - if pspec.name == 'fill-color': - self._fill_color = value - self.emit_paint_needed(0, 0, -1, -1) - elif pspec.name == 'stroke-color': - self._stroke_color = value - self.emit_paint_needed(0, 0, -1, -1) - elif pspec.name == 'progress-color': - self._progress_color = value - self.emit_paint_needed(0, 0, -1, -1) - elif pspec.name == 'percent': - self._percent = value - self.emit_paint_needed(0, 0, -1, -1) - - def do_get_property(self, pspec): - if pspec.name == 'fill-color': - return self._fill_color - elif pspec.name == 'stroke-color': - return self._stroke_color - elif pspec.name == 'progress-color': - return self._progress_color - elif pspec.name == 'percent': - return self._percent - - def _int_to_rgb(self, int_color): - red = (int_color >> 24) & 0x000000FF - green = (int_color >> 16) & 0x000000FF - blue = (int_color >> 8) & 0x000000FF - alpha = int_color & 0x000000FF - return (red / 255.0, green / 255.0, blue / 255.0) - - def do_paint_below_children(self, cr, damaged_box): - [width, height] = self.get_allocation() - - line_width = 3.0 - x = line_width - y = line_width - width -= line_width * 2 - height -= line_width * 2 - - self._paint_ellipse(cr, x, y, width, height, self._fill_color) - - color = self._int_to_rgb(self._stroke_color) - cr.set_source_rgb(*color) - cr.set_line_width(line_width) - cr.stroke(); - - self._paint_progress_bar(cr, x, y, width, height, line_width) - - def _paint_progress_bar(self, cr, x, y, width, height, line_width): - prog_x = x + line_width - prog_y = y + line_width - prog_width = (width - (line_width * 2)) * (self._percent / 100.0) - prog_height = (height - (line_width * 2)) - - self._paint_ellipse(cr, prog_x, prog_y, width, height, self._progress_color) - - def _paint_ellipse(self, cr, x, y, width, height, fill_color): - cr.move_to(x + self._radius, y) - cr.arc(x + width - self._radius, - y + self._radius, - self._radius, - math.pi * 1.5, - math.pi * 2) - cr.arc(x + width - self._radius, - x + height - self._radius, - self._radius, - 0, - math.pi * 0.5) - cr.arc(x + self._radius, - y + height - self._radius, - self._radius, - math.pi * 0.5, - math.pi) - cr.arc(x + self._radius, - y + self._radius, - self._radius, - math.pi, - math.pi * 1.5); - - color = self._int_to_rgb(fill_color) - cr.set_source_rgb(*color) - cr.fill_preserve(); + __gtype_name__ = 'ClipboardBubble' + + __gproperties__ = { + 'fill-color': (object, None, None, + gobject.PARAM_READWRITE), + 'stroke-color': (object, None, None, + gobject.PARAM_READWRITE), + 'progress-color': (object, None, None, + gobject.PARAM_READWRITE), + 'percent' : (object, None, None, + gobject.PARAM_READWRITE), + } + + def __init__(self, **kwargs): + self._stroke_color = 0xFFFFFFFF + self._fill_color = 0xFFFFFFFF + self._progress_color = 0x000000FF + self._percent = 0 + self._radius = 8 + + hippo.CanvasBox.__init__(self, **kwargs) + + def do_set_property(self, pspec, value): + if pspec.name == 'fill-color': + self._fill_color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'stroke-color': + self._stroke_color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'progress-color': + self._progress_color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'percent': + self._percent = value + self.emit_paint_needed(0, 0, -1, -1) + + def do_get_property(self, pspec): + if pspec.name == 'fill-color': + return self._fill_color + elif pspec.name == 'stroke-color': + return self._stroke_color + elif pspec.name == 'progress-color': + return self._progress_color + elif pspec.name == 'percent': + return self._percent + + def _int_to_rgb(self, int_color): + red = (int_color >> 24) & 0x000000FF + green = (int_color >> 16) & 0x000000FF + blue = (int_color >> 8) & 0x000000FF + alpha = int_color & 0x000000FF + return (red / 255.0, green / 255.0, blue / 255.0) + + def do_paint_below_children(self, cr, damaged_box): + [width, height] = self.get_allocation() + + line_width = 3.0 + x = line_width + y = line_width + width -= line_width * 2 + height -= line_width * 2 + + self._paint_ellipse(cr, x, y, width, height, self._fill_color) + + color = self._int_to_rgb(self._stroke_color) + cr.set_source_rgb(*color) + cr.set_line_width(line_width) + cr.stroke(); + + self._paint_progress_bar(cr, x, y, width, height, line_width) + + def _paint_progress_bar(self, cr, x, y, width, height, line_width): + prog_x = x + line_width + prog_y = y + line_width + prog_width = (width - (line_width * 2)) * (self._percent / 100.0) + prog_height = (height - (line_width * 2)) + + self._paint_ellipse(cr, prog_x, prog_y, width, height, self._progress_color) + + def _paint_ellipse(self, cr, x, y, width, height, fill_color): + cr.move_to(x + self._radius, y) + cr.arc(x + width - self._radius, + y + self._radius, + self._radius, + math.pi * 1.5, + math.pi * 2) + cr.arc(x + width - self._radius, + x + height - self._radius, + self._radius, + 0, + math.pi * 0.5) + cr.arc(x + self._radius, + y + height - self._radius, + self._radius, + math.pi * 0.5, + math.pi) + cr.arc(x + self._radius, + y + self._radius, + self._radius, + math.pi, + math.pi * 1.5); + + color = self._int_to_rgb(fill_color) + cr.set_source_rgb(*color) + cr.fill_preserve(); diff --git a/sugar/graphics/bubble.py b/sugar/graphics/bubble.py index f5903a0..5bfe87a 100644 --- a/sugar/graphics/bubble.py +++ b/sugar/graphics/bubble.py @@ -22,56 +22,56 @@ import gtk import hippo class Bubble(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'SugarBubble' + __gtype_name__ = 'SugarBubble' - __gproperties__ = { - 'color' : (object, None, None, - gobject.PARAM_READWRITE), - } + __gproperties__ = { + 'color' : (object, None, None, + gobject.PARAM_READWRITE), + } - def __init__(self, **kwargs): - self._color = None - self._radius = 8 + def __init__(self, **kwargs): + self._color = None + self._radius = 8 - hippo.CanvasBox.__init__(self, **kwargs) + hippo.CanvasBox.__init__(self, **kwargs) - def do_set_property(self, pspec, value): - if pspec.name == 'color': - self._color = value - self.emit_paint_needed(0, 0, -1, -1) + def do_set_property(self, pspec, value): + if pspec.name == 'color': + self._color = value + self.emit_paint_needed(0, 0, -1, -1) - def do_get_property(self, pspec): - if pspec.name == 'color': - return self._color + def do_get_property(self, pspec): + if pspec.name == 'color': + return self._color - def _string_to_rgb(self, color_string): - col = gtk.gdk.color_parse(color_string) - return (col.red / 65535.0, col.green / 65535.0, col.blue / 65535.0) + def _string_to_rgb(self, color_string): + col = gtk.gdk.color_parse(color_string) + return (col.red / 65535.0, col.green / 65535.0, col.blue / 65535.0) - def do_paint_below_children(self, cr, damaged_box): - [width, height] = self.get_allocation() + def do_paint_below_children(self, cr, damaged_box): + [width, height] = self.get_allocation() - line_width = 3.0 - x = line_width - y = line_width - width -= line_width * 2 - height -= line_width * 2 + line_width = 3.0 + x = line_width + y = line_width + width -= line_width * 2 + height -= line_width * 2 - cr.move_to(x + self._radius, y); - cr.arc(x + width - self._radius, y + self._radius, - self._radius, math.pi * 1.5, math.pi * 2); - cr.arc(x + width - self._radius, x + height - self._radius, - self._radius, 0, math.pi * 0.5); - cr.arc(x + self._radius, y + height - self._radius, - self._radius, math.pi * 0.5, math.pi); - cr.arc(x + self._radius, y + self._radius, self._radius, - math.pi, math.pi * 1.5); + cr.move_to(x + self._radius, y); + cr.arc(x + width - self._radius, y + self._radius, + self._radius, math.pi * 1.5, math.pi * 2); + cr.arc(x + width - self._radius, x + height - self._radius, + self._radius, 0, math.pi * 0.5); + cr.arc(x + self._radius, y + height - self._radius, + self._radius, math.pi * 0.5, math.pi); + cr.arc(x + self._radius, y + self._radius, self._radius, + math.pi, math.pi * 1.5); - color = self._string_to_rgb(self._color.get_fill_color()) - cr.set_source_rgb(*color) - cr.fill_preserve(); + color = self._string_to_rgb(self._color.get_fill_color()) + cr.set_source_rgb(*color) + cr.fill_preserve(); - color = self._string_to_rgb(self._color.get_stroke_color()) - cr.set_source_rgb(*color) - cr.set_line_width(line_width) - cr.stroke(); + color = self._string_to_rgb(self._color.get_stroke_color()) + cr.set_source_rgb(*color) + cr.set_line_width(line_width) + cr.stroke(); diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py index da3d8ca..06aff7b 100644 --- a/sugar/graphics/canvasicon.py +++ b/sugar/graphics/canvasicon.py @@ -26,133 +26,133 @@ import cairo from sugar.graphics.iconcolor import IconColor class _IconCache: - def __init__(self): - self._icons = {} - self._theme = gtk.icon_theme_get_default() + def __init__(self): + self._icons = {} + self._theme = gtk.icon_theme_get_default() - def _read_icon(self, filename, color): - icon_file = open(filename, 'r') + def _read_icon(self, filename, color): + icon_file = open(filename, 'r') - if color == None: - return rsvg.Handle(file=filename) - else: - data = icon_file.read() - icon_file.close() + if color == None: + return rsvg.Handle(file=filename) + else: + data = icon_file.read() + icon_file.close() - fill = color.get_fill_color() - stroke = color.get_stroke_color() - - entity = '' % fill - data = re.sub('', entity, data) + fill = color.get_fill_color() + stroke = color.get_stroke_color() + + entity = '' % fill + data = re.sub('', entity, data) - entity = '' % stroke - data = re.sub('', entity, data) + entity = '' % stroke + data = re.sub('', entity, data) - return rsvg.Handle(data=data) + return rsvg.Handle(data=data) - def get_handle(self, name, color, size): - info = self._theme.lookup_icon(name, int(size), 0) + def get_handle(self, name, color, size): + info = self._theme.lookup_icon(name, int(size), 0) - if color: - key = (info.get_filename(), color.to_string()) - else: - key = info.get_filename() + if color: + key = (info.get_filename(), color.to_string()) + else: + key = info.get_filename() - if self._icons.has_key(key): - icon = self._icons[key] - else: - icon = self._read_icon(info.get_filename(), color) - self._icons[key] = icon - return icon + if self._icons.has_key(key): + icon = self._icons[key] + else: + icon = self._read_icon(info.get_filename(), color) + self._icons[key] = icon + return icon class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'CanvasIcon' - - __gproperties__ = { - 'icon-name': (str, None, None, None, - gobject.PARAM_READWRITE), - 'color' : (object, None, None, - gobject.PARAM_READWRITE), - 'size' : (int, None, None, - 0, 1024, 24, - gobject.PARAM_READWRITE) - } - - _cache = _IconCache() - - def __init__(self, **kwargs): - self._size = 24 - self._color = None - self._icon_name = None - - hippo.CanvasBox.__init__(self, **kwargs) - - self._buffer = None - - self.connect('button-press-event', self._button_press_event_cb) - - def do_set_property(self, pspec, value): - if pspec.name == 'icon-name': - self._icon_name = value - self._buffer = None - self.emit_paint_needed(0, 0, -1, -1) - elif pspec.name == 'color': - self._buffer = None - self._color = value - self.emit_paint_needed(0, 0, -1, -1) - elif pspec.name == 'size': - self._buffer = None - self._size = value - self.emit_request_changed() - - def do_get_property(self, pspec): - if pspec.name == 'size': - return self._size - elif pspec.name == 'icon-name': - return self._icon_name - elif pspec.name == 'color': - return self._color - - def _get_buffer(self, cr, handle, size): - if self._buffer == None: - target = cr.get_target() - surface = target.create_similar(cairo.CONTENT_COLOR_ALPHA, - int(size) + 1, int(size) + 1) - - dimensions = handle.get_dimension_data() - scale = float(size) / float(dimensions[0]) - - ctx = cairo.Context(surface) - ctx.scale(scale, scale) - handle.render_cairo(ctx) - del ctx - - self._buffer = surface - self._buffer_scale = scale - - return self._buffer - - def do_paint_below_children(self, cr, damaged_box): - icon_name = self._icon_name - if icon_name == None: - icon_name = 'stock-missing' - - handle = CanvasIcon._cache.get_handle( - icon_name, self._color, self._size) - buf = self._get_buffer(cr, handle, self._size) - - [width, height] = self.get_allocation() - x = (width - self._size) / 2 - y = (height - self._size) / 2 - - cr.set_source_surface(buf, x, y) - cr.paint() - - def do_get_width_request(self): - return self._size - - def do_get_height_request(self, for_width): - return self._size - - def _button_press_event_cb(self, item, event): - item.emit_activated() + __gtype_name__ = 'CanvasIcon' + + __gproperties__ = { + 'icon-name': (str, None, None, None, + gobject.PARAM_READWRITE), + 'color' : (object, None, None, + gobject.PARAM_READWRITE), + 'size' : (int, None, None, + 0, 1024, 24, + gobject.PARAM_READWRITE) + } + + _cache = _IconCache() + + def __init__(self, **kwargs): + self._size = 24 + self._color = None + self._icon_name = None + + hippo.CanvasBox.__init__(self, **kwargs) + + self._buffer = None + + self.connect('button-press-event', self._button_press_event_cb) + + def do_set_property(self, pspec, value): + if pspec.name == 'icon-name': + self._icon_name = value + self._buffer = None + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'color': + self._buffer = None + self._color = value + self.emit_paint_needed(0, 0, -1, -1) + elif pspec.name == 'size': + self._buffer = None + self._size = value + self.emit_request_changed() + + def do_get_property(self, pspec): + if pspec.name == 'size': + return self._size + elif pspec.name == 'icon-name': + return self._icon_name + elif pspec.name == 'color': + return self._color + + def _get_buffer(self, cr, handle, size): + if self._buffer == None: + target = cr.get_target() + surface = target.create_similar(cairo.CONTENT_COLOR_ALPHA, + int(size) + 1, int(size) + 1) + + dimensions = handle.get_dimension_data() + scale = float(size) / float(dimensions[0]) + + ctx = cairo.Context(surface) + ctx.scale(scale, scale) + handle.render_cairo(ctx) + del ctx + + self._buffer = surface + self._buffer_scale = scale + + return self._buffer + + def do_paint_below_children(self, cr, damaged_box): + icon_name = self._icon_name + if icon_name == None: + icon_name = 'stock-missing' + + handle = CanvasIcon._cache.get_handle( + icon_name, self._color, self._size) + buf = self._get_buffer(cr, handle, self._size) + + [width, height] = self.get_allocation() + x = (width - self._size) / 2 + y = (height - self._size) / 2 + + cr.set_source_surface(buf, x, y) + cr.paint() + + def do_get_width_request(self): + return self._size + + def do_get_height_request(self, for_width): + return self._size + + def _button_press_event_cb(self, item, event): + item.emit_activated() diff --git a/sugar/graphics/grid.py b/sugar/graphics/grid.py index 350b4ec..cfbdf67 100644 --- a/sugar/graphics/grid.py +++ b/sugar/graphics/grid.py @@ -21,19 +21,19 @@ COLS = 16 ROWS = 12 class Grid(object): - def __init__(self): - self._factor = gtk.gdk.screen_width() / COLS + def __init__(self): + self._factor = gtk.gdk.screen_width() / COLS - def point(self, grid_x, grid_y): - return [grid_x * self._factor, grid_y * self._factor] + def point(self, grid_x, grid_y): + return [grid_x * self._factor, grid_y * self._factor] - def rectangle(self, grid_x, grid_y, grid_w, grid_h): - return [grid_x * self._factor, grid_y * self._factor, - grid_w * self._factor, grid_h * self._factor] + def rectangle(self, grid_x, grid_y, grid_w, grid_h): + return [grid_x * self._factor, grid_y * self._factor, + grid_w * self._factor, grid_h * self._factor] - def dimension(self, grid_dimension): - return grid_dimension * self._factor + def dimension(self, grid_dimension): + return grid_dimension * self._factor - def fit_point(self, x, y): - return [int(x / self._factor), int(y / self._factor)] - + def fit_point(self, x, y): + return [int(x / self._factor), int(y / self._factor)] + diff --git a/sugar/graphics/iconcolor.py b/sugar/graphics/iconcolor.py index d736d54..71dfe18 100644 --- a/sugar/graphics/iconcolor.py +++ b/sugar/graphics/iconcolor.py @@ -20,32 +20,32 @@ import random from sugar.graphics.colors import colors def _parse_string(color_string): - if color_string == 'white': - return ['#ffffff', '#414141'] + if color_string == 'white': + return ['#ffffff', '#414141'] - splitted = color_string.split(',') - if len(splitted) == 2: - return [splitted[0], splitted[1]] - else: - return None + splitted = color_string.split(',') + if len(splitted) == 2: + return [splitted[0], splitted[1]] + else: + return None def is_valid(color_string): - return (_parse_string(color_string) != None) + return (_parse_string(color_string) != None) class IconColor: - def __init__(self, color_string=None): - if color_string == None or not is_valid(color_string): - n = int(random.random() * (len(colors) - 1)) - [self._stroke, self._fill] = colors[n] - else: - [self._stroke, self._fill] = _parse_string(color_string) + def __init__(self, color_string=None): + if color_string == None or not is_valid(color_string): + n = int(random.random() * (len(colors) - 1)) + [self._stroke, self._fill] = colors[n] + else: + [self._stroke, self._fill] = _parse_string(color_string) - def get_stroke_color(self): - return self._stroke + def get_stroke_color(self): + return self._stroke - def get_fill_color(self): - return self._fill + def get_fill_color(self): + return self._fill - def to_string(self): - return '%s,%s' % (self._stroke, self._fill) + def to_string(self): + return '%s,%s' % (self._stroke, self._fill) diff --git a/sugar/graphics/menu.py b/sugar/graphics/menu.py index 508dbb0..5b68d61 100644 --- a/sugar/graphics/menu.py +++ b/sugar/graphics/menu.py @@ -23,85 +23,85 @@ from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics import style class Menu(gtk.Window): - __gsignals__ = { - 'action': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([int])), - } + __gsignals__ = { + 'action': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([int])), + } - def __init__(self, title=None, content_box=None): - gtk.Window.__init__(self, gtk.WINDOW_POPUP) + def __init__(self, title=None, content_box=None): + gtk.Window.__init__(self, gtk.WINDOW_POPUP) - canvas = hippo.Canvas() - self.add(canvas) - canvas.show() + canvas = hippo.Canvas() + self.add(canvas) + canvas.show() - self._root = hippo.CanvasBox() - style.apply_stylesheet(self._root, 'menu') - canvas.set_root(self._root) + self._root = hippo.CanvasBox() + style.apply_stylesheet(self._root, 'menu') + canvas.set_root(self._root) - if title: - self._title_item = hippo.CanvasText(text=title) - style.apply_stylesheet(self._title_item, 'menu.Title') - self._root.append(self._title_item) - else: - self._title_item = None + if title: + self._title_item = hippo.CanvasText(text=title) + style.apply_stylesheet(self._title_item, 'menu.Title') + self._root.append(self._title_item) + else: + self._title_item = None - if content_box: - separator = self._create_separator() - self._root.append(separator) - self._root.append(content_box) + if content_box: + separator = self._create_separator() + self._root.append(separator) + self._root.append(content_box) - self._action_box = None - self._item_box = None + self._action_box = None + self._item_box = None - def _create_separator(self): - separator = hippo.CanvasBox() - style.apply_stylesheet(separator, 'menu.Separator') - return separator + def _create_separator(self): + separator = hippo.CanvasBox() + style.apply_stylesheet(separator, 'menu.Separator') + return separator - def _create_item_box(self): - if self._title_item: - separator = self._create_separator() - self._root.append(separator) + def _create_item_box(self): + if self._title_item: + separator = self._create_separator() + self._root.append(separator) - self._item_box = hippo.CanvasBox( - orientation=hippo.ORIENTATION_VERTICAL) - self._root.append(self._item_box) + self._item_box = hippo.CanvasBox( + orientation=hippo.ORIENTATION_VERTICAL) + self._root.append(self._item_box) - def _create_action_box(self): - separator = self._create_separator() - self._root.append(separator) + def _create_action_box(self): + separator = self._create_separator() + self._root.append(separator) - self._action_box = hippo.CanvasBox( - orientation=hippo.ORIENTATION_HORIZONTAL) - self._root.append(self._action_box) + self._action_box = hippo.CanvasBox( + orientation=hippo.ORIENTATION_HORIZONTAL) + self._root.append(self._action_box) - def add_item(self, label, action_id): - if not self._item_box: - self._create_item_box() + def add_item(self, label, action_id): + if not self._item_box: + self._create_item_box() - text = hippo.CanvasText(text=label) - style.apply_stylesheet(text, 'menu.Item') + text = hippo.CanvasText(text=label) + style.apply_stylesheet(text, 'menu.Item') - # FIXME need a way to make hippo items activable in python - text.connect('button-press-event', self._item_clicked_cb, action_id) - #text.connect('activated', self._action_clicked_cb, action_id) + # FIXME need a way to make hippo items activable in python + text.connect('button-press-event', self._item_clicked_cb, action_id) + #text.connect('activated', self._action_clicked_cb, action_id) - self._item_box.append(text) + self._item_box.append(text) - def add_action(self, icon, action_id): - if not self._action_box: - self._create_action_box() + def add_action(self, icon, action_id): + if not self._action_box: + self._create_action_box() - style.apply_stylesheet(icon, 'menu.ActionIcon') - icon.connect('activated', self._action_clicked_cb, action_id) - self._action_box.append(icon) + style.apply_stylesheet(icon, 'menu.ActionIcon') + icon.connect('activated', self._action_clicked_cb, action_id) + self._action_box.append(icon) - def remove_action(self, icon): - self._action_box.remove(icon) + def remove_action(self, icon): + self._action_box.remove(icon) - def _item_clicked_cb(self, icon, event, action): - self.emit('action', action) + def _item_clicked_cb(self, icon, event, action): + self.emit('action', action) - def _action_clicked_cb(self, icon, action): - self.emit('action', action) + def _action_clicked_cb(self, icon, action): + self.emit('action', action) diff --git a/sugar/graphics/menuicon.py b/sugar/graphics/menuicon.py index 8c0041e..62d1275 100644 --- a/sugar/graphics/menuicon.py +++ b/sugar/graphics/menuicon.py @@ -23,58 +23,58 @@ from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.timeline import Timeline class MenuIcon(CanvasIcon): - def __init__(self, menu_shell, **kwargs): - CanvasIcon.__init__(self, **kwargs) + def __init__(self, menu_shell, **kwargs): + CanvasIcon.__init__(self, **kwargs) - self._menu_shell = menu_shell - self._menu = None - self._hover_menu = False + self._menu_shell = menu_shell + self._menu = None + self._hover_menu = False - self._timeline = Timeline(self) - self._timeline.add_tag('popup', 6, 6) - self._timeline.add_tag('before_popdown', 7, 7) - self._timeline.add_tag('popdown', 8, 8) + self._timeline = Timeline(self) + self._timeline.add_tag('popup', 6, 6) + self._timeline.add_tag('before_popdown', 7, 7) + self._timeline.add_tag('popdown', 8, 8) - self.connect('motion-notify-event', self._motion_notify_event_cb) + self.connect('motion-notify-event', self._motion_notify_event_cb) - def do_popup(self, current, n_frames): - if self._menu: - return + def do_popup(self, current, n_frames): + if self._menu: + return - self._menu = self.create_menu() + self._menu = self.create_menu() - self._menu.connect('enter-notify-event', - self._menu_enter_notify_event_cb) - self._menu.connect('leave-notify-event', - self._menu_leave_notify_event_cb) + self._menu.connect('enter-notify-event', + self._menu_enter_notify_event_cb) + self._menu.connect('leave-notify-event', + self._menu_leave_notify_event_cb) - [x, y] = self._menu_shell.get_position(self._menu, self) + [x, y] = self._menu_shell.get_position(self._menu, self) - self._menu.move(x, y) - self._menu.show() + self._menu.move(x, y) + self._menu.show() - self._menu_shell.set_active(self) + self._menu_shell.set_active(self) - def do_popdown(self, current, frame): - if self._menu: - self._menu.destroy() - self._menu = None - self._menu_shell.set_active(None) + def do_popdown(self, current, frame): + if self._menu: + self._menu.destroy() + self._menu = None + self._menu_shell.set_active(None) - def popdown(self): - self._timeline.play('popdown', 'popdown') + def popdown(self): + self._timeline.play('popdown', 'popdown') - def _motion_notify_event_cb(self, item, event): - if event.detail == hippo.MOTION_DETAIL_ENTER: - self._timeline.play(None, 'popup') - elif event.detail == hippo.MOTION_DETAIL_LEAVE: - if not self._hover_menu: - self._timeline.play('before_popdown', 'popdown') + def _motion_notify_event_cb(self, item, event): + if event.detail == hippo.MOTION_DETAIL_ENTER: + self._timeline.play(None, 'popup') + elif event.detail == hippo.MOTION_DETAIL_LEAVE: + if not self._hover_menu: + self._timeline.play('before_popdown', 'popdown') - def _menu_enter_notify_event_cb(self, widget, event): - self._hover_menu = True - self._timeline.play('popup', 'popup') + def _menu_enter_notify_event_cb(self, widget, event): + self._hover_menu = True + self._timeline.play('popup', 'popup') - def _menu_leave_notify_event_cb(self, widget, event): - self._hover_menu = False - self._timeline.play('popdown', 'popdown') + def _menu_leave_notify_event_cb(self, widget, event): + self._hover_menu = False + self._timeline.play('popdown', 'popdown') diff --git a/sugar/graphics/menushell.py b/sugar/graphics/menushell.py index 48183e1..61b98b0 100644 --- a/sugar/graphics/menushell.py +++ b/sugar/graphics/menushell.py @@ -19,83 +19,83 @@ import gobject import gtk class MenuShell(gobject.GObject): - __gsignals__ = { - 'activated': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), - 'deactivated': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), - } - - AUTO = 0 - LEFT = 1 - RIGHT = 2 - TOP = 3 - BOTTOM = 4 - - def __init__(self, parent_canvas): - gobject.GObject.__init__(self) - - self._parent_canvas = parent_canvas - self._menu_controller = None - self._position = MenuShell.AUTO - - def set_position(self, position): - self._position = position - - def is_active(self): - return (self._menu_controller != None) - - def set_active(self, controller): - if controller == None: - self.emit('deactivated') - else: - self.emit('activated') - - if self._menu_controller: - self._menu_controller.popdown() - self._menu_controller = controller - - def _get_item_rect(self, item): - [x, y] = item.get_context().translate_to_widget(item) - - [origin_x, origin_y] = self._parent_canvas.window.get_origin() - x += origin_x - y += origin_y - - [w, h] = item.get_allocation() - - return [x, y, w, h] - - def get_position(self, menu, item): - [item_x, item_y, item_w, item_h] = self._get_item_rect(item) - [menu_w, menu_h] = menu.size_request() - - left_x = item_x - menu_w - left_y = item_y - right_x = item_x + item_w - right_y = item_y - top_x = item_x - top_y = item_y - menu_h - bottom_x = item_x - bottom_y = item_y + item_h - - if self._position == MenuShell.LEFT: - [x, y] = [left_x, left_y] - elif self._position == MenuShell.RIGHT: - [x, y] = [right_x, right_y] - elif self._position == MenuShell.TOP: - [x, y] = [top_x, top_y] - elif self._position == MenuShell.BOTTOM: - [x, y] = [bottom_x, bottom_y] - elif self._position == MenuShell.AUTO: - [x, y] = [right_x, right_y] - if x + menu_w > gtk.gdk.screen_width(): - [x, y] = [left_x, left_y] - - x = min(x, gtk.gdk.screen_width() - menu_w) - x = max(0, x) - - y = min(y, gtk.gdk.screen_height() - menu_h) - y = max(0, y) - - return [x, y] + __gsignals__ = { + 'activated': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + 'deactivated': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + } + + AUTO = 0 + LEFT = 1 + RIGHT = 2 + TOP = 3 + BOTTOM = 4 + + def __init__(self, parent_canvas): + gobject.GObject.__init__(self) + + self._parent_canvas = parent_canvas + self._menu_controller = None + self._position = MenuShell.AUTO + + def set_position(self, position): + self._position = position + + def is_active(self): + return (self._menu_controller != None) + + def set_active(self, controller): + if controller == None: + self.emit('deactivated') + else: + self.emit('activated') + + if self._menu_controller: + self._menu_controller.popdown() + self._menu_controller = controller + + def _get_item_rect(self, item): + [x, y] = item.get_context().translate_to_widget(item) + + [origin_x, origin_y] = self._parent_canvas.window.get_origin() + x += origin_x + y += origin_y + + [w, h] = item.get_allocation() + + return [x, y, w, h] + + def get_position(self, menu, item): + [item_x, item_y, item_w, item_h] = self._get_item_rect(item) + [menu_w, menu_h] = menu.size_request() + + left_x = item_x - menu_w + left_y = item_y + right_x = item_x + item_w + right_y = item_y + top_x = item_x + top_y = item_y - menu_h + bottom_x = item_x + bottom_y = item_y + item_h + + if self._position == MenuShell.LEFT: + [x, y] = [left_x, left_y] + elif self._position == MenuShell.RIGHT: + [x, y] = [right_x, right_y] + elif self._position == MenuShell.TOP: + [x, y] = [top_x, top_y] + elif self._position == MenuShell.BOTTOM: + [x, y] = [bottom_x, bottom_y] + elif self._position == MenuShell.AUTO: + [x, y] = [right_x, right_y] + if x + menu_w > gtk.gdk.screen_width(): + [x, y] = [left_x, left_y] + + x = min(x, gtk.gdk.screen_width() - menu_w) + x = max(0, x) + + y = min(y, gtk.gdk.screen_height() - menu_h) + y = max(0, y) + + return [x, y] diff --git a/sugar/graphics/snowflakebox.py b/sugar/graphics/snowflakebox.py index 2af11cd..30f5b05 100644 --- a/sugar/graphics/snowflakebox.py +++ b/sugar/graphics/snowflakebox.py @@ -25,72 +25,72 @@ _CHILDREN_FACTOR = 1 _FLAKE_DISTANCE = 6 class SnowflakeBox(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'SugarSnowflakeBox' - def __init__(self, **kwargs): - hippo.CanvasBox.__init__(self, **kwargs) - self._root = None + __gtype_name__ = 'SugarSnowflakeBox' + def __init__(self, **kwargs): + hippo.CanvasBox.__init__(self, **kwargs) + self._root = None - def set_root(self, icon): - self._root = icon + def set_root(self, icon): + self._root = icon - def _get_center(self): - [width, height] = self.get_allocation() - return [width / 2, height / 2] + def _get_center(self): + [width, height] = self.get_allocation() + return [width / 2, height / 2] - def _get_radius(self): - return _BASE_RADIUS + _CHILDREN_FACTOR * self._get_n_children() + def _get_radius(self): + return _BASE_RADIUS + _CHILDREN_FACTOR * self._get_n_children() - def _layout_root(self): - [width, height] = self._root.get_allocation() - [cx, cy] = self._get_center() + def _layout_root(self): + [width, height] = self._root.get_allocation() + [cx, cy] = self._get_center() - x = cx - (width / 2) - y = cy - (height / 2) + x = cx - (width / 2) + y = cy - (height / 2) - self.move(self._root, int(x), int(y)) + self.move(self._root, int(x), int(y)) - def _get_n_children(self): - return len(self.get_children()) - 1 + def _get_n_children(self): + return len(self.get_children()) - 1 - def _layout_child(self, child, index): - r = self._get_radius() - if (self._get_n_children() > 10): - r += _FLAKE_DISTANCE * (index % 3) + def _layout_child(self, child, index): + r = self._get_radius() + if (self._get_n_children() > 10): + r += _FLAKE_DISTANCE * (index % 3) - angle = 2 * math.pi * index / self._get_n_children() + angle = 2 * math.pi * index / self._get_n_children() - [width, height] = child.get_allocation() - [cx, cy] = self._get_center() + [width, height] = child.get_allocation() + [cx, cy] = self._get_center() - x = cx + math.cos(angle) * r - (width / 2) - y = cy + math.sin(angle) * r - (height / 2) + x = cx + math.cos(angle) * r - (width / 2) + y = cy + math.sin(angle) * r - (height / 2) - self.move(child, int(x), int(y)) + self.move(child, int(x), int(y)) - def do_get_width_request(self): - hippo.CanvasBox.do_get_width_request(self) + def do_get_width_request(self): + hippo.CanvasBox.do_get_width_request(self) - max_child_size = 0 - for child in self.get_children(): - width = child.get_width_request() - height = child.get_height_request(width) - max_child_size = max (max_child_size, width) - max_child_size = max (max_child_size, height) + max_child_size = 0 + for child in self.get_children(): + width = child.get_width_request() + height = child.get_height_request(width) + max_child_size = max (max_child_size, width) + max_child_size = max (max_child_size, height) - return self._get_radius() * 2 + \ - max_child_size + _FLAKE_DISTANCE * 2 + return self._get_radius() * 2 + \ + max_child_size + _FLAKE_DISTANCE * 2 - def do_get_height_request(self, width): - hippo.CanvasBox.do_get_height_request(self, width) - return width + def do_get_height_request(self, width): + hippo.CanvasBox.do_get_height_request(self, width) + return width - def do_allocate(self, width, height): - hippo.CanvasBox.do_allocate(self, width, height) + def do_allocate(self, width, height): + hippo.CanvasBox.do_allocate(self, width, height) - self._layout_root() + self._layout_root() - index = 0 - for child in self.get_children(): - if child != self._root: - self._layout_child(child, index) - index += 1 + index = 0 + for child in self.get_children(): + if child != self._root: + self._layout_child(child, index) + index += 1 diff --git a/sugar/graphics/spreadbox.py b/sugar/graphics/spreadbox.py index 7d4047d..59dfe9c 100644 --- a/sugar/graphics/spreadbox.py +++ b/sugar/graphics/spreadbox.py @@ -25,108 +25,108 @@ _DISTANCE_THRESHOLD = 10.0 _FORCE_CONSTANT = 0.1 class SpreadBox(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'SugarSpreadBox' + __gtype_name__ = 'SugarSpreadBox' - def __init__(self, **kwargs): - hippo.CanvasBox.__init__(self, **kwargs) + def __init__(self, **kwargs): + hippo.CanvasBox.__init__(self, **kwargs) - self._items_to_position = [] - self._stable = False + self._items_to_position = [] + self._stable = False - def add_item(self, item): - self._items_to_position.append(item) - self.append(item, hippo.PACK_FIXED) + def add_item(self, item): + self._items_to_position.append(item) + self.append(item, hippo.PACK_FIXED) - def remove_item(self, item): - if self._items_to_position.count(item) > 0: - self._items_to_position.remove(item) - self.remove(item) + def remove_item(self, item): + if self._items_to_position.count(item) > 0: + self._items_to_position.remove(item) + self.remove(item) - def _get_item_radius(self, item): - [width, height] = item.get_request() - return math.sqrt(width ** 2 + height ** 2) / 2 + def _get_item_radius(self, item): + [width, height] = item.get_request() + return math.sqrt(width ** 2 + height ** 2) / 2 - def _get_item_center(self, item): - [width, height] = item.get_request() - [x, y] = self.get_position(item) + def _get_item_center(self, item): + [width, height] = item.get_request() + [x, y] = self.get_position(item) - c_x = int(x + float(width) / 2.0) - c_y = int(y + float(height) / 2.0) + c_x = int(x + float(width) / 2.0) + c_y = int(y + float(height) / 2.0) - return [c_x, c_y] + return [c_x, c_y] - def _get_repulsion(self, icon1, icon2): - [c1_x, c1_y] = self._get_item_center(icon1) - [c2_x, c2_y] = self._get_item_center(icon2) + def _get_repulsion(self, icon1, icon2): + [c1_x, c1_y] = self._get_item_center(icon1) + [c2_x, c2_y] = self._get_item_center(icon2) - a = c2_x - c1_x - b = c2_y - c1_y + a = c2_x - c1_x + b = c2_y - c1_y - r1 = self._get_item_radius(icon1) - r2 = self._get_item_radius(icon2) - distance = math.sqrt(a ** 2 + b ** 2) - r1 - r2 + r1 = self._get_item_radius(icon1) + r2 = self._get_item_radius(icon2) + distance = math.sqrt(a ** 2 + b ** 2) - r1 - r2 - if distance < _DISTANCE_THRESHOLD: - f_x = int(math.ceil(-_FORCE_CONSTANT * float(a))) - f_y = int(math.ceil(-_FORCE_CONSTANT * float(b))) - else: - f_x = 0 - f_y = 0 + if distance < _DISTANCE_THRESHOLD: + f_x = int(math.ceil(-_FORCE_CONSTANT * float(a))) + f_y = int(math.ceil(-_FORCE_CONSTANT * float(b))) + else: + f_x = 0 + f_y = 0 - return [f_x, f_y] + return [f_x, f_y] - def _clamp_position(self, icon, x, y): - x = max(0, x) - y = max(0, y) + def _clamp_position(self, icon, x, y): + x = max(0, x) + y = max(0, y) - [item_w, item_h] = icon.get_request() - [box_w, box_h] = self.get_allocation() + [item_w, item_h] = icon.get_request() + [box_w, box_h] = self.get_allocation() - x = min(box_w - item_w, x) - y = min(box_h - item_h, y) + x = min(box_w - item_w, x) + y = min(box_h - item_h, y) - return [x, y] + return [x, y] - def _spread_icons(self): - self._stable = True + def _spread_icons(self): + self._stable = True - for icon1 in self.get_children(): - vx = 0 - vy = 0 + for icon1 in self.get_children(): + vx = 0 + vy = 0 - for icon2 in self.get_children(): - if icon1 != icon2: - [f_x, f_y] = self._get_repulsion(icon1, icon2) - if f_x != 0 or f_y != 0: - self._stable = False - vx += f_x - vy += f_y + for icon2 in self.get_children(): + if icon1 != icon2: + [f_x, f_y] = self._get_repulsion(icon1, icon2) + if f_x != 0 or f_y != 0: + self._stable = False + vx += f_x + vy += f_y - if vx != 0 or vy != 0: - [x, y] = self.get_position(icon1) - new_x = x + vx - new_y = y + vy + if vx != 0 or vy != 0: + [x, y] = self.get_position(icon1) + new_x = x + vx + new_y = y + vy - [new_x, new_y] = self._clamp_position(icon1, new_x, new_y) + [new_x, new_y] = self._clamp_position(icon1, new_x, new_y) - self.move(icon1, new_x, new_y) + self.move(icon1, new_x, new_y) - def do_allocate(self, width, height): - hippo.CanvasBox.do_allocate(self, width, height) + def do_allocate(self, width, height): + hippo.CanvasBox.do_allocate(self, width, height) - for item in self._items_to_position: - [item_w, item_h] = item.get_request() + for item in self._items_to_position: + [item_w, item_h] = item.get_request() - x = int(random.random() * width - item_w) - y = int(random.random() * height - item_h) + x = int(random.random() * width - item_w) + y = int(random.random() * height - item_h) - [x, y] = self._clamp_position(item, x, y) - self.move(item, x, y) + [x, y] = self._clamp_position(item, x, y) + self.move(item, x, y) - self._items_to_position = [] + self._items_to_position = [] - tries = 20 - self._spread_icons() - while not self._stable and tries > 0: - self._spread_icons() - tries -= 1 + tries = 20 + self._spread_icons() + while not self._stable and tries > 0: + self._spread_icons() + tries -= 1 diff --git a/sugar/graphics/style.py b/sugar/graphics/style.py index f0ab3e8..6c4bff3 100644 --- a/sugar/graphics/style.py +++ b/sugar/graphics/style.py @@ -31,21 +31,21 @@ large_icon_size = standard_icon_size * 2.0 xlarge_icon_size = standard_icon_size * 3.0 def load_stylesheet(module): - for objname in dir(module): - if not objname.startswith('_'): - obj = getattr(module, objname) - if isinstance(obj, dict): - register_stylesheet(objname.replace('_', '.'), obj) + for objname in dir(module): + if not objname.startswith('_'): + obj = getattr(module, objname) + if isinstance(obj, dict): + register_stylesheet(objname.replace('_', '.'), obj) def register_stylesheet(name, style): - _styles[name] = style + _styles[name] = style def apply_stylesheet(item, stylesheet_name): - if _styles.has_key(stylesheet_name): - style_sheet = _styles[stylesheet_name] - for name in style_sheet.keys(): - item.set_property(name, style_sheet[name]) + if _styles.has_key(stylesheet_name): + style_sheet = _styles[stylesheet_name] + for name in style_sheet.keys(): + item.set_property(name, style_sheet[name]) def get_font_description(style, relative_size): - base_size = 18 * _screen_factor - return '%s %dpx' % (style, int(base_size * relative_size)) + base_size = 18 * _screen_factor + return '%s %dpx' % (style, int(base_size * relative_size)) diff --git a/sugar/graphics/stylesheet.py b/sugar/graphics/stylesheet.py index d349733..ae9b4c8 100644 --- a/sugar/graphics/stylesheet.py +++ b/sugar/graphics/stylesheet.py @@ -1,31 +1,31 @@ from sugar.graphics import style menu = { - 'background_color' : 0x000000FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit + 'background_color' : 0x000000FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit } menu_Title = { - 'color' : 0xFFFFFFFF, - 'font' : style.get_font_description('Bold', 1.2) + 'color' : 0xFFFFFFFF, + 'font' : style.get_font_description('Bold', 1.2) } menu_Separator = { - 'background_color' : 0xFFFFFFFF, - 'box_height' : style.separator_thickness + 'background_color' : 0xFFFFFFFF, + 'box_height' : style.separator_thickness } menu_ActionIcon = { - 'size' : style.standard_icon_size + 'size' : style.standard_icon_size } menu_Item = { - 'color' : 0xFFFFFFFF, - 'font' : style.get_font_description('Plain', 1.1) + 'color' : 0xFFFFFFFF, + 'font' : style.get_font_description('Plain', 1.1) } menu_Text = { - 'color' : 0xFFFFFFFF, - 'font' : style.get_font_description('Plain', 1.2) + 'color' : 0xFFFFFFFF, + 'font' : style.get_font_description('Plain', 1.2) } diff --git a/sugar/graphics/timeline.py b/sugar/graphics/timeline.py index 5c40ca5..3944771 100644 --- a/sugar/graphics/timeline.py +++ b/sugar/graphics/timeline.py @@ -18,100 +18,100 @@ import gobject class _Tag: - def __init__(self, name, start_frame, end_frame): - self.name = name - self.start_frame = start_frame - self.end_frame = end_frame + def __init__(self, name, start_frame, end_frame): + self.name = name + self.start_frame = start_frame + self.end_frame = end_frame class TimelineObserver: - def __init__(self, observer): - self._observer = observer + def __init__(self, observer): + self._observer = observer - def next_frame(self, tag, current_frame, n_frames): - try: - method = getattr(self._observer, 'do_' + tag) - except AttributeError: - method = None + def next_frame(self, tag, current_frame, n_frames): + try: + method = getattr(self._observer, 'do_' + tag) + except AttributeError: + method = None - if method: - method(current_frame, n_frames) + if method: + method(current_frame, n_frames) class Timeline: - def __init__(self, observer): - self._fps = 12 - self._tags = [] - self._name_to_tag = {} - self._current_frame = 0 - self._timeout_sid = 0 - self._observer = TimelineObserver(observer) - - def add_tag(self, name, start_frame, end_frame): - tag = _Tag(name, start_frame, end_frame) - self._tags.append(tag) - self._name_to_tag[name] = tag - - def remove_tag(self, name): - tag = self._tags[name] - self._tags.remove(tag) - del self._tags[name] - - def _next_frame(self, tag, frame): - n_frames = tag.start_frame - tag.end_frame - self._observer.next_frame(tag.name, frame, n_frames) - - def goto(self, tag_name, end_frame=False): - self.pause() - - tag = self._name_to_tag[tag_name] - if end_frame: - self._current_frame = tag.end_frame - else: - self._current_frame = tag.start_frame - - self._next_frame(tag, self._current_frame) - - def on_tag(self, name): - tag = self._name_to_tag[name] - return (tag.start_frame <= self._current_frame and \ - tag.end_frame >= self._current_frame) - - def _get_tags_for_frame(self, frame): - result = [] - for tag in self._tags: - if tag.start_frame <= frame and tag.end_frame >= frame: - result.append(tag) - return result - - def _timeout_cb(self, end_frame): - for tag in self._get_tags_for_frame(self._current_frame): - cur_frame = self._current_frame - tag.start_frame - self._next_frame(tag, cur_frame) - - if self._current_frame < end_frame: - self._current_frame += 1 - return True - else: - return False - - def play(self, start_tag=None, stop_tag=None): - self.pause() - - if start_tag == None: - start = 0 - else: - start = self._name_to_tag[start_tag].start_frame - - if stop_tag == None: - end = self._tags[len(self._tags) - 1].end_frame - else: - end = self._name_to_tag[stop_tag].end_frame - - self._current_frame = start - - interval = 1000 / self._fps - self._timeout_sid = gobject.timeout_add( - interval, self._timeout_cb, end) - - def pause(self): - if self._timeout_sid > 0: - gobject.source_remove(self._timeout_sid) + def __init__(self, observer): + self._fps = 12 + self._tags = [] + self._name_to_tag = {} + self._current_frame = 0 + self._timeout_sid = 0 + self._observer = TimelineObserver(observer) + + def add_tag(self, name, start_frame, end_frame): + tag = _Tag(name, start_frame, end_frame) + self._tags.append(tag) + self._name_to_tag[name] = tag + + def remove_tag(self, name): + tag = self._tags[name] + self._tags.remove(tag) + del self._tags[name] + + def _next_frame(self, tag, frame): + n_frames = tag.start_frame - tag.end_frame + self._observer.next_frame(tag.name, frame, n_frames) + + def goto(self, tag_name, end_frame=False): + self.pause() + + tag = self._name_to_tag[tag_name] + if end_frame: + self._current_frame = tag.end_frame + else: + self._current_frame = tag.start_frame + + self._next_frame(tag, self._current_frame) + + def on_tag(self, name): + tag = self._name_to_tag[name] + return (tag.start_frame <= self._current_frame and \ + tag.end_frame >= self._current_frame) + + def _get_tags_for_frame(self, frame): + result = [] + for tag in self._tags: + if tag.start_frame <= frame and tag.end_frame >= frame: + result.append(tag) + return result + + def _timeout_cb(self, end_frame): + for tag in self._get_tags_for_frame(self._current_frame): + cur_frame = self._current_frame - tag.start_frame + self._next_frame(tag, cur_frame) + + if self._current_frame < end_frame: + self._current_frame += 1 + return True + else: + return False + + def play(self, start_tag=None, stop_tag=None): + self.pause() + + if start_tag == None: + start = 0 + else: + start = self._name_to_tag[start_tag].start_frame + + if stop_tag == None: + end = self._tags[len(self._tags) - 1].end_frame + else: + end = self._name_to_tag[stop_tag].end_frame + + self._current_frame = start + + interval = 1000 / self._fps + self._timeout_sid = gobject.timeout_add( + interval, self._timeout_cb, end) + + def pause(self): + if self._timeout_sid > 0: + gobject.source_remove(self._timeout_sid) diff --git a/sugar/logger.py b/sugar/logger.py index b4b2e0c..5eb8039 100644 --- a/sugar/logger.py +++ b/sugar/logger.py @@ -29,82 +29,82 @@ STDOUT_LEVEL = 1000 STDERR_LEVEL = 2000 class LogWriter: - def __init__(self, module_id): - self._module_id = module_id - - logs_dir = _get_logs_dir() - log_path = os.path.join(logs_dir, module_id + '.log') - self._log_file = open(log_path, 'w') - - def write_record(self, record): - self.write(record.levelno, record.msg) - - def write(self, level, msg): - if level == logging.ERROR: - level_txt = 'ERROR' - elif level == logging.WARNING: - level_txt = 'WARNING' - elif level == logging.DEBUG: - level_txt = 'DEBUG' - elif level == logging.INFO: - level_txt = 'INFO' - elif level == STDERR_LEVEL: - level_txt = 'STDERR' - elif level == STDOUT_LEVEL: - level_txt = 'STDOUT' - - fmt = "%s - %s\n" % (level_txt, msg) - fmt = fmt.encode("utf8") - self._log_file.write(fmt) - self._log_file.flush() + def __init__(self, module_id): + self._module_id = module_id + + logs_dir = _get_logs_dir() + log_path = os.path.join(logs_dir, module_id + '.log') + self._log_file = open(log_path, 'w') + + def write_record(self, record): + self.write(record.levelno, record.msg) + + def write(self, level, msg): + if level == logging.ERROR: + level_txt = 'ERROR' + elif level == logging.WARNING: + level_txt = 'WARNING' + elif level == logging.DEBUG: + level_txt = 'DEBUG' + elif level == logging.INFO: + level_txt = 'INFO' + elif level == STDERR_LEVEL: + level_txt = 'STDERR' + elif level == STDOUT_LEVEL: + level_txt = 'STDOUT' + + fmt = "%s - %s\n" % (level_txt, msg) + fmt = fmt.encode("utf8") + self._log_file.write(fmt) + self._log_file.flush() class Handler(logging.Handler): - def __init__(self, writer): - logging.Handler.__init__(self) + def __init__(self, writer): + logging.Handler.__init__(self) - self._writer = writer + self._writer = writer - def emit(self, record): - self._writer.write_record(record) + def emit(self, record): + self._writer.write_record(record) class StdoutCatcher: - def write(self, txt): - _log_writer.write(STDOUT_LEVEL, txt) - sys.__stdout__.write(txt) + def write(self, txt): + _log_writer.write(STDOUT_LEVEL, txt) + sys.__stdout__.write(txt) class StderrCatcher: - def write(self, txt): - _log_writer.write(STDERR_LEVEL, txt) - sys.__stderr__.write(txt) + def write(self, txt): + _log_writer.write(STDERR_LEVEL, txt) + sys.__stderr__.write(txt) def __exception_handler(typ, exc, tb): - trace = StringIO() - traceback.print_exception(typ, exc, tb, None, trace) - print >> sys.stderr, trace.getvalue() + trace = StringIO() + traceback.print_exception(typ, exc, tb, None, trace) + print >> sys.stderr, trace.getvalue() - _log_writer.write(logging.ERROR, trace.getvalue()) + _log_writer.write(logging.ERROR, trace.getvalue()) def _get_logs_dir(): - logs_dir = os.path.join(env.get_profile_path(), 'logs') - if not os.path.isdir(logs_dir): - os.makedirs(logs_dir) - return logs_dir + logs_dir = os.path.join(env.get_profile_path(), 'logs') + if not os.path.isdir(logs_dir): + os.makedirs(logs_dir) + return logs_dir def start(module_id): - log_writer = LogWriter(module_id) + log_writer = LogWriter(module_id) - root_logger = logging.getLogger('') - root_logger.setLevel(logging.DEBUG) - root_logger.addHandler(Handler(log_writer)) + root_logger = logging.getLogger('') + root_logger.setLevel(logging.DEBUG) + root_logger.addHandler(Handler(log_writer)) - sys.stdout = StdoutCatcher() - sys.stderr = StderrCatcher() + sys.stdout = StdoutCatcher() + sys.stderr = StderrCatcher() - global _log_writer - _log_writer = log_writer - sys.excepthook = __exception_handler + global _log_writer + _log_writer = log_writer + sys.excepthook = __exception_handler def cleanup(): - logs_dir = _get_logs_dir() - for f in os.listdir(logs_dir): - os.remove(os.path.join(logs_dir, f)) + logs_dir = _get_logs_dir() + for f in os.listdir(logs_dir): + os.remove(os.path.join(logs_dir, f)) diff --git a/sugar/p2p/MostlyReliablePipe.py b/sugar/p2p/MostlyReliablePipe.py index 4218181..604eada 100644 --- a/sugar/p2p/MostlyReliablePipe.py +++ b/sugar/p2p/MostlyReliablePipe.py @@ -32,952 +32,952 @@ import gobject def _stringify_sha(sha_hash): - print_sha = "" - for char in sha_hash: - print_sha = print_sha + binascii.b2a_hex(char) - return print_sha + print_sha = "" + for char in sha_hash: + print_sha = print_sha + binascii.b2a_hex(char) + return print_sha def _sha_data(data): - sha_hash = sha.new() - sha_hash.update(data) - return sha_hash.digest() + sha_hash = sha.new() + sha_hash.update(data) + return sha_hash.digest() _UDP_DATAGRAM_SIZE = 512 class SegmentBase(object): - _MAGIC = 0xbaea4304 - - # 4: magic (0xbaea4304) - # 1: type - # 2: segment number - # 2: total segments - # 2: message sequence number - #20: total data sha1 - _HEADER_TEMPLATE = "! IbHHH20s" - _HEADER_LEN = struct.calcsize(_HEADER_TEMPLATE) - _MTU = _UDP_DATAGRAM_SIZE - _HEADER_LEN - - # Message segment packet types - _SEGMENT_TYPE_DATA = 0 - _SEGMENT_TYPE_RETRANSMIT = 1 - _SEGMENT_TYPE_ACK = 2 - - def magic(): - return SegmentBase._MAGIC - magic = staticmethod(magic) - - def header_template(): - return SegmentBase._HEADER_TEMPLATE - header_template = staticmethod(header_template) - - def type_data(): - return SegmentBase._SEGMENT_TYPE_DATA - type_data = staticmethod(type_data) - - def type_retransmit(): - return SegmentBase._SEGMENT_TYPE_RETRANSMIT - type_retransmit = staticmethod(type_retransmit) - - def type_ack(): - return SegmentBase._SEGMENT_TYPE_ACK - type_ack = staticmethod(type_ack) - - def header_len(): - """Return the header size of SegmentBase packets.""" - return SegmentBase._HEADER_LEN - header_len = staticmethod(header_len) - - def mtu(): - """Return the SegmentBase packet MTU.""" - return SegmentBase._MTU - mtu = staticmethod(mtu) - - def __init__(self, segno, total_segs, msg_seq_num, master_sha): - self._type = None - self._transmits = 0 - self._last_transmit = 0 - self._data = None - self._data_len = 0 - self.userdata = None - self._stime = time.time() - self._addr = None - - # Sanity checks on the message attributes - if not segno or not isinstance(segno, int): - raise ValueError("Segment number must be in integer.") - if segno < 1 or segno > 65535: - raise ValueError("Segment number must be between 1 and 65535 inclusive.") - if not total_segs or not isinstance(total_segs, int): - raise ValueError("Message segment total must be an integer.") - if total_segs < 1 or total_segs > 65535: - raise ValueError("Message must have between 1 and 65535 segments inclusive.") - if segno > total_segs: - raise ValueError("Segment number cannot be larger than message segment total.") - if not msg_seq_num or not isinstance(msg_seq_num, int): - raise ValueError("Message sequnce number must be an integer.") - if msg_seq_num < 1 or msg_seq_num > 65535: - raise ValueError("Message sequence number must be between 1 and 65535 inclusive.") - if not master_sha or not isinstance(master_sha, str) or len(master_sha) != 20: - raise ValueError("Message SHA1 checksum invalid.") - - self._segno = segno - self._total_segs = total_segs - self._msg_seq_num = msg_seq_num - self._master_sha = master_sha - - def _validate_address(addr): - if not addr or not isinstance(addr, tuple): - raise ValueError("Address must be a tuple.") - if len(addr) != 2 or not isinstance(addr[0], str) or not isinstance(addr[1], int): - raise ValueError("Address format was invalid.") - if addr[1] < 1 or addr[1] > 65535: - raise ValueError("Address port was invalid.") - _validate_address = staticmethod(_validate_address) - - def new_from_data(addr, data): - """Static constructor for creation from a packed data stream.""" - SegmentBase._validate_address(addr) - - # Verify minimum length - if not data: - raise ValueError("Segment data is invalid.") - data_len = len(data) - if data_len < SegmentBase.header_len() + 1: - raise ValueError("Segment is less then minimum required length") - if data_len > _UDP_DATAGRAM_SIZE: - raise ValueError("Segment data is larger than allowed.") - stream = StringIO.StringIO(data) - - # Determine and verify the length of included data - stream.seek(0, 2) - data_len = stream.tell() - SegmentBase._HEADER_LEN - stream.seek(0) - - if data_len < 1: - raise ValueError("Segment must have some data.") - if data_len > SegmentBase._MTU: - raise ValueError("Data length must not be larger than the MTU (%s)." % SegmentBase._MTU) - - # Read the first header attributes - (magic, seg_type, segno, total_segs, msg_seq_num, master_sha) = struct.unpack(SegmentBase._HEADER_TEMPLATE, - stream.read(SegmentBase._HEADER_LEN)) - - # Sanity checks on the message attributes - if magic != SegmentBase._MAGIC: - raise ValueError("Segment does not have the correct magic.") - - # if the segment is the only one in the message, validate the data - if segno == 1 and total_segs == 1: - data_sha = _sha_data(stream.read(data_len)) - if data_sha != master_sha: - raise ValueError("Single segment message SHA checksums didn't match.") - stream.seek(SegmentBase._HEADER_LEN) - - if seg_type == SegmentBase._SEGMENT_TYPE_DATA: - segment = DataSegment(segno, total_segs, msg_seq_num, master_sha) - elif seg_type == SegmentBase._SEGMENT_TYPE_RETRANSMIT: - segment = RetransmitSegment(segno, total_segs, msg_seq_num, master_sha) - elif seg_type == SegmentBase._SEGMENT_TYPE_ACK: - segment = AckSegment(segno, total_segs, msg_seq_num, master_sha) - else: - raise ValueError("Segment has invalid type.") - - # Segment specific data interpretation - segment._addr = addr - segment._unpack_data(stream, data_len) - - return segment - new_from_data = staticmethod(new_from_data) - - def stime(self): - return self._stime - - def address(self): - return self._addr - - def segment_number(self): - return self._segno - - def total_segments(self): - return self._total_segs - - def message_sequence_number(self): - return self._msg_seq_num - - def data(self): - return self._data - - def master_sha(self): - return self._master_sha - - def segment_type(self): - return self._type - - def packetize(self): - """Return a correctly formatted message that can be immediately sent.""" - header = struct.pack(self._HEADER_TEMPLATE, self._MAGIC, self._type, - self._segno, self._total_segs, self._msg_seq_num, self._master_sha) - return header + self._data - - def transmits(self): - return self._transmits - - def inc_transmits(self): - self._transmits = self._transmits + 1 - self._last_transmit = time.time() - - def last_transmit(self): - return self._last_transmit + _MAGIC = 0xbaea4304 + + # 4: magic (0xbaea4304) + # 1: type + # 2: segment number + # 2: total segments + # 2: message sequence number + #20: total data sha1 + _HEADER_TEMPLATE = "! IbHHH20s" + _HEADER_LEN = struct.calcsize(_HEADER_TEMPLATE) + _MTU = _UDP_DATAGRAM_SIZE - _HEADER_LEN + + # Message segment packet types + _SEGMENT_TYPE_DATA = 0 + _SEGMENT_TYPE_RETRANSMIT = 1 + _SEGMENT_TYPE_ACK = 2 + + def magic(): + return SegmentBase._MAGIC + magic = staticmethod(magic) + + def header_template(): + return SegmentBase._HEADER_TEMPLATE + header_template = staticmethod(header_template) + + def type_data(): + return SegmentBase._SEGMENT_TYPE_DATA + type_data = staticmethod(type_data) + + def type_retransmit(): + return SegmentBase._SEGMENT_TYPE_RETRANSMIT + type_retransmit = staticmethod(type_retransmit) + + def type_ack(): + return SegmentBase._SEGMENT_TYPE_ACK + type_ack = staticmethod(type_ack) + + def header_len(): + """Return the header size of SegmentBase packets.""" + return SegmentBase._HEADER_LEN + header_len = staticmethod(header_len) + + def mtu(): + """Return the SegmentBase packet MTU.""" + return SegmentBase._MTU + mtu = staticmethod(mtu) + + def __init__(self, segno, total_segs, msg_seq_num, master_sha): + self._type = None + self._transmits = 0 + self._last_transmit = 0 + self._data = None + self._data_len = 0 + self.userdata = None + self._stime = time.time() + self._addr = None + + # Sanity checks on the message attributes + if not segno or not isinstance(segno, int): + raise ValueError("Segment number must be in integer.") + if segno < 1 or segno > 65535: + raise ValueError("Segment number must be between 1 and 65535 inclusive.") + if not total_segs or not isinstance(total_segs, int): + raise ValueError("Message segment total must be an integer.") + if total_segs < 1 or total_segs > 65535: + raise ValueError("Message must have between 1 and 65535 segments inclusive.") + if segno > total_segs: + raise ValueError("Segment number cannot be larger than message segment total.") + if not msg_seq_num or not isinstance(msg_seq_num, int): + raise ValueError("Message sequnce number must be an integer.") + if msg_seq_num < 1 or msg_seq_num > 65535: + raise ValueError("Message sequence number must be between 1 and 65535 inclusive.") + if not master_sha or not isinstance(master_sha, str) or len(master_sha) != 20: + raise ValueError("Message SHA1 checksum invalid.") + + self._segno = segno + self._total_segs = total_segs + self._msg_seq_num = msg_seq_num + self._master_sha = master_sha + + def _validate_address(addr): + if not addr or not isinstance(addr, tuple): + raise ValueError("Address must be a tuple.") + if len(addr) != 2 or not isinstance(addr[0], str) or not isinstance(addr[1], int): + raise ValueError("Address format was invalid.") + if addr[1] < 1 or addr[1] > 65535: + raise ValueError("Address port was invalid.") + _validate_address = staticmethod(_validate_address) + + def new_from_data(addr, data): + """Static constructor for creation from a packed data stream.""" + SegmentBase._validate_address(addr) + + # Verify minimum length + if not data: + raise ValueError("Segment data is invalid.") + data_len = len(data) + if data_len < SegmentBase.header_len() + 1: + raise ValueError("Segment is less then minimum required length") + if data_len > _UDP_DATAGRAM_SIZE: + raise ValueError("Segment data is larger than allowed.") + stream = StringIO.StringIO(data) + + # Determine and verify the length of included data + stream.seek(0, 2) + data_len = stream.tell() - SegmentBase._HEADER_LEN + stream.seek(0) + + if data_len < 1: + raise ValueError("Segment must have some data.") + if data_len > SegmentBase._MTU: + raise ValueError("Data length must not be larger than the MTU (%s)." % SegmentBase._MTU) + + # Read the first header attributes + (magic, seg_type, segno, total_segs, msg_seq_num, master_sha) = struct.unpack(SegmentBase._HEADER_TEMPLATE, + stream.read(SegmentBase._HEADER_LEN)) + + # Sanity checks on the message attributes + if magic != SegmentBase._MAGIC: + raise ValueError("Segment does not have the correct magic.") + + # if the segment is the only one in the message, validate the data + if segno == 1 and total_segs == 1: + data_sha = _sha_data(stream.read(data_len)) + if data_sha != master_sha: + raise ValueError("Single segment message SHA checksums didn't match.") + stream.seek(SegmentBase._HEADER_LEN) + + if seg_type == SegmentBase._SEGMENT_TYPE_DATA: + segment = DataSegment(segno, total_segs, msg_seq_num, master_sha) + elif seg_type == SegmentBase._SEGMENT_TYPE_RETRANSMIT: + segment = RetransmitSegment(segno, total_segs, msg_seq_num, master_sha) + elif seg_type == SegmentBase._SEGMENT_TYPE_ACK: + segment = AckSegment(segno, total_segs, msg_seq_num, master_sha) + else: + raise ValueError("Segment has invalid type.") + + # Segment specific data interpretation + segment._addr = addr + segment._unpack_data(stream, data_len) + + return segment + new_from_data = staticmethod(new_from_data) + + def stime(self): + return self._stime + + def address(self): + return self._addr + + def segment_number(self): + return self._segno + + def total_segments(self): + return self._total_segs + + def message_sequence_number(self): + return self._msg_seq_num + + def data(self): + return self._data + + def master_sha(self): + return self._master_sha + + def segment_type(self): + return self._type + + def packetize(self): + """Return a correctly formatted message that can be immediately sent.""" + header = struct.pack(self._HEADER_TEMPLATE, self._MAGIC, self._type, + self._segno, self._total_segs, self._msg_seq_num, self._master_sha) + return header + self._data + + def transmits(self): + return self._transmits + + def inc_transmits(self): + self._transmits = self._transmits + 1 + self._last_transmit = time.time() + + def last_transmit(self): + return self._last_transmit class DataSegment(SegmentBase): - """A message segment that encapsulates random data.""" - - def __init__(self, segno, total_segs, msg_seq_num, master_sha): - SegmentBase.__init__(self, segno, total_segs, msg_seq_num, master_sha) - self._type = SegmentBase._SEGMENT_TYPE_DATA - - def _get_template_for_len(length): - return "! %ds" % length - _get_template_for_len = staticmethod(_get_template_for_len) - - def _unpack_data(self, stream, data_len): - """Unpack the data stream, called by constructor.""" - self._data_len = data_len - template = DataSegment._get_template_for_len(self._data_len) - self._data = struct.unpack(template, stream.read(self._data_len))[0] - - def new_from_parts(segno, total_segs, msg_seq_num, master_sha, data): - """Construct a new message segment from individual attributes.""" - if not data: - raise ValueError("Must have valid data.") - segment = DataSegment(segno, total_segs, msg_seq_num, master_sha) - segment._data_len = len(data) - template = DataSegment._get_template_for_len(segment._data_len) - segment._data = struct.pack(template, data) - return segment - new_from_parts = staticmethod(new_from_parts) + """A message segment that encapsulates random data.""" + + def __init__(self, segno, total_segs, msg_seq_num, master_sha): + SegmentBase.__init__(self, segno, total_segs, msg_seq_num, master_sha) + self._type = SegmentBase._SEGMENT_TYPE_DATA + + def _get_template_for_len(length): + return "! %ds" % length + _get_template_for_len = staticmethod(_get_template_for_len) + + def _unpack_data(self, stream, data_len): + """Unpack the data stream, called by constructor.""" + self._data_len = data_len + template = DataSegment._get_template_for_len(self._data_len) + self._data = struct.unpack(template, stream.read(self._data_len))[0] + + def new_from_parts(segno, total_segs, msg_seq_num, master_sha, data): + """Construct a new message segment from individual attributes.""" + if not data: + raise ValueError("Must have valid data.") + segment = DataSegment(segno, total_segs, msg_seq_num, master_sha) + segment._data_len = len(data) + template = DataSegment._get_template_for_len(segment._data_len) + segment._data = struct.pack(template, data) + return segment + new_from_parts = staticmethod(new_from_parts) class RetransmitSegment(SegmentBase): - """A message segment that encapsulates a retransmission request.""" - - # Retransmission data format: - # 2: message sequence number - # 20: total data sha1 - # 2: segment number - _RT_DATA_TEMPLATE = "! H20sH" - _RT_DATA_LEN = struct.calcsize(_RT_DATA_TEMPLATE) - - def data_template(): - return RetransmitSegment._RT_DATA_TEMPLATE - data_template = staticmethod(data_template) - - def __init__(self, segno, total_segs, msg_seq_num, master_sha): - """Should not be called directly.""" - if segno != 1 or total_segs != 1: - raise ValueError("Retransmission request messages must have only one segment.") - - SegmentBase.__init__(self, segno, total_segs, msg_seq_num, master_sha) - self._type = SegmentBase._SEGMENT_TYPE_RETRANSMIT - - def _verify_data(rt_msg_seq_num, rt_master_sha, rt_segment_number): - # Sanity checks on the message attributes - if not rt_segment_number or not isinstance(rt_segment_number, int): - raise ValueError("RT Segment number must be in integer.") - if rt_segment_number < 1 or rt_segment_number > 65535: - raise ValueError("RT Segment number must be between 1 and 65535 inclusive.") - if not rt_msg_seq_num or not isinstance(rt_msg_seq_num, int): - raise ValueError("RT Message sequnce number must be an integer.") - if rt_msg_seq_num < 1 or rt_msg_seq_num > 65535: - raise ValueError("RT Message sequence number must be between 1 and 65535 inclusive.") - if not rt_master_sha or not isinstance(rt_master_sha, str) or len(rt_master_sha) != 20: - raise ValueError("RT Message SHA1 checksum invalid.") - _verify_data = staticmethod(_verify_data) - - def _make_rtms_data(rt_msg_seq_num, rt_master_sha, rt_segment_number): - """Pack retransmission request payload.""" - data = struct.pack(RetransmitSegment._RT_DATA_TEMPLATE, rt_msg_seq_num, - rt_master_sha, rt_segment_number) - return (data, _sha_data(data)) - _make_rtms_data = staticmethod(_make_rtms_data) - - def new_from_parts(addr, msg_seq_num, rt_msg_seq_num, rt_master_sha, rt_segment_number): - """Static constructor for creation from individual attributes.""" - - RetransmitSegment._verify_data(rt_msg_seq_num, rt_master_sha, rt_segment_number) - (data, data_sha) = RetransmitSegment._make_rtms_data(rt_msg_seq_num, - rt_master_sha, rt_segment_number) - segment = RetransmitSegment(1, 1, msg_seq_num, data_sha) - segment._data_len = RetransmitSegment._RT_DATA_LEN - segment._data = data - SegmentBase._validate_address(addr) - segment._addr = addr - - segment._rt_msg_seq_num = rt_msg_seq_num - segment._rt_master_sha = rt_master_sha - segment._rt_segment_number = rt_segment_number - return segment - new_from_parts = staticmethod(new_from_parts) - - def _unpack_data(self, stream, data_len): - if data_len != self._RT_DATA_LEN: - raise ValueError("Retransmission request data had invalid length.") - data = stream.read(data_len) - (rt_msg_seq_num, rt_master_sha, rt_seg_no) = struct.unpack(self._RT_DATA_TEMPLATE, data) - RetransmitSegment._verify_data(rt_msg_seq_num, rt_master_sha, rt_seg_no) - - self._data = data - self._data_len = data_len - self._rt_msg_seq_num = rt_msg_seq_num - self._rt_master_sha = rt_master_sha - self._rt_segment_number = rt_seg_no - - def rt_msg_seq_num(self): - return self._rt_msg_seq_num - - def rt_master_sha(self): - return self._rt_master_sha - - def rt_segment_number(self): - return self._rt_segment_number + """A message segment that encapsulates a retransmission request.""" + + # Retransmission data format: + # 2: message sequence number + # 20: total data sha1 + # 2: segment number + _RT_DATA_TEMPLATE = "! H20sH" + _RT_DATA_LEN = struct.calcsize(_RT_DATA_TEMPLATE) + + def data_template(): + return RetransmitSegment._RT_DATA_TEMPLATE + data_template = staticmethod(data_template) + + def __init__(self, segno, total_segs, msg_seq_num, master_sha): + """Should not be called directly.""" + if segno != 1 or total_segs != 1: + raise ValueError("Retransmission request messages must have only one segment.") + + SegmentBase.__init__(self, segno, total_segs, msg_seq_num, master_sha) + self._type = SegmentBase._SEGMENT_TYPE_RETRANSMIT + + def _verify_data(rt_msg_seq_num, rt_master_sha, rt_segment_number): + # Sanity checks on the message attributes + if not rt_segment_number or not isinstance(rt_segment_number, int): + raise ValueError("RT Segment number must be in integer.") + if rt_segment_number < 1 or rt_segment_number > 65535: + raise ValueError("RT Segment number must be between 1 and 65535 inclusive.") + if not rt_msg_seq_num or not isinstance(rt_msg_seq_num, int): + raise ValueError("RT Message sequnce number must be an integer.") + if rt_msg_seq_num < 1 or rt_msg_seq_num > 65535: + raise ValueError("RT Message sequence number must be between 1 and 65535 inclusive.") + if not rt_master_sha or not isinstance(rt_master_sha, str) or len(rt_master_sha) != 20: + raise ValueError("RT Message SHA1 checksum invalid.") + _verify_data = staticmethod(_verify_data) + + def _make_rtms_data(rt_msg_seq_num, rt_master_sha, rt_segment_number): + """Pack retransmission request payload.""" + data = struct.pack(RetransmitSegment._RT_DATA_TEMPLATE, rt_msg_seq_num, + rt_master_sha, rt_segment_number) + return (data, _sha_data(data)) + _make_rtms_data = staticmethod(_make_rtms_data) + + def new_from_parts(addr, msg_seq_num, rt_msg_seq_num, rt_master_sha, rt_segment_number): + """Static constructor for creation from individual attributes.""" + + RetransmitSegment._verify_data(rt_msg_seq_num, rt_master_sha, rt_segment_number) + (data, data_sha) = RetransmitSegment._make_rtms_data(rt_msg_seq_num, + rt_master_sha, rt_segment_number) + segment = RetransmitSegment(1, 1, msg_seq_num, data_sha) + segment._data_len = RetransmitSegment._RT_DATA_LEN + segment._data = data + SegmentBase._validate_address(addr) + segment._addr = addr + + segment._rt_msg_seq_num = rt_msg_seq_num + segment._rt_master_sha = rt_master_sha + segment._rt_segment_number = rt_segment_number + return segment + new_from_parts = staticmethod(new_from_parts) + + def _unpack_data(self, stream, data_len): + if data_len != self._RT_DATA_LEN: + raise ValueError("Retransmission request data had invalid length.") + data = stream.read(data_len) + (rt_msg_seq_num, rt_master_sha, rt_seg_no) = struct.unpack(self._RT_DATA_TEMPLATE, data) + RetransmitSegment._verify_data(rt_msg_seq_num, rt_master_sha, rt_seg_no) + + self._data = data + self._data_len = data_len + self._rt_msg_seq_num = rt_msg_seq_num + self._rt_master_sha = rt_master_sha + self._rt_segment_number = rt_seg_no + + def rt_msg_seq_num(self): + return self._rt_msg_seq_num + + def rt_master_sha(self): + return self._rt_master_sha + + def rt_segment_number(self): + return self._rt_segment_number class AckSegment(SegmentBase): - """A message segment that encapsulates a message acknowledgement.""" - - # Ack data format: - # 2: acked message sequence number - # 20: acked message total data sha1 - # 4: acked message source IP address - _ACK_DATA_TEMPLATE = "! H20s4s" - _ACK_DATA_LEN = struct.calcsize(_ACK_DATA_TEMPLATE) - - def data_template(): - return AckSegment._ACK_DATA_TEMPLATE - data_template = staticmethod(data_template) - - def __init__(self, segno, total_segs, msg_seq_num, master_sha): - """Should not be called directly.""" - if segno != 1 or total_segs != 1: - raise ValueError("Acknowledgement messages must have only one segment.") - - SegmentBase.__init__(self, segno, total_segs, msg_seq_num, master_sha) - self._type = SegmentBase._SEGMENT_TYPE_ACK - - def _verify_data(ack_msg_seq_num, ack_master_sha, ack_addr): - # Sanity checks on the message attributes - if not ack_msg_seq_num or not isinstance(ack_msg_seq_num, int): - raise ValueError("Ack message sequnce number must be an integer.") - if ack_msg_seq_num < 1 or ack_msg_seq_num > 65535: - raise ValueError("Ack message sequence number must be between 1 and 65535 inclusive.") - if not ack_master_sha or not isinstance(ack_master_sha, str) or len(ack_master_sha) != 20: - raise ValueError("Ack message SHA1 checksum invalid.") - if not isinstance(ack_addr, str): - raise ValueError("Ack message invalid address type.") - try: - foo = socket.inet_aton(ack_addr) - except socket.error: - raise ValueError("Ack message invalid address.") - _verify_data = staticmethod(_verify_data) - - def _make_ack_data(ack_msg_seq_num, ack_master_sha, ack_addr): - """Pack an ack payload.""" - addr_data = socket.inet_aton(ack_addr) - data = struct.pack(AckSegment._ACK_DATA_TEMPLATE, ack_msg_seq_num, - ack_master_sha, addr_data) - return (data, _sha_data(data)) - _make_ack_data = staticmethod(_make_ack_data) - - def new_from_parts(addr, msg_seq_num, ack_msg_seq_num, ack_master_sha, ack_addr): - """Static constructor for creation from individual attributes.""" - - AckSegment._verify_data(ack_msg_seq_num, ack_master_sha, ack_addr) - (data, data_sha) = AckSegment._make_ack_data(ack_msg_seq_num, - ack_master_sha, ack_addr) - segment = AckSegment(1, 1, msg_seq_num, data_sha) - segment._data_len = AckSegment._ACK_DATA_LEN - segment._data = data - SegmentBase._validate_address(addr) - segment._addr = addr - - segment._ack_msg_seq_num = ack_msg_seq_num - segment._ack_master_sha = ack_master_sha - segment._ack_addr = ack_addr - return segment - new_from_parts = staticmethod(new_from_parts) - - def _unpack_data(self, stream, data_len): - if data_len != self._ACK_DATA_LEN: - raise ValueError("Ack segment data had invalid length.") - data = stream.read(data_len) - (ack_msg_seq_num, ack_master_sha, ack_addr_data) = struct.unpack(self._ACK_DATA_TEMPLATE, data) - try: - ack_addr = socket.inet_ntoa(ack_addr_data) - except socket.error: - raise ValueError("Ack segment data had invalid address.") - AckSegment._verify_data(ack_msg_seq_num, ack_master_sha, ack_addr) - - self._data = data - self._data_len = data_len - self._ack_msg_seq_num = ack_msg_seq_num - self._ack_master_sha = ack_master_sha - self._ack_addr = ack_addr - - def ack_msg_seq_num(self): - return self._ack_msg_seq_num - - def ack_master_sha(self): - return self._ack_master_sha - - def ack_addr(self): - return self._ack_addr + """A message segment that encapsulates a message acknowledgement.""" + + # Ack data format: + # 2: acked message sequence number + # 20: acked message total data sha1 + # 4: acked message source IP address + _ACK_DATA_TEMPLATE = "! H20s4s" + _ACK_DATA_LEN = struct.calcsize(_ACK_DATA_TEMPLATE) + + def data_template(): + return AckSegment._ACK_DATA_TEMPLATE + data_template = staticmethod(data_template) + + def __init__(self, segno, total_segs, msg_seq_num, master_sha): + """Should not be called directly.""" + if segno != 1 or total_segs != 1: + raise ValueError("Acknowledgement messages must have only one segment.") + + SegmentBase.__init__(self, segno, total_segs, msg_seq_num, master_sha) + self._type = SegmentBase._SEGMENT_TYPE_ACK + + def _verify_data(ack_msg_seq_num, ack_master_sha, ack_addr): + # Sanity checks on the message attributes + if not ack_msg_seq_num or not isinstance(ack_msg_seq_num, int): + raise ValueError("Ack message sequnce number must be an integer.") + if ack_msg_seq_num < 1 or ack_msg_seq_num > 65535: + raise ValueError("Ack message sequence number must be between 1 and 65535 inclusive.") + if not ack_master_sha or not isinstance(ack_master_sha, str) or len(ack_master_sha) != 20: + raise ValueError("Ack message SHA1 checksum invalid.") + if not isinstance(ack_addr, str): + raise ValueError("Ack message invalid address type.") + try: + foo = socket.inet_aton(ack_addr) + except socket.error: + raise ValueError("Ack message invalid address.") + _verify_data = staticmethod(_verify_data) + + def _make_ack_data(ack_msg_seq_num, ack_master_sha, ack_addr): + """Pack an ack payload.""" + addr_data = socket.inet_aton(ack_addr) + data = struct.pack(AckSegment._ACK_DATA_TEMPLATE, ack_msg_seq_num, + ack_master_sha, addr_data) + return (data, _sha_data(data)) + _make_ack_data = staticmethod(_make_ack_data) + + def new_from_parts(addr, msg_seq_num, ack_msg_seq_num, ack_master_sha, ack_addr): + """Static constructor for creation from individual attributes.""" + + AckSegment._verify_data(ack_msg_seq_num, ack_master_sha, ack_addr) + (data, data_sha) = AckSegment._make_ack_data(ack_msg_seq_num, + ack_master_sha, ack_addr) + segment = AckSegment(1, 1, msg_seq_num, data_sha) + segment._data_len = AckSegment._ACK_DATA_LEN + segment._data = data + SegmentBase._validate_address(addr) + segment._addr = addr + + segment._ack_msg_seq_num = ack_msg_seq_num + segment._ack_master_sha = ack_master_sha + segment._ack_addr = ack_addr + return segment + new_from_parts = staticmethod(new_from_parts) + + def _unpack_data(self, stream, data_len): + if data_len != self._ACK_DATA_LEN: + raise ValueError("Ack segment data had invalid length.") + data = stream.read(data_len) + (ack_msg_seq_num, ack_master_sha, ack_addr_data) = struct.unpack(self._ACK_DATA_TEMPLATE, data) + try: + ack_addr = socket.inet_ntoa(ack_addr_data) + except socket.error: + raise ValueError("Ack segment data had invalid address.") + AckSegment._verify_data(ack_msg_seq_num, ack_master_sha, ack_addr) + + self._data = data + self._data_len = data_len + self._ack_msg_seq_num = ack_msg_seq_num + self._ack_master_sha = ack_master_sha + self._ack_addr = ack_addr + + def ack_msg_seq_num(self): + return self._ack_msg_seq_num + + def ack_master_sha(self): + return self._ack_master_sha + + def ack_addr(self): + return self._ack_addr class Message(object): - """Tracks an entire message object, which is composed of a number - of individual segments.""" - def __init__(self, src_addr, msg_seq_num, msg_sha, total_segments): - self._rt_target = 0 - self._next_rt_time = 0 - self._last_incoming_time = 0 - self._segments = {} - self._complete = False - self._dispatched_time = 0 - self._data = None - self._data_sha = None - self._src_addr = src_addr - self._msg_seq_num = msg_seq_num - self._msg_sha = msg_sha - self._total_segments = total_segments - self._rt_tries = {} - for i in range(1, self._total_segments + 1): - self._rt_tries[i] = 0 - - def __del__(self): - self.clear() - - def sha(self): - return self._msg_sha - - def source_address(self): - return self._src_addr - - def clear(self): - for key in self._segments.keys()[:]: - del self._segments[key] - del self._rt_tries[key] - self._segments = {} - self._rt_tries = {} - - def has_segment(self, segno): - return self._segments.has_key(segno) - - def first_missing(self): - for i in range(1, self._total_segments + 1): - if not self._segments.has_key(i): - return i - return 0 - - _DEF_RT_REQUEST_INTERVAL = 0.09 # 70ms (in seconds) - def update_rt_wait(self, now): - """now argument should be in seconds.""" - wait = self._DEF_RT_REQUEST_INTERVAL - if self._last_incoming_time > now - 0.02: - msg_completeness = float(len(self._segments)) / float(self._total_segments) - wait = wait + (self._DEF_RT_REQUEST_INTERVAL * (1.0 - msg_completeness)) - self._next_rt_time = now + wait - - def add_segment(self, segment): - if self.complete(): - return - segno = segment.segment_number() - if self._segments.has_key(segno): - return - self._segments[segno] = segment - self._rt_tries[segno] = 0 - now = time.time() - self._last_incoming_time = now - - num_segs = len(self._segments) - if num_segs == self._total_segments: - self._complete = True - self._next_rt_time = 0 - self._data = '' - for seg in self._segments.values(): - self._data = self._data + seg.data() - self._data_sha = _sha_data(self._data) - elif segno == num_segs or num_segs == 1: - # If we're not missing segments, push back retransmit request - self.update_rt_wait(now) - - def get_retransmit_message(self, msg_seq_num, segno): - if segno < 1 or segno > self._total_segments: - return None - seg = RetransmitSegment.new_from_parts(self._src_addr, msg_seq_num, - self._msg_seq_num, self._msg_sha, segno) - self._rt_tries[segno] = self._rt_tries[segno] + 1 - self.update_rt_wait(time.time()) - return seg - - def complete(self): - return self._complete - - def dispatch_time(self): - return self._dispatch_time - - def set_dispatch_time(self): - self._dispatch_time = time.time() - - def data(self): - return (self._data, self._data_sha) - - def last_incoming_time(self): - return self._last_incoming_time - - def next_rt_time(self): - return self._next_rt_time - - def rt_tries(self, segno): - if self._rt_tries.has_key(segno): - return self._rt_tries[segno] - return 0 + """Tracks an entire message object, which is composed of a number + of individual segments.""" + def __init__(self, src_addr, msg_seq_num, msg_sha, total_segments): + self._rt_target = 0 + self._next_rt_time = 0 + self._last_incoming_time = 0 + self._segments = {} + self._complete = False + self._dispatched_time = 0 + self._data = None + self._data_sha = None + self._src_addr = src_addr + self._msg_seq_num = msg_seq_num + self._msg_sha = msg_sha + self._total_segments = total_segments + self._rt_tries = {} + for i in range(1, self._total_segments + 1): + self._rt_tries[i] = 0 + + def __del__(self): + self.clear() + + def sha(self): + return self._msg_sha + + def source_address(self): + return self._src_addr + + def clear(self): + for key in self._segments.keys()[:]: + del self._segments[key] + del self._rt_tries[key] + self._segments = {} + self._rt_tries = {} + + def has_segment(self, segno): + return self._segments.has_key(segno) + + def first_missing(self): + for i in range(1, self._total_segments + 1): + if not self._segments.has_key(i): + return i + return 0 + + _DEF_RT_REQUEST_INTERVAL = 0.09 # 70ms (in seconds) + def update_rt_wait(self, now): + """now argument should be in seconds.""" + wait = self._DEF_RT_REQUEST_INTERVAL + if self._last_incoming_time > now - 0.02: + msg_completeness = float(len(self._segments)) / float(self._total_segments) + wait = wait + (self._DEF_RT_REQUEST_INTERVAL * (1.0 - msg_completeness)) + self._next_rt_time = now + wait + + def add_segment(self, segment): + if self.complete(): + return + segno = segment.segment_number() + if self._segments.has_key(segno): + return + self._segments[segno] = segment + self._rt_tries[segno] = 0 + now = time.time() + self._last_incoming_time = now + + num_segs = len(self._segments) + if num_segs == self._total_segments: + self._complete = True + self._next_rt_time = 0 + self._data = '' + for seg in self._segments.values(): + self._data = self._data + seg.data() + self._data_sha = _sha_data(self._data) + elif segno == num_segs or num_segs == 1: + # If we're not missing segments, push back retransmit request + self.update_rt_wait(now) + + def get_retransmit_message(self, msg_seq_num, segno): + if segno < 1 or segno > self._total_segments: + return None + seg = RetransmitSegment.new_from_parts(self._src_addr, msg_seq_num, + self._msg_seq_num, self._msg_sha, segno) + self._rt_tries[segno] = self._rt_tries[segno] + 1 + self.update_rt_wait(time.time()) + return seg + + def complete(self): + return self._complete + + def dispatch_time(self): + return self._dispatch_time + + def set_dispatch_time(self): + self._dispatch_time = time.time() + + def data(self): + return (self._data, self._data_sha) + + def last_incoming_time(self): + return self._last_incoming_time + + def next_rt_time(self): + return self._next_rt_time + + def rt_tries(self, segno): + if self._rt_tries.has_key(segno): + return self._rt_tries[segno] + return 0 def _get_local_interfaces(): - import array - import struct - import fcntl - import socket + import array + import struct + import fcntl + import socket - max_possible = 4 - bytes = max_possible * 32 - SIOCGIFCONF = 0x8912 - names = array.array('B', '\0' * bytes) + max_possible = 4 + bytes = max_possible * 32 + SIOCGIFCONF = 0x8912 + names = array.array('B', '\0' * bytes) - sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - ifreq = struct.pack('iL', bytes, names.buffer_info()[0]) - result = fcntl.ioctl(sockfd.fileno(), SIOCGIFCONF, ifreq) - sockfd.close() + sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + ifreq = struct.pack('iL', bytes, names.buffer_info()[0]) + result = fcntl.ioctl(sockfd.fileno(), SIOCGIFCONF, ifreq) + sockfd.close() - outbytes = struct.unpack('iL', result)[0] - namestr = names.tostring() + outbytes = struct.unpack('iL', result)[0] + namestr = names.tostring() - return [namestr[i:i+32].split('\0', 1)[0] for i in range(0, outbytes, 32)] + return [namestr[i:i+32].split('\0', 1)[0] for i in range(0, outbytes, 32)] def _get_local_ip_addresses(): - """Call Linux specific bits to retrieve our own IP address.""" - import socket - import sys - import fcntl - import struct - - intfs = _get_local_interfaces() - - ips = [] - SIOCGIFADDR = 0x8915 - sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - for intf in intfs: - if intf == "lo": - continue - try: - ifreq = (intf + '\0'*32)[:32] - result = fcntl.ioctl(sockfd.fileno(), SIOCGIFADDR, ifreq) - addr = socket.inet_ntoa(result[20:24]) - ips.append(addr) - except IOError, exc: - print "Error getting IP address: %s" % exc - sockfd.close() - return ips + """Call Linux specific bits to retrieve our own IP address.""" + import socket + import sys + import fcntl + import struct + + intfs = _get_local_interfaces() + + ips = [] + SIOCGIFADDR = 0x8915 + sockfd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + for intf in intfs: + if intf == "lo": + continue + try: + ifreq = (intf + '\0'*32)[:32] + result = fcntl.ioctl(sockfd.fileno(), SIOCGIFADDR, ifreq) + addr = socket.inet_ntoa(result[20:24]) + ips.append(addr) + except IOError, exc: + print "Error getting IP address: %s" % exc + sockfd.close() + return ips class MostlyReliablePipe(object): - """Implement Mostly-Reliable UDP. We don't actually care about guaranteeing - delivery or receipt, just a better effort than no effort at all.""" - - _UDP_MSG_SIZE = SegmentBase.mtu() + SegmentBase.header_len() - _SEGMENT_TTL = 120 # 2 minutes - - def __init__(self, local_addr, remote_addr, port, data_cb, user_data=None): - self._local_addr = local_addr - self._remote_addr = remote_addr - self._port = port - self._data_cb = data_cb - self._user_data = user_data - self._started = False - self._send_worker = 0 - self._seq_counter = 0 - self._drop_prob = 0 - self._rt_check_worker_id = 0 - - self._outgoing = [] - self._sent = {} - - self._incoming = {} # (message sha, # of segments) -> [segment1, segment2, ...] - self._dispatched = {} - self._acks = {} # (message sequence #, master sha, source addr) -> received timestamp - self._ack_check_worker_id = 0 - - self._local_ips = _get_local_ip_addresses() - - self._setup_listener() - self._setup_sender() - - def __del__(self): - if self._send_worker > 0: - gobject.source_remove(self._send_worker) - self._send_worker = 0 - if self._rt_check_worker_id > 0: - gobject.source_remove(self._rt_check_worker_id) - self._rt_check_worker_id = 0 - if self._ack_check_worker_id > 0: - gobject.source_remove(self._ack_check_worker_id) - self._ack_check_worker_id = 0 - - def _setup_sender(self): - """Setup the send socket for multicast.""" - self._send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Make the socket multicast-aware, and set TTL. - self._send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) # Change TTL (=20) to suit - - def _setup_listener(self): - """Set up the listener socket for multicast traffic.""" - # Listener socket - self._listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - - # Set some options to make it multicast-friendly - self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) - - def start(self): - """Let the listener socket start listening for network data.""" - # Set some more multicast options - self._listen_sock.bind((self._local_addr, self._port)) - self._listen_sock.settimeout(2) + """Implement Mostly-Reliable UDP. We don't actually care about guaranteeing + delivery or receipt, just a better effort than no effort at all.""" + + _UDP_MSG_SIZE = SegmentBase.mtu() + SegmentBase.header_len() + _SEGMENT_TTL = 120 # 2 minutes + + def __init__(self, local_addr, remote_addr, port, data_cb, user_data=None): + self._local_addr = local_addr + self._remote_addr = remote_addr + self._port = port + self._data_cb = data_cb + self._user_data = user_data + self._started = False + self._send_worker = 0 + self._seq_counter = 0 + self._drop_prob = 0 + self._rt_check_worker_id = 0 + + self._outgoing = [] + self._sent = {} + + self._incoming = {} # (message sha, # of segments) -> [segment1, segment2, ...] + self._dispatched = {} + self._acks = {} # (message sequence #, master sha, source addr) -> received timestamp + self._ack_check_worker_id = 0 + + self._local_ips = _get_local_ip_addresses() + + self._setup_listener() + self._setup_sender() + + def __del__(self): + if self._send_worker > 0: + gobject.source_remove(self._send_worker) + self._send_worker = 0 + if self._rt_check_worker_id > 0: + gobject.source_remove(self._rt_check_worker_id) + self._rt_check_worker_id = 0 + if self._ack_check_worker_id > 0: + gobject.source_remove(self._ack_check_worker_id) + self._ack_check_worker_id = 0 + + def _setup_sender(self): + """Setup the send socket for multicast.""" + self._send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + # Make the socket multicast-aware, and set TTL. + self._send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) # Change TTL (=20) to suit + + def _setup_listener(self): + """Set up the listener socket for multicast traffic.""" + # Listener socket + self._listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + # Set some options to make it multicast-friendly + self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) + self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) + + def start(self): + """Let the listener socket start listening for network data.""" + # Set some more multicast options + self._listen_sock.bind((self._local_addr, self._port)) + self._listen_sock.settimeout(2) # Disable for now to try to fix "cannot assign requested address" errors -# intf = socket.gethostbyname(socket.gethostname()) -# self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, -# socket.inet_aton(intf) + socket.inet_aton('0.0.0.0')) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, - socket.inet_aton(self._remote_addr) + socket.inet_aton('0.0.0.0')) - - # Watch the listener socket for data - gobject.io_add_watch(self._listen_sock, gobject.IO_IN, self._handle_incoming_data) - gobject.timeout_add(self._SEGMENT_TTL * 1000, self._segment_ttl_worker) - self._rt_check_worker_id = gobject.timeout_add(50, self._retransmit_check_worker) - self._ack_check_worker_id = gobject.timeout_add(50, self._ack_check_worker) - - self._started = True - - def _segment_ttl_worker(self): - """Cull already-sent message segments that are past their TTL.""" - now = time.time() - for key in self._sent.keys()[:]: - segment = self._sent[key] - if segment.stime() < now - self._SEGMENT_TTL: - if segment.userdata: - gobject.source_remove(segment.userdata) - del self._sent[key] - - # Cull incomplete incoming segment chains that haven't gotten any data - # for a long time either - for msg_key in self._incoming.keys()[:]: - message = self._incoming[msg_key] - if message.last_incoming_time() < now - self._SEGMENT_TTL: - del self._incoming[msg_key] - - # Remove already received and dispatched messages after a while - for msg_key in self._dispatched.keys()[:]: - message = self._dispatched[msg_key] - if message.dispatch_time() < now - (self._SEGMENT_TTL*2): - del self._dispatched[msg_key] - - # Remove received acks after a while - for ack_key in self._acks.keys()[:]: - ack_time = self._acks[ack_key] - if ack_time < now - (self._SEGMENT_TTL*2): - del self._acks[ack_key] - - return True - - _MAX_SEGMENT_RETRIES = 10 - def _retransmit_request(self, message): - """Returns true if the message has exceeded it's retry limit.""" - first_missing = message.first_missing() - if first_missing > 0: - num_retries = message.rt_tries(first_missing) - if num_retries > self._MAX_SEGMENT_RETRIES: - return True - msg_seq = self._next_msg_seq() - seg = message.get_retransmit_message(msg_seq, first_missing) - if seg: - print "(MRP): Requesting retransmit of %d by %s" % (first_missing, message.source_address()) - self._outgoing.append(seg) - self._schedule_send_worker() - return False - - def _retransmit_check_worker(self): - """Periodically check for and send retransmit requests for message - segments that got lost.""" - try: - now = time.time() - for key in self._incoming.keys()[:]: - message = self._incoming[key] - if message.complete(): - continue - next_rt = message.next_rt_time() - if next_rt == 0 or next_rt > now: - continue - if self._retransmit_request(message): - # Kill the message, too many retries - print "(MRP): Dropped message %s, exceeded retries." % _stringify_sha(message.sha()) - self._dispatched[key] = message - message.set_dispatch_time() - del self._incoming[key] - except KeyboardInterrupt: - return False - return True - - def _process_incoming_data(self, segment): - """Handle a new message segment. First checks if there is only one - segment to the message, and if the checksum from the header matches - that computed from the data, dispatches it. Otherwise, it adds the - new segment to the list of other segments for that message, and - checks to see if the message is complete. If all segments are present, - the message is reassembled and dispatched.""" - - msg_sha = segment.master_sha() - nsegs = segment.total_segments() - addr = segment.address() - segno = segment.segment_number() - - msg_seq_num = segment.message_sequence_number() - msg_key = (addr[0], msg_seq_num, msg_sha, nsegs) - - if self._dispatched.has_key(msg_key): - # We already dispatched this message, this segment is useless - return - # First segment in the message - if not self._incoming.has_key(msg_key): - self._incoming[msg_key] = Message((addr[0], self._port), msg_seq_num, msg_sha, nsegs) - # Acknowledge the message if it didn't come from us - if addr[0] not in self._local_ips: - ack_key = (msg_seq_num, msg_sha, addr[0]) - if not self._acks.has_key(ack_key): - self._send_ack_for_message(msg_seq_num, msg_sha, addr[0]) - - message = self._incoming[msg_key] - # Look for a dupe, and if so, drop the new segment - if message.has_segment(segno): - return - message.add_segment(segment) - - # Dispatch the message if all segments are present and the sha is correct - if message.complete(): - (msg_data, complete_data_sha) = message.data() - if msg_sha == complete_data_sha: - self._data_cb(addr, msg_data, self._user_data) - self._dispatched[msg_key] = message - message.set_dispatch_time() - del self._incoming[msg_key] - return - - def _segment_retransmit_cb(self, key, segment): - """Add a segment ot the outgoing queue and schedule its transmission.""" - del self._sent[key] - self._outgoing.append(segment) - self._schedule_send_worker() - return False - - def _schedule_segment_retransmit(self, key, segment, when, now): - """Schedule retransmission of a segment if one is not already scheduled.""" - if segment.userdata: - # Already scheduled for retransmit - return - - if when <= now: - # Immediate retransmission - self._segment_retransmit_cb(key, segment) - else: - # convert time to milliseconds - timeout = int((when - now) * 1000) - segment.userdata = gobject.timeout_add(timeout, self._segment_retransmit_cb, - key, segment) - - _STD_RETRANSMIT_INTERVAL = 0.05 # 50ms (in seconds) - def _process_retransmit_request(self, segment): - """Validate and process a retransmission request.""" - key = (segment.rt_msg_seq_num(), segment.rt_master_sha(), segment.rt_segment_number()) - if not self._sent.has_key(key): - # Either we don't know about the segment, or it was already culled - return - - # Calculate next retransmission time and schedule packet for retransmit - segment = self._sent[key] - # only retransmit segments every 150ms or more - now = time.time() - next_transmit = max(now, segment.last_transmit() + self._STD_RETRANSMIT_INTERVAL) - self._schedule_segment_retransmit(key, segment, next_transmit, now) - - def _ack_check_worker(self): - """Periodically check for messages that haven't received an ack - yet, and retransmit them.""" - try: - now = time.time() - for key in self._sent.keys()[:]: - segment = self._sent[key] - # We only care about retransmitting the first segment - # of a message, since if other machines don't have the - # rest of the segments, they'll issue retransmit requests - if segment.segment_number() != 1: - continue - if segment.last_transmit() > now - 0.150: # 150ms - # Was just retransmitted recently, wait longer - # before retransmitting it - continue - ack_key = None - for ip in self._local_ips: - ack_key = (segment.message_sequence_number(), segment.master_sha(), ip) - if self._acks.has_key(ack_key): - break - ack_key = None - # If the segment already has been acked, don't send it - # again unless somebody explicitly requests a retransmit - if ack_key is not None: - continue - - del self._sent[key] - self._outgoing.append(segment) - self._schedule_send_worker() - except KeyboardInterrupt: - return False - return True - - def _send_ack_for_message(self, ack_msg_seq_num, ack_msg_sha, ack_addr): - """Send an ack segment for a message.""" - msg_seq_num = self._next_msg_seq() - full_remote_addr = (self._remote_addr, self._port) - ack = AckSegment.new_from_parts(full_remote_addr, msg_seq_num, - ack_msg_seq_num, ack_msg_sha, ack_addr) - self._outgoing.append(ack) - self._schedule_send_worker() - self._process_incoming_ack(ack) - - def _process_incoming_ack(self, segment): - """Save the ack so that we don't send an ack when we start getting the segments - the ack was acknowledging.""" - # If the ack is supposed to be for a message we sent, only accept it if - # we actually sent the message to which it refers - ack_addr = segment.ack_addr() - ack_master_sha = segment.ack_master_sha() - ack_msg_seq_num = segment.ack_msg_seq_num() - if ack_addr in self._local_ips: - sent_key = (ack_msg_seq_num, ack_master_sha, 1) - if not self._sent.has_key(sent_key): - return - ack_key = (ack_msg_seq_num, ack_master_sha, ack_addr) - if not self._acks.has_key(ack_key): - self._acks[ack_key] = time.time() - - def set_drop_probability(self, prob=4): - """Debugging function to randomly drop incoming packets. - The prob argument should be an integer between 1 and 10 to drop, - or 0 to drop none. Higher numbers drop more packets.""" - if not isinstance(prob, int): - raise ValueError("Drop probability must be an integer.") - if prob < 1 or prob > 10: - raise ValueError("Drop probability must be between 1 and 10 inclusive.") - self._drop_prob = prob - - def _handle_incoming_data(self, source, condition): - """Handle incoming network data by making a message segment out of it - sending it off to the processing function.""" - if not (condition & gobject.IO_IN): - return True - msg = {} - data, addr = source.recvfrom(self._UDP_MSG_SIZE) - - should_drop = False - p = random.random() * 10.0 - if self._drop_prob > 0 and p <= self._drop_prob: - should_drop = True - - try: - segment = SegmentBase.new_from_data(addr, data) - if should_drop: - print "(MRP): Dropped segment %d." % segment.segment_number() - else: - stype = segment.segment_type() - if stype == SegmentBase.type_data(): - self._process_incoming_data(segment) - elif stype == SegmentBase.type_retransmit(): - self._process_retransmit_request(segment) - elif stype == SegmentBase.type_ack(): - self._process_incoming_ack(segment) - except ValueError, exc: - print "(MRP): Bad segment: %s" % exc - return True - - def _next_msg_seq(self): - self._seq_counter = self._seq_counter + 1 - if self._seq_counter > 65535: - self._seq_counter = 1 - return self._seq_counter - - def send(self, data): - """Break data up into chunks and queue for later transmission.""" - if not self._started: - raise Exception("Can't send anything until started!") - - msg_seq = self._next_msg_seq() - - # Pack the data into network byte order - template = "! %ds" % len(str(data)) - data = struct.pack(template, str(data)) - master_sha = _sha_data(data) - - # Split up the data into segments - left = length = len(data) - mtu = SegmentBase.mtu() - nmessages = length / mtu - if length % mtu > 0: - nmessages = nmessages + 1 - seg_num = 1 - while left > 0: - seg = DataSegment.new_from_parts(seg_num, nmessages, - msg_seq, master_sha, data[:mtu]) - self._outgoing.append(seg) - seg_num = seg_num + 1 - data = data[mtu:] - left = left - mtu - self._schedule_send_worker() - - def _schedule_send_worker(self): - if len(self._outgoing) > 0 and self._send_worker == 0: - self._send_worker = gobject.timeout_add(50, self._send_worker_cb) - - def _send_worker_cb(self): - """Send all queued segments that have yet to be transmitted.""" - self._send_worker = 0 - nsent = 0 - for segment in self._outgoing: - packet = segment.packetize() - segment.inc_transmits() - addr = (self._remote_addr, self._port) - if segment.address(): - addr = segment.address() - self._send_sock.sendto(packet, addr) - if segment.userdata: - gobject.source_remove(segment.userdata) - segment.userdata = None # Retransmission GSource - key = (segment.message_sequence_number(), segment.master_sha(), segment.segment_number()) - self._sent[key] = segment - nsent = nsent + 1 - if nsent > 10: - break - self._outgoing = self._outgoing[nsent:] - if len(self._outgoing): - self._schedule_send_worker() - return False +# intf = socket.gethostbyname(socket.gethostname()) +# self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, +# socket.inet_aton(intf) + socket.inet_aton('0.0.0.0')) + self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, + socket.inet_aton(self._remote_addr) + socket.inet_aton('0.0.0.0')) + + # Watch the listener socket for data + gobject.io_add_watch(self._listen_sock, gobject.IO_IN, self._handle_incoming_data) + gobject.timeout_add(self._SEGMENT_TTL * 1000, self._segment_ttl_worker) + self._rt_check_worker_id = gobject.timeout_add(50, self._retransmit_check_worker) + self._ack_check_worker_id = gobject.timeout_add(50, self._ack_check_worker) + + self._started = True + + def _segment_ttl_worker(self): + """Cull already-sent message segments that are past their TTL.""" + now = time.time() + for key in self._sent.keys()[:]: + segment = self._sent[key] + if segment.stime() < now - self._SEGMENT_TTL: + if segment.userdata: + gobject.source_remove(segment.userdata) + del self._sent[key] + + # Cull incomplete incoming segment chains that haven't gotten any data + # for a long time either + for msg_key in self._incoming.keys()[:]: + message = self._incoming[msg_key] + if message.last_incoming_time() < now - self._SEGMENT_TTL: + del self._incoming[msg_key] + + # Remove already received and dispatched messages after a while + for msg_key in self._dispatched.keys()[:]: + message = self._dispatched[msg_key] + if message.dispatch_time() < now - (self._SEGMENT_TTL*2): + del self._dispatched[msg_key] + + # Remove received acks after a while + for ack_key in self._acks.keys()[:]: + ack_time = self._acks[ack_key] + if ack_time < now - (self._SEGMENT_TTL*2): + del self._acks[ack_key] + + return True + + _MAX_SEGMENT_RETRIES = 10 + def _retransmit_request(self, message): + """Returns true if the message has exceeded it's retry limit.""" + first_missing = message.first_missing() + if first_missing > 0: + num_retries = message.rt_tries(first_missing) + if num_retries > self._MAX_SEGMENT_RETRIES: + return True + msg_seq = self._next_msg_seq() + seg = message.get_retransmit_message(msg_seq, first_missing) + if seg: + print "(MRP): Requesting retransmit of %d by %s" % (first_missing, message.source_address()) + self._outgoing.append(seg) + self._schedule_send_worker() + return False + + def _retransmit_check_worker(self): + """Periodically check for and send retransmit requests for message + segments that got lost.""" + try: + now = time.time() + for key in self._incoming.keys()[:]: + message = self._incoming[key] + if message.complete(): + continue + next_rt = message.next_rt_time() + if next_rt == 0 or next_rt > now: + continue + if self._retransmit_request(message): + # Kill the message, too many retries + print "(MRP): Dropped message %s, exceeded retries." % _stringify_sha(message.sha()) + self._dispatched[key] = message + message.set_dispatch_time() + del self._incoming[key] + except KeyboardInterrupt: + return False + return True + + def _process_incoming_data(self, segment): + """Handle a new message segment. First checks if there is only one + segment to the message, and if the checksum from the header matches + that computed from the data, dispatches it. Otherwise, it adds the + new segment to the list of other segments for that message, and + checks to see if the message is complete. If all segments are present, + the message is reassembled and dispatched.""" + + msg_sha = segment.master_sha() + nsegs = segment.total_segments() + addr = segment.address() + segno = segment.segment_number() + + msg_seq_num = segment.message_sequence_number() + msg_key = (addr[0], msg_seq_num, msg_sha, nsegs) + + if self._dispatched.has_key(msg_key): + # We already dispatched this message, this segment is useless + return + # First segment in the message + if not self._incoming.has_key(msg_key): + self._incoming[msg_key] = Message((addr[0], self._port), msg_seq_num, msg_sha, nsegs) + # Acknowledge the message if it didn't come from us + if addr[0] not in self._local_ips: + ack_key = (msg_seq_num, msg_sha, addr[0]) + if not self._acks.has_key(ack_key): + self._send_ack_for_message(msg_seq_num, msg_sha, addr[0]) + + message = self._incoming[msg_key] + # Look for a dupe, and if so, drop the new segment + if message.has_segment(segno): + return + message.add_segment(segment) + + # Dispatch the message if all segments are present and the sha is correct + if message.complete(): + (msg_data, complete_data_sha) = message.data() + if msg_sha == complete_data_sha: + self._data_cb(addr, msg_data, self._user_data) + self._dispatched[msg_key] = message + message.set_dispatch_time() + del self._incoming[msg_key] + return + + def _segment_retransmit_cb(self, key, segment): + """Add a segment ot the outgoing queue and schedule its transmission.""" + del self._sent[key] + self._outgoing.append(segment) + self._schedule_send_worker() + return False + + def _schedule_segment_retransmit(self, key, segment, when, now): + """Schedule retransmission of a segment if one is not already scheduled.""" + if segment.userdata: + # Already scheduled for retransmit + return + + if when <= now: + # Immediate retransmission + self._segment_retransmit_cb(key, segment) + else: + # convert time to milliseconds + timeout = int((when - now) * 1000) + segment.userdata = gobject.timeout_add(timeout, self._segment_retransmit_cb, + key, segment) + + _STD_RETRANSMIT_INTERVAL = 0.05 # 50ms (in seconds) + def _process_retransmit_request(self, segment): + """Validate and process a retransmission request.""" + key = (segment.rt_msg_seq_num(), segment.rt_master_sha(), segment.rt_segment_number()) + if not self._sent.has_key(key): + # Either we don't know about the segment, or it was already culled + return + + # Calculate next retransmission time and schedule packet for retransmit + segment = self._sent[key] + # only retransmit segments every 150ms or more + now = time.time() + next_transmit = max(now, segment.last_transmit() + self._STD_RETRANSMIT_INTERVAL) + self._schedule_segment_retransmit(key, segment, next_transmit, now) + + def _ack_check_worker(self): + """Periodically check for messages that haven't received an ack + yet, and retransmit them.""" + try: + now = time.time() + for key in self._sent.keys()[:]: + segment = self._sent[key] + # We only care about retransmitting the first segment + # of a message, since if other machines don't have the + # rest of the segments, they'll issue retransmit requests + if segment.segment_number() != 1: + continue + if segment.last_transmit() > now - 0.150: # 150ms + # Was just retransmitted recently, wait longer + # before retransmitting it + continue + ack_key = None + for ip in self._local_ips: + ack_key = (segment.message_sequence_number(), segment.master_sha(), ip) + if self._acks.has_key(ack_key): + break + ack_key = None + # If the segment already has been acked, don't send it + # again unless somebody explicitly requests a retransmit + if ack_key is not None: + continue + + del self._sent[key] + self._outgoing.append(segment) + self._schedule_send_worker() + except KeyboardInterrupt: + return False + return True + + def _send_ack_for_message(self, ack_msg_seq_num, ack_msg_sha, ack_addr): + """Send an ack segment for a message.""" + msg_seq_num = self._next_msg_seq() + full_remote_addr = (self._remote_addr, self._port) + ack = AckSegment.new_from_parts(full_remote_addr, msg_seq_num, + ack_msg_seq_num, ack_msg_sha, ack_addr) + self._outgoing.append(ack) + self._schedule_send_worker() + self._process_incoming_ack(ack) + + def _process_incoming_ack(self, segment): + """Save the ack so that we don't send an ack when we start getting the segments + the ack was acknowledging.""" + # If the ack is supposed to be for a message we sent, only accept it if + # we actually sent the message to which it refers + ack_addr = segment.ack_addr() + ack_master_sha = segment.ack_master_sha() + ack_msg_seq_num = segment.ack_msg_seq_num() + if ack_addr in self._local_ips: + sent_key = (ack_msg_seq_num, ack_master_sha, 1) + if not self._sent.has_key(sent_key): + return + ack_key = (ack_msg_seq_num, ack_master_sha, ack_addr) + if not self._acks.has_key(ack_key): + self._acks[ack_key] = time.time() + + def set_drop_probability(self, prob=4): + """Debugging function to randomly drop incoming packets. + The prob argument should be an integer between 1 and 10 to drop, + or 0 to drop none. Higher numbers drop more packets.""" + if not isinstance(prob, int): + raise ValueError("Drop probability must be an integer.") + if prob < 1 or prob > 10: + raise ValueError("Drop probability must be between 1 and 10 inclusive.") + self._drop_prob = prob + + def _handle_incoming_data(self, source, condition): + """Handle incoming network data by making a message segment out of it + sending it off to the processing function.""" + if not (condition & gobject.IO_IN): + return True + msg = {} + data, addr = source.recvfrom(self._UDP_MSG_SIZE) + + should_drop = False + p = random.random() * 10.0 + if self._drop_prob > 0 and p <= self._drop_prob: + should_drop = True + + try: + segment = SegmentBase.new_from_data(addr, data) + if should_drop: + print "(MRP): Dropped segment %d." % segment.segment_number() + else: + stype = segment.segment_type() + if stype == SegmentBase.type_data(): + self._process_incoming_data(segment) + elif stype == SegmentBase.type_retransmit(): + self._process_retransmit_request(segment) + elif stype == SegmentBase.type_ack(): + self._process_incoming_ack(segment) + except ValueError, exc: + print "(MRP): Bad segment: %s" % exc + return True + + def _next_msg_seq(self): + self._seq_counter = self._seq_counter + 1 + if self._seq_counter > 65535: + self._seq_counter = 1 + return self._seq_counter + + def send(self, data): + """Break data up into chunks and queue for later transmission.""" + if not self._started: + raise Exception("Can't send anything until started!") + + msg_seq = self._next_msg_seq() + + # Pack the data into network byte order + template = "! %ds" % len(str(data)) + data = struct.pack(template, str(data)) + master_sha = _sha_data(data) + + # Split up the data into segments + left = length = len(data) + mtu = SegmentBase.mtu() + nmessages = length / mtu + if length % mtu > 0: + nmessages = nmessages + 1 + seg_num = 1 + while left > 0: + seg = DataSegment.new_from_parts(seg_num, nmessages, + msg_seq, master_sha, data[:mtu]) + self._outgoing.append(seg) + seg_num = seg_num + 1 + data = data[mtu:] + left = left - mtu + self._schedule_send_worker() + + def _schedule_send_worker(self): + if len(self._outgoing) > 0 and self._send_worker == 0: + self._send_worker = gobject.timeout_add(50, self._send_worker_cb) + + def _send_worker_cb(self): + """Send all queued segments that have yet to be transmitted.""" + self._send_worker = 0 + nsent = 0 + for segment in self._outgoing: + packet = segment.packetize() + segment.inc_transmits() + addr = (self._remote_addr, self._port) + if segment.address(): + addr = segment.address() + self._send_sock.sendto(packet, addr) + if segment.userdata: + gobject.source_remove(segment.userdata) + segment.userdata = None # Retransmission GSource + key = (segment.message_sequence_number(), segment.master_sha(), segment.segment_number()) + self._sent[key] = segment + nsent = nsent + 1 + if nsent > 10: + break + self._outgoing = self._outgoing[nsent:] + if len(self._outgoing): + self._schedule_send_worker() + return False ################################################################# @@ -988,348 +988,348 @@ import unittest class SegmentBaseTestCase(unittest.TestCase): - _DEF_SEGNO = 1 - _DEF_TOT_SEGS = 5 - _DEF_MSG_SEQ_NUM = 4556 - _DEF_MASTER_SHA = "12345678901234567890" - _DEF_SEG_TYPE = 0 + _DEF_SEGNO = 1 + _DEF_TOT_SEGS = 5 + _DEF_MSG_SEQ_NUM = 4556 + _DEF_MASTER_SHA = "12345678901234567890" + _DEF_SEG_TYPE = 0 - _DEF_ADDRESS = ('123.3.2.1', 3333) - _SEG_MAGIC = 0xbaea4304 + _DEF_ADDRESS = ('123.3.2.1', 3333) + _SEG_MAGIC = 0xbaea4304 class SegmentBaseInitTestCase(SegmentBaseTestCase): - def _test_init_fail(self, segno, total_segs, msg_seq_num, master_sha, fail_msg): - try: - seg = SegmentBase(segno, total_segs, msg_seq_num, master_sha) - except ValueError, exc: - pass - else: - self.fail("expected a ValueError for %s." % fail_msg) - - def testSegmentBase(self): - assert SegmentBase.magic() == self._SEG_MAGIC, "Segment magic wasn't correct!" - assert SegmentBase.header_len() > 0, "header size was not greater than zero." - assert SegmentBase.mtu() > 0, "MTU was not greater than zero." - assert SegmentBase.mtu() + SegmentBase.header_len() == _UDP_DATAGRAM_SIZE, "MTU + header size didn't equal expected %d." % _UDP_DATAGRAM_SIZE - - def testGoodInit(self): - seg = SegmentBase(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) - assert seg.stime() < time.time(), "segment start time is less than now!" - assert not seg.address(), "Segment address was not None after init." - assert seg.segment_number() == self._DEF_SEGNO, "Segment number wasn't correct after init." - assert seg.total_segments() == self._DEF_TOT_SEGS, "Total segments wasn't correct after init." - assert seg.message_sequence_number() == self._DEF_MSG_SEQ_NUM, "Message sequence number wasn't correct after init." - assert seg.master_sha() == self._DEF_MASTER_SHA, "Message master SHA wasn't correct after init." - assert seg.segment_type() == None, "Segment type was not None after init." - assert seg.transmits() == 0, "Segment transmits was not 0 after init." - assert seg.last_transmit() == 0, "Segment last transmit was not 0 after init." - assert seg.data() == None, "Segment data was not None after init." - - def testSegmentNumber(self): - self._test_init_fail(0, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") - self._test_init_fail(65536, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") - self._test_init_fail(None, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") - self._test_init_fail("", self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") - - def testTotalMessageSegmentNumber(self): - self._test_init_fail(self._DEF_SEGNO, 0, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") - self._test_init_fail(self._DEF_SEGNO, 65536, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") - self._test_init_fail(self._DEF_SEGNO, None, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") - self._test_init_fail(self._DEF_SEGNO, "", self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") - - def testMessageSequenceNumber(self): - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, 0, self._DEF_MASTER_SHA, "invalid message sequence number") - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, 65536, self._DEF_MASTER_SHA, "invalid message sequence number") - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, None, self._DEF_MASTER_SHA, "invalid message sequence number") - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, "", self._DEF_MASTER_SHA, "invalid message sequence number") - - def testMasterSHA(self): - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, "1" * 19, "invalid SHA1 data hash") - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, "1" * 21, "invalid SHA1 data hash") - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, None, "invalid SHA1 data hash") - self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, 1234, "invalid SHA1 data hash") - - def _testNewFromDataFail(self, addr, data, fail_msg): - try: - seg = SegmentBase.new_from_data(addr, data) - except ValueError, exc: - pass - else: - self.fail("expected a ValueError about %s." % fail_msg) - - def testNewFromDataAddress(self): - self._testNewFromDataFail(None, None, "bad address") - self._testNewFromDataFail('', None, "bad address") - self._testNewFromDataFail((''), None, "bad address") - self._testNewFromDataFail((1), None, "bad address") - self._testNewFromDataFail(('', ''), None, "bad address") - self._testNewFromDataFail((1, 3333), None, "bad address") - self._testNewFromDataFail(('', 0), None, "bad address") - self._testNewFromDataFail(('', 65536), None, "bad address") - - def testNewFromDataData(self): - """Only test generic new_from_data() bits, not type-specific ones.""" - self._testNewFromDataFail(self._DEF_ADDRESS, None, "invalid data") - - really_short_data = "111" - self._testNewFromDataFail(self._DEF_ADDRESS, really_short_data, "data too short") - - only_header_data = "1" * SegmentBase.header_len() - self._testNewFromDataFail(self._DEF_ADDRESS, only_header_data, "data too short") - - too_much_data = "1" * (_UDP_DATAGRAM_SIZE + 1) - self._testNewFromDataFail(self._DEF_ADDRESS, too_much_data, "too much data") - - header_template = SegmentBase.header_template() - bad_magic_data = struct.pack(header_template, 0x12345678, self._DEF_SEG_TYPE, - self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) - self._testNewFromDataFail(self._DEF_ADDRESS, bad_magic_data, "invalid magic") - - bad_type_data = struct.pack(header_template, self._SEG_MAGIC, -1, self._DEF_SEGNO, - self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) - self._testNewFromDataFail(self._DEF_ADDRESS, bad_type_data, "invalid segment type") - - # Test master_sha that doesn't match data's SHA - header = struct.pack(header_template, self._SEG_MAGIC, self._DEF_SEG_TYPE, 1, 1, - self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) - data = struct.pack("! 15s", "7" * 15) - self._testNewFromDataFail(self._DEF_ADDRESS, header + data, "single-segment message SHA mismatch") - - def addToSuite(suite): - suite.addTest(SegmentBaseInitTestCase("testGoodInit")) - suite.addTest(SegmentBaseInitTestCase("testSegmentNumber")) - suite.addTest(SegmentBaseInitTestCase("testTotalMessageSegmentNumber")) - suite.addTest(SegmentBaseInitTestCase("testMessageSequenceNumber")) - suite.addTest(SegmentBaseInitTestCase("testMasterSHA")) - suite.addTest(SegmentBaseInitTestCase("testNewFromDataAddress")) - suite.addTest(SegmentBaseInitTestCase("testNewFromDataData")) - addToSuite = staticmethod(addToSuite) + def _test_init_fail(self, segno, total_segs, msg_seq_num, master_sha, fail_msg): + try: + seg = SegmentBase(segno, total_segs, msg_seq_num, master_sha) + except ValueError, exc: + pass + else: + self.fail("expected a ValueError for %s." % fail_msg) + + def testSegmentBase(self): + assert SegmentBase.magic() == self._SEG_MAGIC, "Segment magic wasn't correct!" + assert SegmentBase.header_len() > 0, "header size was not greater than zero." + assert SegmentBase.mtu() > 0, "MTU was not greater than zero." + assert SegmentBase.mtu() + SegmentBase.header_len() == _UDP_DATAGRAM_SIZE, "MTU + header size didn't equal expected %d." % _UDP_DATAGRAM_SIZE + + def testGoodInit(self): + seg = SegmentBase(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) + assert seg.stime() < time.time(), "segment start time is less than now!" + assert not seg.address(), "Segment address was not None after init." + assert seg.segment_number() == self._DEF_SEGNO, "Segment number wasn't correct after init." + assert seg.total_segments() == self._DEF_TOT_SEGS, "Total segments wasn't correct after init." + assert seg.message_sequence_number() == self._DEF_MSG_SEQ_NUM, "Message sequence number wasn't correct after init." + assert seg.master_sha() == self._DEF_MASTER_SHA, "Message master SHA wasn't correct after init." + assert seg.segment_type() == None, "Segment type was not None after init." + assert seg.transmits() == 0, "Segment transmits was not 0 after init." + assert seg.last_transmit() == 0, "Segment last transmit was not 0 after init." + assert seg.data() == None, "Segment data was not None after init." + + def testSegmentNumber(self): + self._test_init_fail(0, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") + self._test_init_fail(65536, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") + self._test_init_fail(None, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") + self._test_init_fail("", self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") + + def testTotalMessageSegmentNumber(self): + self._test_init_fail(self._DEF_SEGNO, 0, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") + self._test_init_fail(self._DEF_SEGNO, 65536, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") + self._test_init_fail(self._DEF_SEGNO, None, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") + self._test_init_fail(self._DEF_SEGNO, "", self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid total segments") + + def testMessageSequenceNumber(self): + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, 0, self._DEF_MASTER_SHA, "invalid message sequence number") + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, 65536, self._DEF_MASTER_SHA, "invalid message sequence number") + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, None, self._DEF_MASTER_SHA, "invalid message sequence number") + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, "", self._DEF_MASTER_SHA, "invalid message sequence number") + + def testMasterSHA(self): + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, "1" * 19, "invalid SHA1 data hash") + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, "1" * 21, "invalid SHA1 data hash") + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, None, "invalid SHA1 data hash") + self._test_init_fail(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, 1234, "invalid SHA1 data hash") + + def _testNewFromDataFail(self, addr, data, fail_msg): + try: + seg = SegmentBase.new_from_data(addr, data) + except ValueError, exc: + pass + else: + self.fail("expected a ValueError about %s." % fail_msg) + + def testNewFromDataAddress(self): + self._testNewFromDataFail(None, None, "bad address") + self._testNewFromDataFail('', None, "bad address") + self._testNewFromDataFail((''), None, "bad address") + self._testNewFromDataFail((1), None, "bad address") + self._testNewFromDataFail(('', ''), None, "bad address") + self._testNewFromDataFail((1, 3333), None, "bad address") + self._testNewFromDataFail(('', 0), None, "bad address") + self._testNewFromDataFail(('', 65536), None, "bad address") + + def testNewFromDataData(self): + """Only test generic new_from_data() bits, not type-specific ones.""" + self._testNewFromDataFail(self._DEF_ADDRESS, None, "invalid data") + + really_short_data = "111" + self._testNewFromDataFail(self._DEF_ADDRESS, really_short_data, "data too short") + + only_header_data = "1" * SegmentBase.header_len() + self._testNewFromDataFail(self._DEF_ADDRESS, only_header_data, "data too short") + + too_much_data = "1" * (_UDP_DATAGRAM_SIZE + 1) + self._testNewFromDataFail(self._DEF_ADDRESS, too_much_data, "too much data") + + header_template = SegmentBase.header_template() + bad_magic_data = struct.pack(header_template, 0x12345678, self._DEF_SEG_TYPE, + self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) + self._testNewFromDataFail(self._DEF_ADDRESS, bad_magic_data, "invalid magic") + + bad_type_data = struct.pack(header_template, self._SEG_MAGIC, -1, self._DEF_SEGNO, + self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) + self._testNewFromDataFail(self._DEF_ADDRESS, bad_type_data, "invalid segment type") + + # Test master_sha that doesn't match data's SHA + header = struct.pack(header_template, self._SEG_MAGIC, self._DEF_SEG_TYPE, 1, 1, + self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) + data = struct.pack("! 15s", "7" * 15) + self._testNewFromDataFail(self._DEF_ADDRESS, header + data, "single-segment message SHA mismatch") + + def addToSuite(suite): + suite.addTest(SegmentBaseInitTestCase("testGoodInit")) + suite.addTest(SegmentBaseInitTestCase("testSegmentNumber")) + suite.addTest(SegmentBaseInitTestCase("testTotalMessageSegmentNumber")) + suite.addTest(SegmentBaseInitTestCase("testMessageSequenceNumber")) + suite.addTest(SegmentBaseInitTestCase("testMasterSHA")) + suite.addTest(SegmentBaseInitTestCase("testNewFromDataAddress")) + suite.addTest(SegmentBaseInitTestCase("testNewFromDataData")) + addToSuite = staticmethod(addToSuite) class DataSegmentTestCase(SegmentBaseTestCase): - """Test DataSegment class specific initialization and stuff.""" - - def testInit(self): - seg = DataSegment(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA) - assert seg.segment_type() == SegmentBase.type_data(), "Segment wasn't a data segment." - - def testNewFromParts(self): - try: - seg = DataSegment.new_from_parts(self._DEF_SEGNO, self._DEF_TOT_SEGS, - self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, None) - except ValueError, exc: - pass - else: - self.fail("Expected ValueError about invalid data.") - - # Ensure message data is same as we stuff in after object is instantiated - payload = "How are you today?" - seg = DataSegment.new_from_parts(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA, payload) - assert seg.data() == payload, "Data after segment creation didn't match expected." - - def testNewFromData(self): - """Test DataSegment's new_from_data() functionality.""" - - # Make sure something valid actually works - header_template = SegmentBase.header_template() - payload_str = "How are you today?" - payload = struct.pack("! %ds" % len(payload_str), payload_str) - payload_sha = _sha_data(payload) - header = struct.pack(header_template, self._SEG_MAGIC, SegmentBase.type_data(), self._DEF_SEGNO, - self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, payload_sha) - seg = SegmentBase.new_from_data(self._DEF_ADDRESS, header + payload) - - assert seg.address() == self._DEF_ADDRESS, "Segment address did not match expected." - assert seg.segment_type() == SegmentBase.type_data(), "Segment type did not match expected." - assert seg.segment_number() == self._DEF_SEGNO, "Segment number did not match expected." - assert seg.total_segments() == self._DEF_TOT_SEGS, "Total segments did not match expected." - assert seg.message_sequence_number() == self._DEF_MSG_SEQ_NUM, "Message sequence number did not match expected." - assert seg.master_sha() == payload_sha, "Message master SHA did not match expected." - assert seg.data() == payload, "Segment data did not match expected payload." - - def addToSuite(suite): - suite.addTest(DataSegmentTestCase("testInit")) - suite.addTest(DataSegmentTestCase("testNewFromParts")) - suite.addTest(DataSegmentTestCase("testNewFromData")) - addToSuite = staticmethod(addToSuite) + """Test DataSegment class specific initialization and stuff.""" + + def testInit(self): + seg = DataSegment(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA) + assert seg.segment_type() == SegmentBase.type_data(), "Segment wasn't a data segment." + + def testNewFromParts(self): + try: + seg = DataSegment.new_from_parts(self._DEF_SEGNO, self._DEF_TOT_SEGS, + self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, None) + except ValueError, exc: + pass + else: + self.fail("Expected ValueError about invalid data.") + + # Ensure message data is same as we stuff in after object is instantiated + payload = "How are you today?" + seg = DataSegment.new_from_parts(self._DEF_SEGNO, self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA, payload) + assert seg.data() == payload, "Data after segment creation didn't match expected." + + def testNewFromData(self): + """Test DataSegment's new_from_data() functionality.""" + + # Make sure something valid actually works + header_template = SegmentBase.header_template() + payload_str = "How are you today?" + payload = struct.pack("! %ds" % len(payload_str), payload_str) + payload_sha = _sha_data(payload) + header = struct.pack(header_template, self._SEG_MAGIC, SegmentBase.type_data(), self._DEF_SEGNO, + self._DEF_TOT_SEGS, self._DEF_MSG_SEQ_NUM, payload_sha) + seg = SegmentBase.new_from_data(self._DEF_ADDRESS, header + payload) + + assert seg.address() == self._DEF_ADDRESS, "Segment address did not match expected." + assert seg.segment_type() == SegmentBase.type_data(), "Segment type did not match expected." + assert seg.segment_number() == self._DEF_SEGNO, "Segment number did not match expected." + assert seg.total_segments() == self._DEF_TOT_SEGS, "Total segments did not match expected." + assert seg.message_sequence_number() == self._DEF_MSG_SEQ_NUM, "Message sequence number did not match expected." + assert seg.master_sha() == payload_sha, "Message master SHA did not match expected." + assert seg.data() == payload, "Segment data did not match expected payload." + + def addToSuite(suite): + suite.addTest(DataSegmentTestCase("testInit")) + suite.addTest(DataSegmentTestCase("testNewFromParts")) + suite.addTest(DataSegmentTestCase("testNewFromData")) + addToSuite = staticmethod(addToSuite) class RetransmitSegmentTestCase(SegmentBaseTestCase): - """Test RetransmitSegment class specific initialization and stuff.""" - - def _test_init_fail(self, segno, total_segs, msg_seq_num, master_sha, fail_msg): - try: - seg = RetransmitSegment(segno, total_segs, msg_seq_num, master_sha) - except ValueError, exc: - pass - else: - self.fail("expected a ValueError for %s." % fail_msg) - - def testInit(self): - self._test_init_fail(0, 1, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") - self._test_init_fail(2, 1, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") - self._test_init_fail(1, 0, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid number of total segments") - self._test_init_fail(1, 2, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid number of total segments") - - # Something that's supposed to work - seg = RetransmitSegment(1, 1, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) - assert seg.segment_type() == SegmentBase.type_retransmit(), "Segment wasn't a retransmit segment." - - def _test_new_from_parts_fail(self, msg_seq_num, rt_msg_seq_num, rt_master_sha, rt_segment_number, fail_msg): - try: - seg = RetransmitSegment.new_from_parts(self._DEF_ADDRESS, msg_seq_num, rt_msg_seq_num, - rt_master_sha, rt_segment_number) - except ValueError, exc: - pass - else: - self.fail("expected a ValueError for %s." % fail_msg) - - def testNewFromParts(self): - """Test RetransmitSegment's new_from_parts() functionality.""" - self._test_new_from_parts_fail(0, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid message sequence number") - self._test_new_from_parts_fail(65536, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid message sequence number") - self._test_new_from_parts_fail(None, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid message sequence number") - self._test_new_from_parts_fail("", self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid message sequence number") - - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, 0, self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid retransmit message sequence number") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, 65536, self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid retransmit message sequence number") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, None, self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid retransmit message sequence number") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, "", self._DEF_MASTER_SHA, - self._DEF_SEGNO, "invalid retransmit message sequence number") - - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, "1" * 19, - self._DEF_SEGNO, "invalid retransmit message master SHA") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, "1" * 21, - self._DEF_SEGNO, "invalid retransmit message master SHA") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, None, - self._DEF_SEGNO, "invalid retransmit message master SHA") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, 1234, - self._DEF_SEGNO, "invalid retransmit message master SHA") - - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA, 0, "invalid retransmit message segment number") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA, 65536, "invalid retransmit message segment number") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA, None, "invalid retransmit message segment number") - self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA, "", "invalid retransmit message segment number") - - # Ensure message data is same as we stuff in after object is instantiated - seg = RetransmitSegment.new_from_parts(self._DEF_ADDRESS, self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA, self._DEF_SEGNO) - assert seg.rt_msg_seq_num() == self._DEF_MSG_SEQ_NUM, "RT message sequence number after segment creation didn't match expected." - assert seg.rt_master_sha() == self._DEF_MASTER_SHA, "RT master SHA after segment creation didn't match expected." - assert seg.rt_segment_number() == self._DEF_SEGNO, "RT segment number after segment creation didn't match expected." - - def _new_from_data(self, rt_msg_seq_num, rt_master_sha, rt_segment_number): - payload = struct.pack(RetransmitSegment.data_template(), rt_msg_seq_num, rt_master_sha, rt_segment_number) - payload_sha = _sha_data(payload) - header_template = SegmentBase.header_template() - header = struct.pack(header_template, self._SEG_MAGIC, SegmentBase.type_retransmit(), 1, 1, - self._DEF_MSG_SEQ_NUM, payload_sha) - return header + payload - - def _test_new_from_data_fail(self, rt_msg_seq_num, rt_master_sha, rt_segment_number, fail_msg): - try: - packet = self._new_from_data(rt_msg_seq_num, rt_master_sha, rt_segment_number) - seg = SegmentBase.new_from_data(self._DEF_ADDRESS, packet) - except ValueError, exc: - pass - else: - self.fail("Expected a ValueError about %s." % fail_msg) - - def testNewFromData(self): - """Test DataSegment's new_from_data() functionality.""" - self._test_new_from_data_fail(0, self._DEF_MASTER_SHA, self._DEF_SEGNO, "invalid RT message sequence number") - self._test_new_from_data_fail(65536, self._DEF_MASTER_SHA, self._DEF_SEGNO, "invalid RT message sequence number") - - self._test_new_from_data_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, 0, "invalid RT segment number") - self._test_new_from_data_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, 65536, "invalid RT segment number") - - # Ensure something that should work - packet = self._new_from_data(self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, self._DEF_SEGNO) - seg = SegmentBase.new_from_data(self._DEF_ADDRESS, packet) - assert seg.segment_type() == SegmentBase.type_retransmit(), "Segment wasn't expected type." - assert seg.rt_msg_seq_num() == self._DEF_MSG_SEQ_NUM, "Segment RT message sequence number didn't match expected." - assert seg.rt_master_sha() == self._DEF_MASTER_SHA, "Segment RT master SHA didn't match expected." - assert seg.rt_segment_number() == self._DEF_SEGNO, "Segment RT segment number didn't match expected." - - def testPartsToData(self): - seg = RetransmitSegment.new_from_parts(self._DEF_ADDRESS, self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, - self._DEF_MASTER_SHA, self._DEF_SEGNO) - new_seg = SegmentBase.new_from_data(self._DEF_ADDRESS, seg.packetize()) - assert new_seg.rt_msg_seq_num() == self._DEF_MSG_SEQ_NUM, "Segment RT message sequence number didn't match expected." - assert new_seg.rt_master_sha() == self._DEF_MASTER_SHA, "Segment RT master SHA didn't match expected." - assert new_seg.rt_segment_number() == self._DEF_SEGNO, "Segment RT segment number didn't match expected." - - def addToSuite(suite): - suite.addTest(RetransmitSegmentTestCase("testInit")) - suite.addTest(RetransmitSegmentTestCase("testNewFromParts")) - suite.addTest(RetransmitSegmentTestCase("testNewFromData")) - suite.addTest(RetransmitSegmentTestCase("testPartsToData")) - addToSuite = staticmethod(addToSuite) + """Test RetransmitSegment class specific initialization and stuff.""" + + def _test_init_fail(self, segno, total_segs, msg_seq_num, master_sha, fail_msg): + try: + seg = RetransmitSegment(segno, total_segs, msg_seq_num, master_sha) + except ValueError, exc: + pass + else: + self.fail("expected a ValueError for %s." % fail_msg) + + def testInit(self): + self._test_init_fail(0, 1, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") + self._test_init_fail(2, 1, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid segment number") + self._test_init_fail(1, 0, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid number of total segments") + self._test_init_fail(1, 2, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, "invalid number of total segments") + + # Something that's supposed to work + seg = RetransmitSegment(1, 1, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA) + assert seg.segment_type() == SegmentBase.type_retransmit(), "Segment wasn't a retransmit segment." + + def _test_new_from_parts_fail(self, msg_seq_num, rt_msg_seq_num, rt_master_sha, rt_segment_number, fail_msg): + try: + seg = RetransmitSegment.new_from_parts(self._DEF_ADDRESS, msg_seq_num, rt_msg_seq_num, + rt_master_sha, rt_segment_number) + except ValueError, exc: + pass + else: + self.fail("expected a ValueError for %s." % fail_msg) + + def testNewFromParts(self): + """Test RetransmitSegment's new_from_parts() functionality.""" + self._test_new_from_parts_fail(0, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid message sequence number") + self._test_new_from_parts_fail(65536, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid message sequence number") + self._test_new_from_parts_fail(None, self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid message sequence number") + self._test_new_from_parts_fail("", self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid message sequence number") + + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, 0, self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid retransmit message sequence number") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, 65536, self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid retransmit message sequence number") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, None, self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid retransmit message sequence number") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, "", self._DEF_MASTER_SHA, + self._DEF_SEGNO, "invalid retransmit message sequence number") + + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, "1" * 19, + self._DEF_SEGNO, "invalid retransmit message master SHA") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, "1" * 21, + self._DEF_SEGNO, "invalid retransmit message master SHA") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, None, + self._DEF_SEGNO, "invalid retransmit message master SHA") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, 1234, + self._DEF_SEGNO, "invalid retransmit message master SHA") + + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA, 0, "invalid retransmit message segment number") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA, 65536, "invalid retransmit message segment number") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA, None, "invalid retransmit message segment number") + self._test_new_from_parts_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA, "", "invalid retransmit message segment number") + + # Ensure message data is same as we stuff in after object is instantiated + seg = RetransmitSegment.new_from_parts(self._DEF_ADDRESS, self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA, self._DEF_SEGNO) + assert seg.rt_msg_seq_num() == self._DEF_MSG_SEQ_NUM, "RT message sequence number after segment creation didn't match expected." + assert seg.rt_master_sha() == self._DEF_MASTER_SHA, "RT master SHA after segment creation didn't match expected." + assert seg.rt_segment_number() == self._DEF_SEGNO, "RT segment number after segment creation didn't match expected." + + def _new_from_data(self, rt_msg_seq_num, rt_master_sha, rt_segment_number): + payload = struct.pack(RetransmitSegment.data_template(), rt_msg_seq_num, rt_master_sha, rt_segment_number) + payload_sha = _sha_data(payload) + header_template = SegmentBase.header_template() + header = struct.pack(header_template, self._SEG_MAGIC, SegmentBase.type_retransmit(), 1, 1, + self._DEF_MSG_SEQ_NUM, payload_sha) + return header + payload + + def _test_new_from_data_fail(self, rt_msg_seq_num, rt_master_sha, rt_segment_number, fail_msg): + try: + packet = self._new_from_data(rt_msg_seq_num, rt_master_sha, rt_segment_number) + seg = SegmentBase.new_from_data(self._DEF_ADDRESS, packet) + except ValueError, exc: + pass + else: + self.fail("Expected a ValueError about %s." % fail_msg) + + def testNewFromData(self): + """Test DataSegment's new_from_data() functionality.""" + self._test_new_from_data_fail(0, self._DEF_MASTER_SHA, self._DEF_SEGNO, "invalid RT message sequence number") + self._test_new_from_data_fail(65536, self._DEF_MASTER_SHA, self._DEF_SEGNO, "invalid RT message sequence number") + + self._test_new_from_data_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, 0, "invalid RT segment number") + self._test_new_from_data_fail(self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, 65536, "invalid RT segment number") + + # Ensure something that should work + packet = self._new_from_data(self._DEF_MSG_SEQ_NUM, self._DEF_MASTER_SHA, self._DEF_SEGNO) + seg = SegmentBase.new_from_data(self._DEF_ADDRESS, packet) + assert seg.segment_type() == SegmentBase.type_retransmit(), "Segment wasn't expected type." + assert seg.rt_msg_seq_num() == self._DEF_MSG_SEQ_NUM, "Segment RT message sequence number didn't match expected." + assert seg.rt_master_sha() == self._DEF_MASTER_SHA, "Segment RT master SHA didn't match expected." + assert seg.rt_segment_number() == self._DEF_SEGNO, "Segment RT segment number didn't match expected." + + def testPartsToData(self): + seg = RetransmitSegment.new_from_parts(self._DEF_ADDRESS, self._DEF_MSG_SEQ_NUM, self._DEF_MSG_SEQ_NUM, + self._DEF_MASTER_SHA, self._DEF_SEGNO) + new_seg = SegmentBase.new_from_data(self._DEF_ADDRESS, seg.packetize()) + assert new_seg.rt_msg_seq_num() == self._DEF_MSG_SEQ_NUM, "Segment RT message sequence number didn't match expected." + assert new_seg.rt_master_sha() == self._DEF_MASTER_SHA, "Segment RT master SHA didn't match expected." + assert new_seg.rt_segment_number() == self._DEF_SEGNO, "Segment RT segment number didn't match expected." + + def addToSuite(suite): + suite.addTest(RetransmitSegmentTestCase("testInit")) + suite.addTest(RetransmitSegmentTestCase("testNewFromParts")) + suite.addTest(RetransmitSegmentTestCase("testNewFromData")) + suite.addTest(RetransmitSegmentTestCase("testPartsToData")) + addToSuite = staticmethod(addToSuite) class SHAUtilsTestCase(unittest.TestCase): - def testSHA(self): - data = "235jklqt3hjwasdv879wfe89723rqjh32tr3hwaejksdvd89udsv89dsgiougjktqjhk23tjht23hjt3qhjewagthjasgdgsd" - data_sha = _sha_data(data) - assert len(data_sha) == 20, "SHA wasn't correct size." - known_sha = "\xee\x9e\xb9\x1d\xe8\x96\x75\xcb\x12\xf1\x25\x22\x0f\x76\xf7\xf3\xc8\x4e\xbf\xcd" - assert data_sha == known_sha, "SHA didn't match known SHA." + def testSHA(self): + data = "235jklqt3hjwasdv879wfe89723rqjh32tr3hwaejksdvd89udsv89dsgiougjktqjhk23tjht23hjt3qhjewagthjasgdgsd" + data_sha = _sha_data(data) + assert len(data_sha) == 20, "SHA wasn't correct size." + known_sha = "\xee\x9e\xb9\x1d\xe8\x96\x75\xcb\x12\xf1\x25\x22\x0f\x76\xf7\xf3\xc8\x4e\xbf\xcd" + assert data_sha == known_sha, "SHA didn't match known SHA." - def testStringifySHA(self): - data = "jlkwjlkaegdjlksgdjklsdgajklganjtwn23n325n23tjwgeajkga nafDA fwqnjlqtjkl23tjk2365jlk235jkl2356jlktjkltewjlktewjklewtjklaggsda" - data_known_sha = "9650c23db78092a0ffda4577c87ebf36d25c868e" - assert _stringify_sha(_sha_data(data)) == data_known_sha, "SHA stringify didn't return correct SHA." - # Do it twice for kicks - assert _stringify_sha(_sha_data(data)) == data_known_sha, "SHA stringify didn't return correct SHA." + def testStringifySHA(self): + data = "jlkwjlkaegdjlksgdjklsdgajklganjtwn23n325n23tjwgeajkga nafDA fwqnjlqtjkl23tjk2365jlk235jkl2356jlktjkltewjlktewjklewtjklaggsda" + data_known_sha = "9650c23db78092a0ffda4577c87ebf36d25c868e" + assert _stringify_sha(_sha_data(data)) == data_known_sha, "SHA stringify didn't return correct SHA." + # Do it twice for kicks + assert _stringify_sha(_sha_data(data)) == data_known_sha, "SHA stringify didn't return correct SHA." - def addToSuite(suite): - suite.addTest(SHAUtilsTestCase("testSHA")) - suite.addTest(SHAUtilsTestCase("testStringifySHA")) - addToSuite = staticmethod(addToSuite) + def addToSuite(suite): + suite.addTest(SHAUtilsTestCase("testSHA")) + suite.addTest(SHAUtilsTestCase("testStringifySHA")) + addToSuite = staticmethod(addToSuite) def unit_test(): - suite = unittest.TestSuite() - SegmentBaseInitTestCase.addToSuite(suite) - DataSegmentTestCase.addToSuite(suite) - RetransmitSegmentTestCase.addToSuite(suite) - SHAUtilsTestCase.addToSuite(suite) + suite = unittest.TestSuite() + SegmentBaseInitTestCase.addToSuite(suite) + DataSegmentTestCase.addToSuite(suite) + RetransmitSegmentTestCase.addToSuite(suite) + SHAUtilsTestCase.addToSuite(suite) - runner = unittest.TextTestRunner() - runner.run(suite) + runner = unittest.TextTestRunner() + runner.run(suite) def got_data(addr, data, user_data=None): - print "Got data from %s, writing to %s." % (addr, user_data) - fl = open(user_data, "w+") - fl.write(data) - fl.close() + print "Got data from %s, writing to %s." % (addr, user_data) + fl = open(user_data, "w+") + fl.write(data) + fl.close() def simple_test(): - import sys - pipe = MostlyReliablePipe('', '224.0.0.222', 2293, got_data, sys.argv[2]) -# pipe.set_drop_probability(4) - pipe.start() - fl = open(sys.argv[1], "r") - data = fl.read() - fl.close() - msg = """The said Eliza, John, and Georgiana were now clustered round their mama in the drawing-room: + import sys + pipe = MostlyReliablePipe('', '224.0.0.222', 2293, got_data, sys.argv[2]) +# pipe.set_drop_probability(4) + pipe.start() + fl = open(sys.argv[1], "r") + data = fl.read() + fl.close() + msg = """The said Eliza, John, and Georgiana were now clustered round their mama in the drawing-room: she lay reclined on a sofa by the fireside, and with her darlings about her (for the time neither quarrelling nor crying) looked perfectly happy. Me, she had dispensed from joining the group; saying, 'She regretted to be under the necessity of keeping me at a distance; but that until she heard from @@ -1337,58 +1337,58 @@ Bessie, and could discover by her own observation, that I was endeavouring in go a more sociable and childlike disposition, a more attractive and sprightly manner -- something lighter, franker, more natural, as it were -- she really must exclude me from privileges intended only for contented, happy, little children.'""" - pipe.send(data) - try: - gtk.main() - except KeyboardInterrupt: - print 'Ctrl+C pressed, exiting...' + pipe.send(data) + try: + gtk.main() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' def net_test_got_data(addr, data, user_data=None): - # Don't report data if we are a sender - if user_data: - return - print "%s (%s)" % (data, addr) + # Don't report data if we are a sender + if user_data: + return + print "%s (%s)" % (data, addr) idstamp = 0 def transmit_data(pipe): - global idstamp - msg = "Message #%d" % idstamp - print "Sending '%s'" % msg - pipe.send(msg) - idstamp = idstamp + 1 - return True + global idstamp + msg = "Message #%d" % idstamp + print "Sending '%s'" % msg + pipe.send(msg) + idstamp = idstamp + 1 + return True def network_test(): - import sys, os - send = False - if len(sys.argv) != 2: - print "Need one arg, either 'send' or 'recv'" - os._exit(1) - if sys.argv[1] == "send": - send = True - elif sys.argv[1] == "recv": - send = False - else: - print "Arg should be either 'send' or 'recv'" - os._exit(1) - - pipe = MostlyReliablePipe('', '224.0.0.222', 2293, net_test_got_data, send) - pipe.start() - if send: - gobject.timeout_add(1000, transmit_data, pipe) - try: - gtk.main() - except KeyboardInterrupt: - print 'Ctrl+C pressed, exiting...' + import sys, os + send = False + if len(sys.argv) != 2: + print "Need one arg, either 'send' or 'recv'" + os._exit(1) + if sys.argv[1] == "send": + send = True + elif sys.argv[1] == "recv": + send = False + else: + print "Arg should be either 'send' or 'recv'" + os._exit(1) + + pipe = MostlyReliablePipe('', '224.0.0.222', 2293, net_test_got_data, send) + pipe.start() + if send: + gobject.timeout_add(1000, transmit_data, pipe) + try: + gtk.main() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' def main(): -# unit_test() -# simple_test() - network_test() +# unit_test() +# simple_test() + network_test() if __name__ == "__main__": - main() + main() diff --git a/sugar/p2p/NotificationListener.py b/sugar/p2p/NotificationListener.py index f68bbb2..42668ad 100644 --- a/sugar/p2p/NotificationListener.py +++ b/sugar/p2p/NotificationListener.py @@ -21,18 +21,18 @@ from sugar.p2p.Notifier import Notifier from sugar.p2p import network class NotificationListener: - def __init__(self, service): - logging.debug('Start notification listener. Service %s, address %s, port %s' % (service.get_type(), service.get_address(), service.get_port())) - server = network.GroupServer(service.get_address(), - service.get_port(), - self._recv_multicast) - server.start() - - self._listeners = [] - - def add_listener(self, listener): - self._listeners.append(listener) - - def _recv_multicast(self, msg): - for listener in self._listeners: - listener(msg) + def __init__(self, service): + logging.debug('Start notification listener. Service %s, address %s, port %s' % (service.get_type(), service.get_address(), service.get_port())) + server = network.GroupServer(service.get_address(), + service.get_port(), + self._recv_multicast) + server.start() + + self._listeners = [] + + def add_listener(self, listener): + self._listeners.append(listener) + + def _recv_multicast(self, msg): + for listener in self._listeners: + listener(msg) diff --git a/sugar/p2p/Notifier.py b/sugar/p2p/Notifier.py index f216fda..69d0af6 100644 --- a/sugar/p2p/Notifier.py +++ b/sugar/p2p/Notifier.py @@ -18,10 +18,10 @@ from sugar.p2p import network class Notifier: - def __init__(self, service): - address = service.get_address() - port = service.get_port() - self._client = network.GroupClient(address, port) - - def notify(self, msg): - self._client.send_msg(msg) + def __init__(self, service): + address = service.get_address() + port = service.get_port() + self._client = network.GroupClient(address, port) + + def notify(self, msg): + self._client.send_msg(msg) diff --git a/sugar/p2p/Stream.py b/sugar/p2p/Stream.py index edb4d1b..b3239b3 100644 --- a/sugar/p2p/Stream.py +++ b/sugar/p2p/Stream.py @@ -26,135 +26,135 @@ from MostlyReliablePipe import MostlyReliablePipe from sugar.presence import Service def is_multicast_address(address): - """Simple numerical check for whether an IP4 address - is in the range for multicast addresses or not.""" - if not address: - return False - if address[3] != '.': - return False - first = int(float(address[:3])) - if first >= 224 and first <= 239: - return True - return False + """Simple numerical check for whether an IP4 address + is in the range for multicast addresses or not.""" + if not address: + return False + if address[3] != '.': + return False + first = int(float(address[:3])) + if first >= 224 and first <= 239: + return True + return False class Stream(object): - def __init__(self, service): - if not service.get_port(): - raise ValueError("service must have an address.") - self._service = service - self._reader_port = self._service.get_port() - self._writer_port = self._reader_port - self._address = self._service.get_address() - self._callback = None - - def new_from_service(service, start_reader=True): - if is_multicast_address(service.get_address()): - return MulticastStream(service) - else: - return UnicastStream(service, start_reader) - new_from_service = staticmethod(new_from_service) - - def set_data_listener(self, callback): - self._callback = callback - - def _recv(self, address, data): - if self._callback: - self._callback(address, data) + def __init__(self, service): + if not service.get_port(): + raise ValueError("service must have an address.") + self._service = service + self._reader_port = self._service.get_port() + self._writer_port = self._reader_port + self._address = self._service.get_address() + self._callback = None + + def new_from_service(service, start_reader=True): + if is_multicast_address(service.get_address()): + return MulticastStream(service) + else: + return UnicastStream(service, start_reader) + new_from_service = staticmethod(new_from_service) + + def set_data_listener(self, callback): + self._callback = callback + + def _recv(self, address, data): + if self._callback: + self._callback(address, data) class UnicastStreamWriter(object): - def __init__(self, stream, service): - # set up the writer - self._service = service - if not service.get_address(): - raise ValueError("service must have a valid address.") - self._address = self._service.get_address() - self._port = self._service.get_port() - self._xmlrpc_addr = "http://%s:%d" % (self._address, self._port) - self._writer = network.GlibServerProxy(self._xmlrpc_addr) - - def write(self, xmlrpc_data): - """Write some data to the default endpoint of this pipe on the remote server.""" - try: - self._writer.message(None, None, xmlrpc_data) - return True - except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError): - traceback.print_exc() - return False - - def custom_request(self, method_name, request_cb, user_data, *args): - """Call a custom XML-RPC method on the remote server.""" - try: - method = getattr(self._writer, method_name) - method(request_cb, user_data, *args) - return True - except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError): - traceback.print_exc() - return False + def __init__(self, stream, service): + # set up the writer + self._service = service + if not service.get_address(): + raise ValueError("service must have a valid address.") + self._address = self._service.get_address() + self._port = self._service.get_port() + self._xmlrpc_addr = "http://%s:%d" % (self._address, self._port) + self._writer = network.GlibServerProxy(self._xmlrpc_addr) + + def write(self, xmlrpc_data): + """Write some data to the default endpoint of this pipe on the remote server.""" + try: + self._writer.message(None, None, xmlrpc_data) + return True + except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError): + traceback.print_exc() + return False + + def custom_request(self, method_name, request_cb, user_data, *args): + """Call a custom XML-RPC method on the remote server.""" + try: + method = getattr(self._writer, method_name) + method(request_cb, user_data, *args) + return True + except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError): + traceback.print_exc() + return False class UnicastStream(Stream): - def __init__(self, service, start_reader=True): - """Initializes the stream. If the 'start_reader' argument is True, - the stream will initialize and start a new stream reader, if it - is False, no reader will be created and the caller must call the - start_reader() method to start the stream reader and be able to - receive any data from the stream.""" - Stream.__init__(self, service) - if start_reader: - self.start_reader() - - def start_reader(self): - """Start the stream's reader, which for UnicastStream objects is - and XMLRPC server. If there's a port conflict with some other - service, the reader will try to find another port to use instead. - Returns the port number used for the reader.""" - # Set up the reader - self._reader = network.GlibXMLRPCServer(("", self._reader_port)) - self._reader.register_function(self._message, "message") - - def _message(self, message): - """Called by the XMLRPC server when network data arrives.""" - address = network.get_authinfo() - self._recv(address, message) - return True - - def register_reader_handler(self, handler, name): - """Register a custom message handler with the reader. This call - adds a custom XMLRPC method call with the name 'name' to the reader's - XMLRPC server, which then calls the 'handler' argument back when - a method call for it arrives over the network.""" - if name == "message": - raise ValueError("Handler name 'message' is a reserved handler.") - self._reader.register_function(handler, name) - - def new_writer(self, service): - """Return a new stream writer object.""" - return UnicastStreamWriter(self, service) + def __init__(self, service, start_reader=True): + """Initializes the stream. If the 'start_reader' argument is True, + the stream will initialize and start a new stream reader, if it + is False, no reader will be created and the caller must call the + start_reader() method to start the stream reader and be able to + receive any data from the stream.""" + Stream.__init__(self, service) + if start_reader: + self.start_reader() + + def start_reader(self): + """Start the stream's reader, which for UnicastStream objects is + and XMLRPC server. If there's a port conflict with some other + service, the reader will try to find another port to use instead. + Returns the port number used for the reader.""" + # Set up the reader + self._reader = network.GlibXMLRPCServer(("", self._reader_port)) + self._reader.register_function(self._message, "message") + + def _message(self, message): + """Called by the XMLRPC server when network data arrives.""" + address = network.get_authinfo() + self._recv(address, message) + return True + + def register_reader_handler(self, handler, name): + """Register a custom message handler with the reader. This call + adds a custom XMLRPC method call with the name 'name' to the reader's + XMLRPC server, which then calls the 'handler' argument back when + a method call for it arrives over the network.""" + if name == "message": + raise ValueError("Handler name 'message' is a reserved handler.") + self._reader.register_function(handler, name) + + def new_writer(self, service): + """Return a new stream writer object.""" + return UnicastStreamWriter(self, service) class MulticastStream(Stream): - def __init__(self, service): - Stream.__init__(self, service) - self._service = service - self._internal_start_reader() - - def start_reader(self): - return self._reader_port - - def _internal_start_reader(self): - logging.debug('Start multicast stream, address %s, port %d' % (self._address, self._reader_port)) - if not self._service.get_address(): - raise ValueError("service must have a valid address.") - self._pipe = MostlyReliablePipe('', self._address, self._reader_port, - self._recv_data_cb) - self._pipe.start() - - def write(self, data): - self._pipe.send(data) - - def _recv_data_cb(self, address, data, user_data=None): - self._recv(address[0], data) - - def new_writer(self, service=None): - return self + def __init__(self, service): + Stream.__init__(self, service) + self._service = service + self._internal_start_reader() + + def start_reader(self): + return self._reader_port + + def _internal_start_reader(self): + logging.debug('Start multicast stream, address %s, port %d' % (self._address, self._reader_port)) + if not self._service.get_address(): + raise ValueError("service must have a valid address.") + self._pipe = MostlyReliablePipe('', self._address, self._reader_port, + self._recv_data_cb) + self._pipe.start() + + def write(self, data): + self._pipe.send(data) + + def _recv_data_cb(self, address, data, user_data=None): + self._recv(address[0], data) + + def new_writer(self, service=None): + return self diff --git a/sugar/p2p/network.py b/sugar/p2p/network.py index 6718669..e5b4e4b 100644 --- a/sugar/p2p/network.py +++ b/sugar/p2p/network.py @@ -35,347 +35,347 @@ RESULT_SUCCESS = 1 __authinfos = {} def _add_authinfo(authinfo): - __authinfos[threading.currentThread()] = authinfo + __authinfos[threading.currentThread()] = authinfo def get_authinfo(): - return __authinfos.get(threading.currentThread()) + return __authinfos.get(threading.currentThread()) def _del_authinfo(): - del __authinfos[threading.currentThread()] + del __authinfos[threading.currentThread()] class GlibTCPServer(SocketServer.TCPServer): - """GlibTCPServer + """GlibTCPServer - Integrate socket accept into glib mainloop. - """ + Integrate socket accept into glib mainloop. + """ - allow_reuse_address = True - request_queue_size = 20 + allow_reuse_address = True + request_queue_size = 20 - def __init__(self, server_address, RequestHandlerClass): - SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass) - self.socket.setblocking(0) # Set nonblocking + def __init__(self, server_address, RequestHandlerClass): + SocketServer.TCPServer.__init__(self, server_address, RequestHandlerClass) + self.socket.setblocking(0) # Set nonblocking - # Watch the listener socket for data - gobject.io_add_watch(self.socket, gobject.IO_IN, self._handle_accept) + # Watch the listener socket for data + gobject.io_add_watch(self.socket, gobject.IO_IN, self._handle_accept) - def _handle_accept(self, source, condition): - """Process incoming data on the server's socket by doing an accept() - via handle_request().""" - if not (condition & gobject.IO_IN): - return True - self.handle_request() - return True + def _handle_accept(self, source, condition): + """Process incoming data on the server's socket by doing an accept() + via handle_request().""" + if not (condition & gobject.IO_IN): + return True + self.handle_request() + return True class GlibXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): - """ GlibXMLRPCRequestHandler - - The stock SimpleXMLRPCRequestHandler and server don't allow any way to pass - the client's address and/or SSL certificate into the function that actually - _processes_ the request. So we have to store it in a thread-indexed dict. - """ - - def do_POST(self): - _add_authinfo(self.client_address) - try: - SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self) - except socket.timeout: - pass - except socket.error, e: - print "Error (%s): socket error - '%s'" % (self.client_address, e) - except: - print "Error while processing POST:" - traceback.print_exc() - _del_authinfo() + """ GlibXMLRPCRequestHandler + + The stock SimpleXMLRPCRequestHandler and server don't allow any way to pass + the client's address and/or SSL certificate into the function that actually + _processes_ the request. So we have to store it in a thread-indexed dict. + """ + + def do_POST(self): + _add_authinfo(self.client_address) + try: + SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.do_POST(self) + except socket.timeout: + pass + except socket.error, e: + print "Error (%s): socket error - '%s'" % (self.client_address, e) + except: + print "Error while processing POST:" + traceback.print_exc() + _del_authinfo() class GlibXMLRPCServer(GlibTCPServer, SimpleXMLRPCServer.SimpleXMLRPCDispatcher): - """GlibXMLRPCServer - - Use nonblocking sockets and handle the accept via glib rather than - blocking on accept(). - """ - - def __init__(self, addr, requestHandler=GlibXMLRPCRequestHandler, logRequests=0): - self.logRequests = logRequests - SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self) - GlibTCPServer.__init__(self, addr, requestHandler) - - def _marshaled_dispatch(self, data, dispatch_method = None): - """Dispatches an XML-RPC method from marshalled (XML) data. - - XML-RPC methods are dispatched from the marshalled (XML) data - using the _dispatch method and the result is returned as - marshalled data. For backwards compatibility, a dispatch - function can be provided as an argument (see comment in - SimpleXMLRPCRequestHandler.do_POST) but overriding the - existing method through subclassing is the prefered means - of changing method dispatch behavior. - """ - - params, method = xmlrpclib.loads(data) - - # generate response - try: - if dispatch_method is not None: - response = dispatch_method(method, params) - else: - response = self._dispatch(method, params) - # wrap response in a singleton tuple - response = (response,) - response = xmlrpclib.dumps(response, methodresponse=1) - except xmlrpclib.Fault, fault: - response = xmlrpclib.dumps(fault) - except: - print "Exception while processing request:" - traceback.print_exc() - - # report exception back to server - response = xmlrpclib.dumps( - xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)) - ) - - return response + """GlibXMLRPCServer + + Use nonblocking sockets and handle the accept via glib rather than + blocking on accept(). + """ + + def __init__(self, addr, requestHandler=GlibXMLRPCRequestHandler, logRequests=0): + self.logRequests = logRequests + SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self) + GlibTCPServer.__init__(self, addr, requestHandler) + + def _marshaled_dispatch(self, data, dispatch_method = None): + """Dispatches an XML-RPC method from marshalled (XML) data. + + XML-RPC methods are dispatched from the marshalled (XML) data + using the _dispatch method and the result is returned as + marshalled data. For backwards compatibility, a dispatch + function can be provided as an argument (see comment in + SimpleXMLRPCRequestHandler.do_POST) but overriding the + existing method through subclassing is the prefered means + of changing method dispatch behavior. + """ + + params, method = xmlrpclib.loads(data) + + # generate response + try: + if dispatch_method is not None: + response = dispatch_method(method, params) + else: + response = self._dispatch(method, params) + # wrap response in a singleton tuple + response = (response,) + response = xmlrpclib.dumps(response, methodresponse=1) + except xmlrpclib.Fault, fault: + response = xmlrpclib.dumps(fault) + except: + print "Exception while processing request:" + traceback.print_exc() + + # report exception back to server + response = xmlrpclib.dumps( + xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)) + ) + + return response class GlibHTTP(httplib.HTTP): - """Subclass HTTP so we can return it's connection class' socket.""" - def connect(self, host=None, port=None): - httplib.HTTP.connect(self, host, port) - self._conn.sock.setblocking(0) - def get_sock(self): - return self._conn.sock + """Subclass HTTP so we can return it's connection class' socket.""" + def connect(self, host=None, port=None): + httplib.HTTP.connect(self, host, port) + self._conn.sock.setblocking(0) + def get_sock(self): + return self._conn.sock class GlibXMLRPCTransport(xmlrpclib.Transport): - """Integrate the request with the glib mainloop rather than blocking.""" - ## - # Connect to server. - # - # @param host Target host. - # @return A connection handle. - - def __init__(self): - pass - - def make_connection(self, host): - """Use our own connection object so we can get its socket.""" - # create a HTTP connection object from a host descriptor - host, extra_headers, x509 = self.get_host_info(host) - return GlibHTTP(host) - - ## - # Send a complete request, and parse the response. - # - # @param host Target host. - # @param handler Target PRC handler. - # @param request_body XML-RPC request body. - # @param verbose Debugging flag. - # @return Parsed response. - - def start_request(self, host, handler, request_body, verbose=0, request_cb=None, user_data=None): - """Do the first half of the request by sending data to the remote - server. The bottom half bits get run when the remote server's response - actually comes back.""" - # issue XML-RPC request - - h = self.make_connection(host) - if verbose: - h.set_debuglevel(1) - - self.send_request(h, handler, request_body) - self.send_host(h, host) - self.send_user_agent(h) - self.send_content(h, request_body) - - # Schedule a GIOWatch so we don't block waiting for the response - gobject.io_add_watch(h.get_sock(), gobject.IO_IN, self._finish_request, - h, host, handler, verbose, request_cb, user_data) - - def _finish_request(self, source, condition, h, host, handler, verbose, request_cb, user_data): - """Parse and return response when the remote server actually returns it.""" - if not (condition & gobject.IO_IN): - return True - - try: - errcode, errmsg, headers = h.getreply() - except socket.error, err: - if err[0] != 104: - raise socket.error(err) - else: - gobject.idle_add(request_cb, RESULT_FAILED, None, user_data) - return False - - if errcode != 200: - raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg, headers) - self.verbose = verbose - response = self._parse_response(h.getfile(), h.get_sock()) - if request_cb: - if len(response) == 1: - response = response[0] - gobject.idle_add(request_cb, RESULT_SUCCESS, response, user_data) - return False + """Integrate the request with the glib mainloop rather than blocking.""" + ## + # Connect to server. + # + # @param host Target host. + # @return A connection handle. + + def __init__(self): + pass + + def make_connection(self, host): + """Use our own connection object so we can get its socket.""" + # create a HTTP connection object from a host descriptor + host, extra_headers, x509 = self.get_host_info(host) + return GlibHTTP(host) + + ## + # Send a complete request, and parse the response. + # + # @param host Target host. + # @param handler Target PRC handler. + # @param request_body XML-RPC request body. + # @param verbose Debugging flag. + # @return Parsed response. + + def start_request(self, host, handler, request_body, verbose=0, request_cb=None, user_data=None): + """Do the first half of the request by sending data to the remote + server. The bottom half bits get run when the remote server's response + actually comes back.""" + # issue XML-RPC request + + h = self.make_connection(host) + if verbose: + h.set_debuglevel(1) + + self.send_request(h, handler, request_body) + self.send_host(h, host) + self.send_user_agent(h) + self.send_content(h, request_body) + + # Schedule a GIOWatch so we don't block waiting for the response + gobject.io_add_watch(h.get_sock(), gobject.IO_IN, self._finish_request, + h, host, handler, verbose, request_cb, user_data) + + def _finish_request(self, source, condition, h, host, handler, verbose, request_cb, user_data): + """Parse and return response when the remote server actually returns it.""" + if not (condition & gobject.IO_IN): + return True + + try: + errcode, errmsg, headers = h.getreply() + except socket.error, err: + if err[0] != 104: + raise socket.error(err) + else: + gobject.idle_add(request_cb, RESULT_FAILED, None, user_data) + return False + + if errcode != 200: + raise xmlrpclib.ProtocolError(host + handler, errcode, errmsg, headers) + self.verbose = verbose + response = self._parse_response(h.getfile(), h.get_sock()) + if request_cb: + if len(response) == 1: + response = response[0] + gobject.idle_add(request_cb, RESULT_SUCCESS, response, user_data) + return False class _Method: - """Right, so python people thought it would be funny to make this - class private to xmlrpclib.py...""" - # some magic to bind an XML-RPC method to an RPC server. - # supports "nested" methods (e.g. examples.getStateName) - def __init__(self, send, name): - self.__send = send - self.__name = name - def __getattr__(self, name): - return _Method(self.__send, "%s.%s" % (self.__name, name)) - def __call__(self, request_cb, user_data, *args): - return self.__send(self.__name, request_cb, user_data, args) + """Right, so python people thought it would be funny to make this + class private to xmlrpclib.py...""" + # some magic to bind an XML-RPC method to an RPC server. + # supports "nested" methods (e.g. examples.getStateName) + def __init__(self, send, name): + self.__send = send + self.__name = name + def __getattr__(self, name): + return _Method(self.__send, "%s.%s" % (self.__name, name)) + def __call__(self, request_cb, user_data, *args): + return self.__send(self.__name, request_cb, user_data, args) class GlibServerProxy(xmlrpclib.ServerProxy): - """Subclass xmlrpclib.ServerProxy so we can run the XML-RPC request - in two parts, integrated with the glib mainloop, such that we don't - block anywhere. - - Using this object is somewhat special; it requires more arguments to each - XML-RPC request call than the normal xmlrpclib.ServerProxy object: - - client = GlibServerProxy("http://127.0.0.1:8888") - user_data = "bar" - xmlrpc_arg1 = "test" - xmlrpc_arg2 = "foo" - client.test(xmlrpc_test_cb, user_data, xmlrpc_arg1, xmlrpc_arg2) - - Here, 'xmlrpc_test_cb' is the callback function, which has the following - signature: - - def xmlrpc_test_cb(result_status, response, user_data=None): - ... - """ - def __init__(self, uri, encoding=None, verbose=0, allow_none=0): - self._transport = GlibXMLRPCTransport() - self._encoding = encoding - self._verbose = verbose - self._allow_none = allow_none - xmlrpclib.ServerProxy.__init__(self, uri, self._transport, encoding, verbose, allow_none) - - # get the url - import urllib - urltype, uri = urllib.splittype(uri) - if urltype not in ("http", "https"): - raise IOError, "unsupported XML-RPC protocol" - self._host, self._handler = urllib.splithost(uri) - if not self._handler: - self._handler = "/RPC2" - - def __request(self, methodname, request_cb, user_data, params): - """Call the method on the remote server. We just start the request here - and the transport itself takes care of scheduling the response callback - when the remote server returns the response. We don't want to block anywhere.""" - - request = xmlrpclib.dumps(params, methodname, encoding=self._encoding, - allow_none=self._allow_none) - - try: - response = self._transport.start_request( - self._host, - self._handler, - request, - verbose=self._verbose, - request_cb=request_cb, - user_data=user_data - ) - except socket.error, exc: - gobject.idle_add(request_cb, RESULT_FAILED, None, user_data) - - def __getattr__(self, name): - # magic method dispatcher - return _Method(self.__request, name) + """Subclass xmlrpclib.ServerProxy so we can run the XML-RPC request + in two parts, integrated with the glib mainloop, such that we don't + block anywhere. + + Using this object is somewhat special; it requires more arguments to each + XML-RPC request call than the normal xmlrpclib.ServerProxy object: + + client = GlibServerProxy("http://127.0.0.1:8888") + user_data = "bar" + xmlrpc_arg1 = "test" + xmlrpc_arg2 = "foo" + client.test(xmlrpc_test_cb, user_data, xmlrpc_arg1, xmlrpc_arg2) + + Here, 'xmlrpc_test_cb' is the callback function, which has the following + signature: + + def xmlrpc_test_cb(result_status, response, user_data=None): + ... + """ + def __init__(self, uri, encoding=None, verbose=0, allow_none=0): + self._transport = GlibXMLRPCTransport() + self._encoding = encoding + self._verbose = verbose + self._allow_none = allow_none + xmlrpclib.ServerProxy.__init__(self, uri, self._transport, encoding, verbose, allow_none) + + # get the url + import urllib + urltype, uri = urllib.splittype(uri) + if urltype not in ("http", "https"): + raise IOError, "unsupported XML-RPC protocol" + self._host, self._handler = urllib.splithost(uri) + if not self._handler: + self._handler = "/RPC2" + + def __request(self, methodname, request_cb, user_data, params): + """Call the method on the remote server. We just start the request here + and the transport itself takes care of scheduling the response callback + when the remote server returns the response. We don't want to block anywhere.""" + + request = xmlrpclib.dumps(params, methodname, encoding=self._encoding, + allow_none=self._allow_none) + + try: + response = self._transport.start_request( + self._host, + self._handler, + request, + verbose=self._verbose, + request_cb=request_cb, + user_data=user_data + ) + except socket.error, exc: + gobject.idle_add(request_cb, RESULT_FAILED, None, user_data) + + def __getattr__(self, name): + # magic method dispatcher + return _Method(self.__request, name) class GroupServer(object): - _MAX_MSG_SIZE = 500 + _MAX_MSG_SIZE = 500 - def __init__(self, address, port, data_cb): - self._address = address - self._port = port - self._data_cb = data_cb + def __init__(self, address, port, data_cb): + self._address = address + self._port = port + self._data_cb = data_cb - self._setup_listener() + self._setup_listener() - def _setup_listener(self): - # Listener socket - self._listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + def _setup_listener(self): + # Listener socket + self._listen_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Set some options to make it multicast-friendly - self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) + # Set some options to make it multicast-friendly + self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) + self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) - def start(self): - # Set some more multicast options - self._listen_sock.bind(('', self._port)) - self._listen_sock.settimeout(2) - intf = socket.gethostbyname(socket.gethostname()) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(intf) + socket.inet_aton('0.0.0.0')) - self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self._address) + socket.inet_aton('0.0.0.0')) + def start(self): + # Set some more multicast options + self._listen_sock.bind(('', self._port)) + self._listen_sock.settimeout(2) + intf = socket.gethostbyname(socket.gethostname()) + self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(intf) + socket.inet_aton('0.0.0.0')) + self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(self._address) + socket.inet_aton('0.0.0.0')) - # Watch the listener socket for data - gobject.io_add_watch(self._listen_sock, gobject.IO_IN, self._handle_incoming_data) + # Watch the listener socket for data + gobject.io_add_watch(self._listen_sock, gobject.IO_IN, self._handle_incoming_data) - def _handle_incoming_data(self, source, condition): - if not (condition & gobject.IO_IN): - return True - msg = {} - msg['data'], (msg['addr'], msg['port']) = source.recvfrom(self._MAX_MSG_SIZE) - if self._data_cb: - self._data_cb(msg) - return True + def _handle_incoming_data(self, source, condition): + if not (condition & gobject.IO_IN): + return True + msg = {} + msg['data'], (msg['addr'], msg['port']) = source.recvfrom(self._MAX_MSG_SIZE) + if self._data_cb: + self._data_cb(msg) + return True class GroupClient(object): - _MAX_MSG_SIZE = 500 + _MAX_MSG_SIZE = 500 - def __init__(self, address, port): - self._address = address - self._port = port + def __init__(self, address, port): + self._address = address + self._port = port - self._send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - # Make the socket multicast-aware, and set TTL. - self._send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) # Change TTL (=20) to suit + self._send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + # Make the socket multicast-aware, and set TTL. + self._send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20) # Change TTL (=20) to suit - def send_msg(self, data): - self._send_sock.sendto(data, (self._address, self._port)) + def send_msg(self, data): + self._send_sock.sendto(data, (self._address, self._port)) class Test(object): - def test(self, arg1): - print "Request got %s" % arg1 - return "success" + def test(self, arg1): + print "Request got %s" % arg1 + return "success" def xmlrpc_test_cb(response, user_data=None): - print "Response was %s, user_data was %s" % (response, user_data) - import gtk - gtk.main_quit() + print "Response was %s, user_data was %s" % (response, user_data) + import gtk + gtk.main_quit() def xmlrpc_test(): - client = GlibServerProxy("http://127.0.0.1:8888") - client.test(xmlrpc_test_cb, "bar", "test data") + client = GlibServerProxy("http://127.0.0.1:8888") + client.test(xmlrpc_test_cb, "bar", "test data") def main(): - import gtk - server = GlibXMLRPCServer(("", 8888)) - inst = Test() - server.register_instance(inst) - - gobject.idle_add(xmlrpc_test) - - try: - gtk.main() - except KeyboardInterrupt: - print 'Ctrl+C pressed, exiting...' - print "Done." + import gtk + server = GlibXMLRPCServer(("", 8888)) + inst = Test() + server.register_instance(inst) + + gobject.idle_add(xmlrpc_test) + + try: + gtk.main() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' + print "Done." if __name__ == "__main__": - main() + main() diff --git a/sugar/presence/Activity.py b/sugar/presence/Activity.py index e267d83..06c8a00 100644 --- a/sugar/presence/Activity.py +++ b/sugar/presence/Activity.py @@ -20,98 +20,98 @@ import dbus class Activity(gobject.GObject): - __gsignals__ = { - 'buddy-joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'service-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'service-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - - _PRESENCE_SERVICE = "org.laptop.Presence" - _ACTIVITY_DBUS_INTERFACE = "org.laptop.Presence.Activity" - - def __init__(self, bus, new_obj_cb, del_obj_cb, object_path): - gobject.GObject.__init__(self) - self._object_path = object_path - self._ps_new_object = new_obj_cb - self._ps_del_object = del_obj_cb - bobj = bus.get_object(self._PRESENCE_SERVICE, object_path) - self._activity = dbus.Interface(bobj, self._ACTIVITY_DBUS_INTERFACE) - self._activity.connect_to_signal('BuddyJoined', self._buddy_joined_cb) - self._activity.connect_to_signal('BuddyLeft', self._buddy_left_cb) - self._activity.connect_to_signal('ServiceAppeared', self._service_appeared_cb) - self._activity.connect_to_signal('ServiceDisappeared', self._service_disappeared_cb) - - self._id = None - self._color = None - - def object_path(self): - return self._object_path - - def _emit_buddy_joined_signal(self, object_path): - self.emit('buddy-joined', self._ps_new_object(object_path)) - return False - - def _buddy_joined_cb(self, object_path): - gobject.idle_add(self._emit_buddy_joined_signal, object_path) - - def _emit_buddy_left_signal(self, object_path): - self.emit('buddy-left', self._ps_new_object(object_path)) - return False - - def _buddy_left_cb(self, object_path): - gobject.idle_add(self._emit_buddy_left_signal, object_path) - - def _emit_service_appeared_signal(self, object_path): - self.emit('service-appeared', self._ps_new_object(object_path)) - return False - - def _service_appeared_cb(self, object_path): - gobject.idle_add(self._emit_service_appeared_signal, object_path) - - def _emit_service_disappeared_signal(self, object_path): - self.emit('service-disappeared', self._ps_new_object(object_path)) - return False - - def _service_disappeared_cb(self, object_path): - gobject.idle_add(self._emit_service_disappeared_signal, object_path) - - def get_id(self): - # Cache activity ID, which should never change anyway - if not self._id: - self._id = self._activity.getId() - return self._id - - def get_color(self): - if not self._color: - self._color = self._activity.getColor() - return self._color - - def get_services(self): - resp = self._activity.getServices() - servs = [] - for item in resp: - servs.append(self._ps_new_object(item)) - return servs - - def get_services_of_type(self, stype): - resp = self._activity.getServicesOfType(stype) - servs = [] - for item in resp: - servs.append(self._ps_new_object(item)) - return servs - - def get_joined_buddies(self): - resp = self._activity.getJoinedBuddies() - buddies = [] - for item in resp: - buddies.append(self._ps_new_object(item)) - return buddies - - def owner_has_joined(self): - # FIXME - return False + __gsignals__ = { + 'buddy-joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'service-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'service-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + _PRESENCE_SERVICE = "org.laptop.Presence" + _ACTIVITY_DBUS_INTERFACE = "org.laptop.Presence.Activity" + + def __init__(self, bus, new_obj_cb, del_obj_cb, object_path): + gobject.GObject.__init__(self) + self._object_path = object_path + self._ps_new_object = new_obj_cb + self._ps_del_object = del_obj_cb + bobj = bus.get_object(self._PRESENCE_SERVICE, object_path) + self._activity = dbus.Interface(bobj, self._ACTIVITY_DBUS_INTERFACE) + self._activity.connect_to_signal('BuddyJoined', self._buddy_joined_cb) + self._activity.connect_to_signal('BuddyLeft', self._buddy_left_cb) + self._activity.connect_to_signal('ServiceAppeared', self._service_appeared_cb) + self._activity.connect_to_signal('ServiceDisappeared', self._service_disappeared_cb) + + self._id = None + self._color = None + + def object_path(self): + return self._object_path + + def _emit_buddy_joined_signal(self, object_path): + self.emit('buddy-joined', self._ps_new_object(object_path)) + return False + + def _buddy_joined_cb(self, object_path): + gobject.idle_add(self._emit_buddy_joined_signal, object_path) + + def _emit_buddy_left_signal(self, object_path): + self.emit('buddy-left', self._ps_new_object(object_path)) + return False + + def _buddy_left_cb(self, object_path): + gobject.idle_add(self._emit_buddy_left_signal, object_path) + + def _emit_service_appeared_signal(self, object_path): + self.emit('service-appeared', self._ps_new_object(object_path)) + return False + + def _service_appeared_cb(self, object_path): + gobject.idle_add(self._emit_service_appeared_signal, object_path) + + def _emit_service_disappeared_signal(self, object_path): + self.emit('service-disappeared', self._ps_new_object(object_path)) + return False + + def _service_disappeared_cb(self, object_path): + gobject.idle_add(self._emit_service_disappeared_signal, object_path) + + def get_id(self): + # Cache activity ID, which should never change anyway + if not self._id: + self._id = self._activity.getId() + return self._id + + def get_color(self): + if not self._color: + self._color = self._activity.getColor() + return self._color + + def get_services(self): + resp = self._activity.getServices() + servs = [] + for item in resp: + servs.append(self._ps_new_object(item)) + return servs + + def get_services_of_type(self, stype): + resp = self._activity.getServicesOfType(stype) + servs = [] + for item in resp: + servs.append(self._ps_new_object(item)) + return servs + + def get_joined_buddies(self): + resp = self._activity.getJoinedBuddies() + buddies = [] + for item in resp: + buddies.append(self._ps_new_object(item)) + return buddies + + def owner_has_joined(self): + # FIXME + return False diff --git a/sugar/presence/Buddy.py b/sugar/presence/Buddy.py index 579592b..740b29b 100644 --- a/sugar/presence/Buddy.py +++ b/sugar/presence/Buddy.py @@ -21,173 +21,173 @@ import dbus class Buddy(gobject.GObject): - __gsignals__ = { - 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([])), - 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([])), - 'service-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'service-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'left-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'current-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - - _PRESENCE_SERVICE = "org.laptop.Presence" - _BUDDY_DBUS_INTERFACE = "org.laptop.Presence.Buddy" - - def __init__(self, bus, new_obj_cb, del_obj_cb, object_path): - gobject.GObject.__init__(self) - self._object_path = object_path - self._ps_new_object = new_obj_cb - self._ps_del_object = del_obj_cb - self._properties = {} - bobj = bus.get_object(self._PRESENCE_SERVICE, object_path) - self._buddy = dbus.Interface(bobj, self._BUDDY_DBUS_INTERFACE) - self._buddy.connect_to_signal('IconChanged', self._icon_changed_cb) - self._buddy.connect_to_signal('ServiceAppeared', self._service_appeared_cb) - self._buddy.connect_to_signal('ServiceDisappeared', self._service_disappeared_cb) - self._buddy.connect_to_signal('Disappeared', self._disappeared_cb) - self._buddy.connect_to_signal('JoinedActivity', self._joined_activity_cb) - self._buddy.connect_to_signal('LeftActivity', self._left_activity_cb) - self._buddy.connect_to_signal('PropertyChanged', self._property_changed_cb) - self._buddy.connect_to_signal('CurrentActivityChanged', self._current_activity_changed_cb) - self._properties = self._get_properties_helper() - - self._current_activity = None - try: - self._current_activity = self._buddy.getCurrentActivity() - except Exception, e: - pass - - def _get_properties_helper(self): - props = self._buddy.getProperties() - if not props: - return {} - return props - - def object_path(self): - return self._object_path - - def _emit_icon_changed_signal(self): - self.emit('icon-changed') - return False - - def _icon_changed_cb(self): - gobject.idle_add(self._emit_icon_changed_signal) - - def _emit_disappeared_signal(self): - self.emit('disappeared') - - def _disappeared_cb(self): - gobject.idle_add(self._emit_disappeared_signal) - - def _emit_service_appeared_signal(self, object_path): - self.emit('service-appeared', self._ps_new_object(object_path)) - return False - - def _service_appeared_cb(self, object_path): - gobject.idle_add(self._emit_service_appeared_signal, object_path) - - def _emit_service_disappeared_signal(self, object_path): - self.emit('service-disappeared', self._ps_new_object(object_path)) - return False - - def _service_disappeared_cb(self, object_path): - gobject.idle_add(self._emit_service_disappeared_signal, object_path) - - def _emit_joined_activity_signal(self, object_path): - self.emit('joined-activity', self._ps_new_object(object_path)) - return False - - def _joined_activity_cb(self, object_path): - gobject.idle_add(self._emit_joined_activity_signal, object_path) - - def _emit_left_activity_signal(self, object_path): - self.emit('left-activity', self._ps_new_object(object_path)) - return False - - def _left_activity_cb(self, object_path): - gobject.idle_add(self._emit_left_activity_signal, object_path) - - def _handle_property_changed_signal(self, prop_list): - self._properties = self._get_properties_helper() - self.emit('property-changed', prop_list) - return False - - def _property_changed_cb(self, prop_list): - gobject.idle_add(self._handle_property_changed_signal, prop_list) - - def _handle_current_activity_changed_signal(self, act_list): - if len(act_list) == 0: - self._current_activity = None - self.emit('current-activity-changed', None) - else: - self._current_activity = act_list[0] - self.emit('current-activity-changed', self._ps_new_object(act_list[0])) - return False - - def _current_activity_changed_cb(self, act_list): - gobject.idle_add(self._handle_current_activity_changed_signal, act_list) - - def get_name(self): - return self._properties['name'] - - def get_ip4_address(self): - return self._properties['ip4_address'] - - def is_owner(self): - return self._properties['owner'] - - def get_color(self): - return self._properties['color'] - - def get_icon(self): - return self._buddy.getIcon() - - def get_current_activity(self): - if not self._current_activity: - return None - return self._ps_new_object(self._current_activity) - - def get_icon_pixbuf(self): - icon = self._buddy.getIcon() - if icon and len(icon): - pbl = gtk.gdk.PixbufLoader() - icon_data = "" - for item in icon: - if item < 0: - item = item + 128 - icon_data = icon_data + chr(item) - pbl.write(icon_data) - pbl.close() - return pbl.get_pixbuf() - else: - return None - - def get_service_of_type(self, stype, activity=None): - try: - act_op = "/" - if activity: - act_op = activity.object_path() - object_path = self._buddy.getServiceOfType(stype, act_op) - except dbus.exceptions.DBusException: - return None - return self._ps_new_object(object_path) - - def get_joined_activities(self): - try: - resp = self._buddy.getJoinedActivities() - except dbus.exceptions.DBusException: - return [] - acts = [] - for item in resp: - acts.append(self._ps_new_object(item)) - return acts + __gsignals__ = { + 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), + 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), + 'service-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'service-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'left-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'current-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + _PRESENCE_SERVICE = "org.laptop.Presence" + _BUDDY_DBUS_INTERFACE = "org.laptop.Presence.Buddy" + + def __init__(self, bus, new_obj_cb, del_obj_cb, object_path): + gobject.GObject.__init__(self) + self._object_path = object_path + self._ps_new_object = new_obj_cb + self._ps_del_object = del_obj_cb + self._properties = {} + bobj = bus.get_object(self._PRESENCE_SERVICE, object_path) + self._buddy = dbus.Interface(bobj, self._BUDDY_DBUS_INTERFACE) + self._buddy.connect_to_signal('IconChanged', self._icon_changed_cb) + self._buddy.connect_to_signal('ServiceAppeared', self._service_appeared_cb) + self._buddy.connect_to_signal('ServiceDisappeared', self._service_disappeared_cb) + self._buddy.connect_to_signal('Disappeared', self._disappeared_cb) + self._buddy.connect_to_signal('JoinedActivity', self._joined_activity_cb) + self._buddy.connect_to_signal('LeftActivity', self._left_activity_cb) + self._buddy.connect_to_signal('PropertyChanged', self._property_changed_cb) + self._buddy.connect_to_signal('CurrentActivityChanged', self._current_activity_changed_cb) + self._properties = self._get_properties_helper() + + self._current_activity = None + try: + self._current_activity = self._buddy.getCurrentActivity() + except Exception, e: + pass + + def _get_properties_helper(self): + props = self._buddy.getProperties() + if not props: + return {} + return props + + def object_path(self): + return self._object_path + + def _emit_icon_changed_signal(self): + self.emit('icon-changed') + return False + + def _icon_changed_cb(self): + gobject.idle_add(self._emit_icon_changed_signal) + + def _emit_disappeared_signal(self): + self.emit('disappeared') + + def _disappeared_cb(self): + gobject.idle_add(self._emit_disappeared_signal) + + def _emit_service_appeared_signal(self, object_path): + self.emit('service-appeared', self._ps_new_object(object_path)) + return False + + def _service_appeared_cb(self, object_path): + gobject.idle_add(self._emit_service_appeared_signal, object_path) + + def _emit_service_disappeared_signal(self, object_path): + self.emit('service-disappeared', self._ps_new_object(object_path)) + return False + + def _service_disappeared_cb(self, object_path): + gobject.idle_add(self._emit_service_disappeared_signal, object_path) + + def _emit_joined_activity_signal(self, object_path): + self.emit('joined-activity', self._ps_new_object(object_path)) + return False + + def _joined_activity_cb(self, object_path): + gobject.idle_add(self._emit_joined_activity_signal, object_path) + + def _emit_left_activity_signal(self, object_path): + self.emit('left-activity', self._ps_new_object(object_path)) + return False + + def _left_activity_cb(self, object_path): + gobject.idle_add(self._emit_left_activity_signal, object_path) + + def _handle_property_changed_signal(self, prop_list): + self._properties = self._get_properties_helper() + self.emit('property-changed', prop_list) + return False + + def _property_changed_cb(self, prop_list): + gobject.idle_add(self._handle_property_changed_signal, prop_list) + + def _handle_current_activity_changed_signal(self, act_list): + if len(act_list) == 0: + self._current_activity = None + self.emit('current-activity-changed', None) + else: + self._current_activity = act_list[0] + self.emit('current-activity-changed', self._ps_new_object(act_list[0])) + return False + + def _current_activity_changed_cb(self, act_list): + gobject.idle_add(self._handle_current_activity_changed_signal, act_list) + + def get_name(self): + return self._properties['name'] + + def get_ip4_address(self): + return self._properties['ip4_address'] + + def is_owner(self): + return self._properties['owner'] + + def get_color(self): + return self._properties['color'] + + def get_icon(self): + return self._buddy.getIcon() + + def get_current_activity(self): + if not self._current_activity: + return None + return self._ps_new_object(self._current_activity) + + def get_icon_pixbuf(self): + icon = self._buddy.getIcon() + if icon and len(icon): + pbl = gtk.gdk.PixbufLoader() + icon_data = "" + for item in icon: + if item < 0: + item = item + 128 + icon_data = icon_data + chr(item) + pbl.write(icon_data) + pbl.close() + return pbl.get_pixbuf() + else: + return None + + def get_service_of_type(self, stype, activity=None): + try: + act_op = "/" + if activity: + act_op = activity.object_path() + object_path = self._buddy.getServiceOfType(stype, act_op) + except dbus.exceptions.DBusException: + return None + return self._ps_new_object(object_path) + + def get_joined_activities(self): + try: + resp = self._buddy.getJoinedActivities() + except dbus.exceptions.DBusException: + return [] + acts = [] + for item in resp: + acts.append(self._ps_new_object(item)) + return acts diff --git a/sugar/presence/PresenceService.py b/sugar/presence/PresenceService.py index d74b0c5..fd6091e 100644 --- a/sugar/presence/PresenceService.py +++ b/sugar/presence/PresenceService.py @@ -20,23 +20,23 @@ import dbus, dbus.glib, gobject import Buddy, Service, Activity class ObjectCache(object): - def __init__(self): - self._cache = {} + def __init__(self): + self._cache = {} - def get(self, object_path): - try: - return self._cache[object_path] - except KeyError: - return None + def get(self, object_path): + try: + return self._cache[object_path] + except KeyError: + return None - def add(self, obj): - op = obj.object_path() - if not self._cache.has_key(op): - self._cache[op] = obj + def add(self, obj): + op = obj.object_path() + if not self._cache.has_key(op): + self._cache[op] = obj - def remove(self, object_path): - if self._cache.has_key(object_path): - del self._cache[object_path] + def remove(self, object_path): + if self._cache.has_key(object_path): + del self._cache[object_path] DBUS_SERVICE = "org.laptop.Presence" @@ -46,192 +46,192 @@ DBUS_PATH = "/org/laptop/Presence" class PresenceService(gobject.GObject): - __gsignals__ = { - 'buddy-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'buddy-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'service-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'service-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'activity-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'activity-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - - _PS_BUDDY_OP = DBUS_PATH + "/Buddies/" - _PS_SERVICE_OP = DBUS_PATH + "/Services/" - _PS_ACTIVITY_OP = DBUS_PATH + "/Activities/" - - - def __init__(self): - gobject.GObject.__init__(self) - self._objcache = ObjectCache() - self._bus = dbus.SessionBus() - self._ps = dbus.Interface(self._bus.get_object(DBUS_SERVICE, - DBUS_PATH), DBUS_INTERFACE) - self._ps.connect_to_signal('BuddyAppeared', self._buddy_appeared_cb) - self._ps.connect_to_signal('BuddyDisappeared', self._buddy_disappeared_cb) - self._ps.connect_to_signal('ServiceAppeared', self._service_appeared_cb) - self._ps.connect_to_signal('ServiceDisappeared', self._service_disappeared_cb) - self._ps.connect_to_signal('ActivityAppeared', self._activity_appeared_cb) - self._ps.connect_to_signal('ActivityDisappeared', self._activity_disappeared_cb) - - def _new_object(self, object_path): - obj = self._objcache.get(object_path) - if not obj: - if object_path.startswith(self._PS_SERVICE_OP): - obj = Service.Service(self._bus, self._new_object, - self._del_object, object_path) - elif object_path.startswith(self._PS_BUDDY_OP): - obj = Buddy.Buddy(self._bus, self._new_object, - self._del_object, object_path) - elif object_path.startswith(self._PS_ACTIVITY_OP): - obj = Activity.Activity(self._bus, self._new_object, - self._del_object, object_path) - else: - raise RuntimeError("Unknown object type") - self._objcache.add(obj) - return obj - - def _del_object(self, object_path): - # FIXME - pass - - def _emit_buddy_appeared_signal(self, object_path): - self.emit('buddy-appeared', self._new_object(object_path)) - return False - - def _buddy_appeared_cb(self, op): - gobject.idle_add(self._emit_buddy_appeared_signal, op) - - def _emit_buddy_disappeared_signal(self, object_path): - self.emit('buddy-disappeared', self._new_object(object_path)) - return False - - def _buddy_disappeared_cb(self, object_path): - gobject.idle_add(self._emit_buddy_disappeared_signal, object_path) - - def _emit_service_appeared_signal(self, object_path): - self.emit('service-appeared', self._new_object(object_path)) - return False - - def _service_appeared_cb(self, object_path): - gobject.idle_add(self._emit_service_appeared_signal, object_path) - - def _emit_service_disappeared_signal(self, object_path): - self.emit('service-disappeared', self._new_object(object_path)) - return False - - def _service_disappeared_cb(self, object_path): - gobject.idle_add(self._emit_service_disappeared_signal, object_path) - - def _emit_activity_appeared_signal(self, object_path): - self.emit('activity-appeared', self._new_object(object_path)) - return False - - def _activity_appeared_cb(self, object_path): - gobject.idle_add(self._emit_activity_appeared_signal, object_path) - - def _emit_activity_disappeared_signal(self, object_path): - self.emit('activity-disappeared', self._new_object(object_path)) - return False - - def _activity_disappeared_cb(self, object_path): - gobject.idle_add(self._emit_activity_disappeared_signal, object_path) - - def get(self, object_path): - return self._new_object(object_path) - - def get_services(self): - resp = self._ps.getServices() - servs = [] - for item in resp: - servs.append(self._new_object(item)) - return servs - - def get_services_of_type(self, stype): - resp = self._ps.getServicesOfType(stype) - servs = [] - for item in resp: - servs.append(self._new_object(item)) - return servs - - def get_activities(self): - resp = self._ps.getActivities() - acts = [] - for item in resp: - acts.append(self._new_object(item)) - return acts - - def get_activity(self, activity_id): - try: - act_op = self._ps.getActivity(activity_id) - except dbus.exceptions.DBusException: - return None - return self._new_object(act_op) - - def get_buddies(self): - resp = self._ps.getBuddies() - buddies = [] - for item in resp: - buddies.append(self._new_object(item)) - return buddies - - def get_buddy_by_name(self, name): - try: - buddy_op = self._ps.getBuddyByName(name) - except dbus.exceptions.DBusException: - return None - return self._new_object(buddy_op) - - def get_buddy_by_address(self, addr): - try: - buddy_op = self._ps.getBuddyByAddress(addr) - except dbus.exceptions.DBusException: - return None - return self._new_object(buddy_op) - - def get_owner(self): - try: - owner_op = self._ps.getOwner() - except dbus.exceptions.DBusException: - return None - return self._new_object(owner_op) - - def share_activity(self, activity, stype, properties={}, address=None, port=-1, domain=u"local"): - actid = activity.get_id() - if address == None: - address = u"" - serv_op = self._ps.shareActivity(actid, stype, properties, address, port, domain) - return self._new_object(serv_op) - - def register_service(self, name, stype, properties={}, address=None, port=-1, domain=u"local"): - if address == None: - address = u"" - serv_op = self._ps.registerService(name, stype, properties, address, port, domain) - return self._new_object(serv_op) - - def unregister_service(self, service): - self._ps.unregisterService(service.object_path()) - - def register_service_type(self, stype): - self._ps.registerServiceType(stype) - - def unregister_service_type(self, stype): - self._ps.unregisterServiceType(stype) + __gsignals__ = { + 'buddy-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'buddy-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'service-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'service-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'activity-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'activity-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + _PS_BUDDY_OP = DBUS_PATH + "/Buddies/" + _PS_SERVICE_OP = DBUS_PATH + "/Services/" + _PS_ACTIVITY_OP = DBUS_PATH + "/Activities/" + + + def __init__(self): + gobject.GObject.__init__(self) + self._objcache = ObjectCache() + self._bus = dbus.SessionBus() + self._ps = dbus.Interface(self._bus.get_object(DBUS_SERVICE, + DBUS_PATH), DBUS_INTERFACE) + self._ps.connect_to_signal('BuddyAppeared', self._buddy_appeared_cb) + self._ps.connect_to_signal('BuddyDisappeared', self._buddy_disappeared_cb) + self._ps.connect_to_signal('ServiceAppeared', self._service_appeared_cb) + self._ps.connect_to_signal('ServiceDisappeared', self._service_disappeared_cb) + self._ps.connect_to_signal('ActivityAppeared', self._activity_appeared_cb) + self._ps.connect_to_signal('ActivityDisappeared', self._activity_disappeared_cb) + + def _new_object(self, object_path): + obj = self._objcache.get(object_path) + if not obj: + if object_path.startswith(self._PS_SERVICE_OP): + obj = Service.Service(self._bus, self._new_object, + self._del_object, object_path) + elif object_path.startswith(self._PS_BUDDY_OP): + obj = Buddy.Buddy(self._bus, self._new_object, + self._del_object, object_path) + elif object_path.startswith(self._PS_ACTIVITY_OP): + obj = Activity.Activity(self._bus, self._new_object, + self._del_object, object_path) + else: + raise RuntimeError("Unknown object type") + self._objcache.add(obj) + return obj + + def _del_object(self, object_path): + # FIXME + pass + + def _emit_buddy_appeared_signal(self, object_path): + self.emit('buddy-appeared', self._new_object(object_path)) + return False + + def _buddy_appeared_cb(self, op): + gobject.idle_add(self._emit_buddy_appeared_signal, op) + + def _emit_buddy_disappeared_signal(self, object_path): + self.emit('buddy-disappeared', self._new_object(object_path)) + return False + + def _buddy_disappeared_cb(self, object_path): + gobject.idle_add(self._emit_buddy_disappeared_signal, object_path) + + def _emit_service_appeared_signal(self, object_path): + self.emit('service-appeared', self._new_object(object_path)) + return False + + def _service_appeared_cb(self, object_path): + gobject.idle_add(self._emit_service_appeared_signal, object_path) + + def _emit_service_disappeared_signal(self, object_path): + self.emit('service-disappeared', self._new_object(object_path)) + return False + + def _service_disappeared_cb(self, object_path): + gobject.idle_add(self._emit_service_disappeared_signal, object_path) + + def _emit_activity_appeared_signal(self, object_path): + self.emit('activity-appeared', self._new_object(object_path)) + return False + + def _activity_appeared_cb(self, object_path): + gobject.idle_add(self._emit_activity_appeared_signal, object_path) + + def _emit_activity_disappeared_signal(self, object_path): + self.emit('activity-disappeared', self._new_object(object_path)) + return False + + def _activity_disappeared_cb(self, object_path): + gobject.idle_add(self._emit_activity_disappeared_signal, object_path) + + def get(self, object_path): + return self._new_object(object_path) + + def get_services(self): + resp = self._ps.getServices() + servs = [] + for item in resp: + servs.append(self._new_object(item)) + return servs + + def get_services_of_type(self, stype): + resp = self._ps.getServicesOfType(stype) + servs = [] + for item in resp: + servs.append(self._new_object(item)) + return servs + + def get_activities(self): + resp = self._ps.getActivities() + acts = [] + for item in resp: + acts.append(self._new_object(item)) + return acts + + def get_activity(self, activity_id): + try: + act_op = self._ps.getActivity(activity_id) + except dbus.exceptions.DBusException: + return None + return self._new_object(act_op) + + def get_buddies(self): + resp = self._ps.getBuddies() + buddies = [] + for item in resp: + buddies.append(self._new_object(item)) + return buddies + + def get_buddy_by_name(self, name): + try: + buddy_op = self._ps.getBuddyByName(name) + except dbus.exceptions.DBusException: + return None + return self._new_object(buddy_op) + + def get_buddy_by_address(self, addr): + try: + buddy_op = self._ps.getBuddyByAddress(addr) + except dbus.exceptions.DBusException: + return None + return self._new_object(buddy_op) + + def get_owner(self): + try: + owner_op = self._ps.getOwner() + except dbus.exceptions.DBusException: + return None + return self._new_object(owner_op) + + def share_activity(self, activity, stype, properties={}, address=None, port=-1, domain=u"local"): + actid = activity.get_id() + if address == None: + address = u"" + serv_op = self._ps.shareActivity(actid, stype, properties, address, port, domain) + return self._new_object(serv_op) + + def register_service(self, name, stype, properties={}, address=None, port=-1, domain=u"local"): + if address == None: + address = u"" + serv_op = self._ps.registerService(name, stype, properties, address, port, domain) + return self._new_object(serv_op) + + def unregister_service(self, service): + self._ps.unregisterService(service.object_path()) + + def register_service_type(self, stype): + self._ps.registerServiceType(stype) + + def unregister_service_type(self, stype): + self._ps.unregisterServiceType(stype) _ps = None def get_instance(): - global _ps - if not _ps: - _ps = PresenceService() - return _ps + global _ps + if not _ps: + _ps = PresenceService() + return _ps def start(): - bus = dbus.SessionBus() - ps = dbus.Interface(bus.get_object(DBUS_SERVICE, DBUS_PATH), DBUS_INTERFACE) - ps.start() + bus = dbus.SessionBus() + ps = dbus.Interface(bus.get_object(DBUS_SERVICE, DBUS_PATH), DBUS_INTERFACE) + ps.start() diff --git a/sugar/presence/Service.py b/sugar/presence/Service.py index a1ef98a..22b436f 100644 --- a/sugar/presence/Service.py +++ b/sugar/presence/Service.py @@ -20,101 +20,101 @@ import dbus def _one_dict_differs(dict1, dict2): - diff_keys = [] - for key, value in dict1.items(): - if not dict2.has_key(key) or dict2[key] != value: - diff_keys.append(key) - return diff_keys + diff_keys = [] + for key, value in dict1.items(): + if not dict2.has_key(key) or dict2[key] != value: + diff_keys.append(key) + return diff_keys def _dicts_differ(dict1, dict2): - diff_keys = [] - diff1 = _one_dict_differs(dict1, dict2) - diff2 = _one_dict_differs(dict2, dict1) - for key in diff2: - if key not in diff1: - diff_keys.append(key) - diff_keys += diff1 - return diff_keys + diff_keys = [] + diff1 = _one_dict_differs(dict1, dict2) + diff2 = _one_dict_differs(dict2, dict1) + for key in diff2: + if key not in diff1: + diff_keys.append(key) + diff_keys += diff1 + return diff_keys class Service(gobject.GObject): - _PRESENCE_SERVICE = "org.laptop.Presence" - _SERVICE_DBUS_INTERFACE = "org.laptop.Presence.Service" - - __gsignals__ = { - 'published-value-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - - def __init__(self, bus, new_obj_cb, del_obj_cb, object_path): - gobject.GObject.__init__(self) - self._object_path = object_path - self._ps_new_object = new_obj_cb - self._ps_del_object = del_obj_cb - sobj = bus.get_object(self._PRESENCE_SERVICE, object_path) - self._service = dbus.Interface(sobj, self._SERVICE_DBUS_INTERFACE) - self._service.connect_to_signal('PropertyChanged', self.__property_changed_cb) - self._service.connect_to_signal('PublishedValueChanged', - self.__published_value_changed_cb) - self._props = self._service.getProperties() - self._pubvals = self._service.getPublishedValues() - - def object_path(self): - return self._object_path - - def __property_changed_cb(self, prop_list): - self._props = self._service.getProperties() - - def get_published_value(self, key): - return self._pubvals[key] - - def get_published_values(self): - self._pubvals = self._service.getPublishedValues() - return self._pubvals - - def set_published_value(self, key, value): - if self._pubvals.has_key(key): - if self._pubvals[key] == value: - return - self._pubvals[key] = value - self._service.setPublishedValue(key, value) - - def set_published_values(self, vals): - self._service.setPublishedValues(vals) - self._pubvals = vals - - def __published_value_changed_cb(self, keys): - oldvals = self._pubvals - self.get_published_values() - diff_keys = _dicts_differ(oldvals, self._pubvals) - if len(diff_keys) > 0: - self.emit('published-value-changed', diff_keys) - - def get_name(self): - return self._props['name'] - - def get_type(self): - return self._props['type'] - - def get_domain(self): - return self._props['domain'] - - def get_address(self): - if self._props.has_key('address'): - return self._props['address'] - return None - - def get_activity_id(self): - if self._props.has_key('activityId'): - return self._props['activityId'] - return None - - def get_port(self): - if self._props.has_key('port'): - return self._props['port'] - return None - - def get_source_address(self): - if self._props.has_key('sourceAddress'): - return self._props['sourceAddress'] - return None + _PRESENCE_SERVICE = "org.laptop.Presence" + _SERVICE_DBUS_INTERFACE = "org.laptop.Presence.Service" + + __gsignals__ = { + 'published-value-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, bus, new_obj_cb, del_obj_cb, object_path): + gobject.GObject.__init__(self) + self._object_path = object_path + self._ps_new_object = new_obj_cb + self._ps_del_object = del_obj_cb + sobj = bus.get_object(self._PRESENCE_SERVICE, object_path) + self._service = dbus.Interface(sobj, self._SERVICE_DBUS_INTERFACE) + self._service.connect_to_signal('PropertyChanged', self.__property_changed_cb) + self._service.connect_to_signal('PublishedValueChanged', + self.__published_value_changed_cb) + self._props = self._service.getProperties() + self._pubvals = self._service.getPublishedValues() + + def object_path(self): + return self._object_path + + def __property_changed_cb(self, prop_list): + self._props = self._service.getProperties() + + def get_published_value(self, key): + return self._pubvals[key] + + def get_published_values(self): + self._pubvals = self._service.getPublishedValues() + return self._pubvals + + def set_published_value(self, key, value): + if self._pubvals.has_key(key): + if self._pubvals[key] == value: + return + self._pubvals[key] = value + self._service.setPublishedValue(key, value) + + def set_published_values(self, vals): + self._service.setPublishedValues(vals) + self._pubvals = vals + + def __published_value_changed_cb(self, keys): + oldvals = self._pubvals + self.get_published_values() + diff_keys = _dicts_differ(oldvals, self._pubvals) + if len(diff_keys) > 0: + self.emit('published-value-changed', diff_keys) + + def get_name(self): + return self._props['name'] + + def get_type(self): + return self._props['type'] + + def get_domain(self): + return self._props['domain'] + + def get_address(self): + if self._props.has_key('address'): + return self._props['address'] + return None + + def get_activity_id(self): + if self._props.has_key('activityId'): + return self._props['activityId'] + return None + + def get_port(self): + if self._props.has_key('port'): + return self._props['port'] + return None + + def get_source_address(self): + if self._props.has_key('sourceAddress'): + return self._props['sourceAddress'] + return None diff --git a/sugar/profile.py b/sugar/profile.py index 5b1c782..f731fba 100644 --- a/sugar/profile.py +++ b/sugar/profile.py @@ -21,34 +21,34 @@ from sugar import env from sugar.graphics.iconcolor import IconColor class _Profile(object): - def __init__(self): - self.name = None - self.color = None - self._load() + def __init__(self): + self.name = None + self.color = None + self._load() - def update(self): - self._load() + def update(self): + self._load() - def _load(self): - cp = ConfigParser() - config_path = os.path.join(env.get_profile_path(), 'config') - parsed = cp.read([config_path]) + def _load(self): + cp = ConfigParser() + config_path = os.path.join(env.get_profile_path(), 'config') + parsed = cp.read([config_path]) - if cp.has_option('Buddy', 'NickName'): - self.name = cp.get('Buddy', 'NickName') + if cp.has_option('Buddy', 'NickName'): + self.name = cp.get('Buddy', 'NickName') - if cp.has_option('Buddy', 'Color'): - self.color = IconColor(cp.get('Buddy', 'Color')) + if cp.has_option('Buddy', 'Color'): + self.color = IconColor(cp.get('Buddy', 'Color')) - del cp + del cp def get_nick_name(): - return _profile.name + return _profile.name def get_color(): - return _profile.color + return _profile.color def update(): - _profile.update() + _profile.update() _profile = _Profile() diff --git a/sugar/simulator.py b/sugar/simulator.py index 7a2d24f..c97f364 100644 --- a/sugar/simulator.py +++ b/sugar/simulator.py @@ -33,151 +33,151 @@ _PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp" _activity_refs = {} class _NameCollection(object): - def __init__(self): - self._names = copy.copy(_nick_names) + def __init__(self): + self._names = copy.copy(_nick_names) - def get_name(self): - i = random.randint(0, len(self._names)) - return self._names.pop(i) + def get_name(self): + i = random.randint(0, len(self._names)) + return self._names.pop(i) class _BotService(object): - def __init__(self, bot): - self._bot = bot - - def announce(self): - props = { 'color': self._bot.color.to_string() } - pservice = PresenceService.get_instance() - self._service = pservice.register_service(self._bot.name, - _PRESENCE_SERVICE_TYPE, properties=props) - - self._stream = Stream.Stream.new_from_service(self._service) - self._stream.register_reader_handler( - self._handle_buddy_icon_request, "get_buddy_icon") - self._stream.register_reader_handler( - self._handle_invite, "invite") - - def _handle_buddy_icon_request(self): - if self._bot.icon: - fd = open(self._bot.icon, "r") - icon_data = fd.read() - fd.close() - if icon_data: - return base64.b64encode(self._icon) - return '' - - def _handle_invite(self, issuer, bundle_id, activity_id): - return '' - - def set_current_activity(self, activity_id): - self._service.set_published_value('curact', dbus.String(activity_id)) + def __init__(self, bot): + self._bot = bot + + def announce(self): + props = { 'color': self._bot.color.to_string() } + pservice = PresenceService.get_instance() + self._service = pservice.register_service(self._bot.name, + _PRESENCE_SERVICE_TYPE, properties=props) + + self._stream = Stream.Stream.new_from_service(self._service) + self._stream.register_reader_handler( + self._handle_buddy_icon_request, "get_buddy_icon") + self._stream.register_reader_handler( + self._handle_invite, "invite") + + def _handle_buddy_icon_request(self): + if self._bot.icon: + fd = open(self._bot.icon, "r") + icon_data = fd.read() + fd.close() + if icon_data: + return base64.b64encode(self._icon) + return '' + + def _handle_invite(self, issuer, bundle_id, activity_id): + return '' + + def set_current_activity(self, activity_id): + self._service.set_published_value('curact', dbus.String(activity_id)) class _JoinActivityAction(object): - def __init__(self, bot, named_ref): - self._bot = bot - self._named_ref = named_ref + def __init__(self, bot, named_ref): + self._bot = bot + self._named_ref = named_ref - def execute(self): - activity_id = _activity_refs[self._named_ref] + def execute(self): + activity_id = _activity_refs[self._named_ref] - pservice = PresenceService.get_instance() - activity = pservice.get_activity(activity_id) - service = activity.get_services()[0] + pservice = PresenceService.get_instance() + activity = pservice.get_activity(activity_id) + service = activity.get_services()[0] - name = "%s [%s]" % (self._bot.name, activity_id) - properties = { 'title' : service.get_published_value('title'), - 'color' : service.get_published_value('color') } + name = "%s [%s]" % (self._bot.name, activity_id) + properties = { 'title' : service.get_published_value('title'), + 'color' : service.get_published_value('color') } - pservice.register_service(name, service.get_type(), - properties, service.get_address(), - service.get_port()) + pservice.register_service(name, service.get_type(), + properties, service.get_address(), + service.get_port()) - self._bot._service.set_current_activity(activity_id) + self._bot._service.set_current_activity(activity_id) class _ChangeActivityAction(object): - def __init__(self, bot, named_ref): - self._bot = bot - self._named_ref = named_ref + def __init__(self, bot, named_ref): + self._bot = bot + self._named_ref = named_ref - def execute(self): - activity_id = _activity_refs[self._named_ref] - self._bot._service.set_current_activity(activity_id) + def execute(self): + activity_id = _activity_refs[self._named_ref] + self._bot._service.set_current_activity(activity_id) class _ShareChatAction(object): - def __init__(self, bot, named_ref, title): - self._bot = bot - self._title = title - self._id = util.unique_id() + def __init__(self, bot, named_ref, title): + self._bot = bot + self._title = title + self._id = util.unique_id() - _activity_refs[named_ref] = self._id + _activity_refs[named_ref] = self._id - def execute(self): - name = "%s [%s]" % (self._bot.name, self._id) - stype = '_GroupChatActivity_Sugar_redhat_com._udp' - properties = { 'title' : self._title, - 'color' : self._bot.color.to_string() } - address = u"232.%d.%d.%d" % (random.randint(0, 254), - random.randint(1, 254), - random.randint(1, 254)) + def execute(self): + name = "%s [%s]" % (self._bot.name, self._id) + stype = '_GroupChatActivity_Sugar_redhat_com._udp' + properties = { 'title' : self._title, + 'color' : self._bot.color.to_string() } + address = u"232.%d.%d.%d" % (random.randint(0, 254), + random.randint(1, 254), + random.randint(1, 254)) - pservice = PresenceService.get_instance() - pservice.register_service(name, stype, properties, address) + pservice = PresenceService.get_instance() + pservice.register_service(name, stype, properties, address) class _WaitAction(object): - def __init__(self, bot, seconds): - self._bot = bot - self._seconds = seconds - - def execute(self): - self._bot._pause_queue(self._seconds) + def __init__(self, bot, seconds): + self._bot = bot + self._seconds = seconds + + def execute(self): + self._bot._pause_queue(self._seconds) class Bot(object): - _name_collection = _NameCollection() + _name_collection = _NameCollection() - def __init__(self): - self.name = Bot._name_collection.get_name() - self.color = IconColor() - self.icon = None + def __init__(self): + self.name = Bot._name_collection.get_name() + self.color = IconColor() + self.icon = None - self._queue = [] + self._queue = [] - def wait(self, seconds): - action = _WaitAction(self, seconds) - self._queue.append(action) + def wait(self, seconds): + action = _WaitAction(self, seconds) + self._queue.append(action) - def share_chat(self, activity_id, title): - action = _ShareChatAction(self, activity_id, title) - self._queue.append(action) + def share_chat(self, activity_id, title): + action = _ShareChatAction(self, activity_id, title) + self._queue.append(action) - def change_activity(self, activity_id): - action = _ChangeActivityAction(self, activity_id) - self._queue.append(action) + def change_activity(self, activity_id): + action = _ChangeActivityAction(self, activity_id) + self._queue.append(action) - def join_activity(self, activity_id): - action = _JoinActivityAction(self, activity_id) - self._queue.append(action) + def join_activity(self, activity_id): + action = _JoinActivityAction(self, activity_id) + self._queue.append(action) - def start(self): - self._service = _BotService(self) - self._service.announce() + def start(self): + self._service = _BotService(self) + self._service.announce() - self._start_queue() + self._start_queue() - def _idle_cb(self): - self._next_action() - return True + def _idle_cb(self): + self._next_action() + return True - def _pause_done_cb(self): - self._start_queue() - return False + def _pause_done_cb(self): + self._start_queue() + return False - def _start_queue(self): - self._queue_sid = gobject.idle_add(self._idle_cb) + def _start_queue(self): + self._queue_sid = gobject.idle_add(self._idle_cb) - def _pause_queue(self, seconds): - gobject.source_remove(self._queue_sid) - gobject.timeout_add(int(seconds * 1000), self._pause_done_cb) + def _pause_queue(self, seconds): + gobject.source_remove(self._queue_sid) + gobject.timeout_add(int(seconds * 1000), self._pause_done_cb) - def _next_action(self): - if len(self._queue) > 0: - action = self._queue.pop(0) - action.execute() + def _next_action(self): + if len(self._queue) > 0: + action = self._queue.pop(0) + action.execute() diff --git a/sugar/util.py b/sugar/util.py index 108c48e..8ad840d 100644 --- a/sugar/util.py +++ b/sugar/util.py @@ -25,50 +25,50 @@ from ConfigParser import ConfigParser from ConfigParser import NoOptionError def printable_hash(in_hash): - """Convert binary hash data into printable characters.""" - printable = "" - for char in in_hash: - printable = printable + binascii.b2a_hex(char) - return printable + """Convert binary hash data into printable characters.""" + printable = "" + for char in in_hash: + printable = printable + binascii.b2a_hex(char) + return printable def _sha_data(data): - """sha1 hash some bytes.""" - sha_hash = sha.new() - sha_hash.update(data) - return sha_hash.digest() + """sha1 hash some bytes.""" + sha_hash = sha.new() + sha_hash.update(data) + return sha_hash.digest() def unique_id(data = ''): - data_string = "%s%s%s" % (time.time(), random.randint(10000, 100000), data) - return printable_hash(_sha_data(data_string)) + data_string = "%s%s%s" % (time.time(), random.randint(10000, 100000), data) + return printable_hash(_sha_data(data_string)) ACTIVITY_ID_LEN = 40 def is_hex(s): - return s.strip(string.hexdigits) == '' + return s.strip(string.hexdigits) == '' def validate_activity_id(actid): - """Validate an activity ID.""" - if not isinstance(actid, str) and not isinstance(actid, unicode): - return False - if len(actid) != ACTIVITY_ID_LEN: - return False - if not is_hex(actid): - return False - return True + """Validate an activity ID.""" + if not isinstance(actid, str) and not isinstance(actid, unicode): + return False + if len(actid) != ACTIVITY_ID_LEN: + return False + if not is_hex(actid): + return False + return True class _ServiceParser(ConfigParser): - def optionxform(self, option): - return option + def optionxform(self, option): + return option def write_service(name, bin, path): - service_cp = _ServiceParser() - section = 'D-BUS Service' - service_cp.add_section(section) - service_cp.set(section, 'Name', name) - service_cp.set(section, 'Exec', bin) + service_cp = _ServiceParser() + section = 'D-BUS Service' + service_cp.add_section(section) + service_cp.set(section, 'Name', name) + service_cp.set(section, 'Exec', bin) - dest_filename = os.path.join(path, name + '.service') - fileobject = open(dest_filename, 'w') - service_cp.write(fileobject) - fileobject.close() + dest_filename = os.path.join(path, name + '.service') + fileobject = open(dest_filename, 'w') + service_cp.write(fileobject) + fileobject.close() -- cgit v0.9.1