diff --git a/configure.ac b/configure.ac index 0828bda5..e31440d0 100644 --- a/configure.ac +++ b/configure.ac @@ -22,5 +22,6 @@ sugar/__installed__.py sugar/browser/Makefile sugar/chat/Makefile sugar/p2p/Makefile +sugar/p2p/model/Makefile sugar/shell/Makefile ]) diff --git a/pylint.sh b/pylint.sh new file mode 100755 index 00000000..4de5a507 --- /dev/null +++ b/pylint.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +# Errors that we may fix in the future +# +# C0301 Line length +# W0201 Attribute defined outside __init__ +# R0201 Method could be a function +TODO="C0301,W0201,R0201" + +# Errors we don't like +# +# W0613 Unused argument (glib signals) +# W0511 - FIXME/TODO/XXX +DISABLE="W0613,W0511" + +MSGS="$TODO,$DISABLE" +ARG="sugar" + +pylint \ + --disable-all \ + --include-ids=y \ + --enable-variables=y \ + --enable-exceptions=y \ + --enable-miscellaneous=y \ + --enable-format=y \ + --enable-classes=y \ + --disable-msg=$MSGS \ + --reports=n \ + --enable-metrics=n \ + --indent-string=" " \ + --ignore="SVGdraw.py" \ + $ARG diff --git a/sugar/browser/browser.py b/sugar/browser/browser.py index 633fa044..fc5ce0a1 100755 --- a/sugar/browser/browser.py +++ b/sugar/browser/browser.py @@ -11,6 +11,7 @@ import gtk import geckoembed from sugar.shell import activity +from sugar.p2p.Group import LocalGroup import sugar.env class AddressToolbar(gtk.Toolbar): @@ -77,7 +78,7 @@ class AddressEntry(gtk.HBox): return self.folded def set_folded(self, folded): - self.folded = not self.folded + self.folded = folded self._update_folded_state() def __button_clicked_cb(self, button): @@ -88,11 +89,12 @@ class AddressEntry(gtk.HBox): self.set_folded(True) class NavigationToolbar(gtk.Toolbar): - def __init__(self, embed): + def __init__(self, browser): gtk.Toolbar.__init__(self) - self.embed = embed + self._browser = browser + self._embed = self._browser.get_embed() - self.set_style(gtk.TOOLBAR_ICONS) + self.set_style(gtk.TOOLBAR_BOTH_HORIZ) self.back = gtk.ToolButton(gtk.STOCK_GO_BACK) self.back.connect("clicked", self.__go_back_cb) @@ -113,7 +115,9 @@ class NavigationToolbar(gtk.Toolbar): self.insert(separator, -1) separator.show() - share = gtk.ToolButton("Share") + share = gtk.ToolButton(None, "Share") + share.set_icon_name('stock_shared-by-me') + share.set_is_important(True) share.connect("clicked", self.__share_cb) self.insert(share, -1) share.show() @@ -128,34 +132,42 @@ class NavigationToolbar(gtk.Toolbar): self._update_sensitivity() - self.embed.connect("location", self.__location_changed) + self._embed.connect("location", self.__location_changed) def _update_sensitivity(self): - self.back.set_sensitive(self.embed.can_go_back()) - self.forward.set_sensitive(self.embed.can_go_forward()) + self.back.set_sensitive(self._embed.can_go_back()) + self.forward.set_sensitive(self._embed.can_go_forward()) def __go_back_cb(self, button): - self.embed.go_back() + self._embed.go_back() def __go_forward_cb(self, button): - self.embed.go_forward() + self._embed.go_forward() def __reload_cb(self, button): - self.embed.reload() + self._embed.reload() def __share_cb(self, button): - pass + self._browser.share() def __location_changed(self, embed): self._update_sensitivity() def __open_address_cb(self, address): - self.embed.load_address(address) + self._embed.load_address(address) class BrowserActivity(activity.Activity): - def __init__(self, uri): + def __init__(self, group, uri): activity.Activity.__init__(self) + self.uri = uri + self._group = group + + def _setup_shared(self, uri): + self._model = self._group.get_store().get_model(uri) + if self._model: + self._load_shared_address() + self._model.add_listener(self.__shared_address_changed_cb) def activity_on_connected_to_shell(self): self.activity_set_ellipsize_tab(True) @@ -173,7 +185,7 @@ class BrowserActivity(activity.Activity): self.embed.show() self.embed.load_address(self.uri) - nav_toolbar = NavigationToolbar(self.embed) + nav_toolbar = NavigationToolbar(self) vbox.pack_start(nav_toolbar, False) nav_toolbar.show() @@ -182,12 +194,36 @@ class BrowserActivity(activity.Activity): plug.show() vbox.show() + + self._setup_shared(uri) def get_embed(self): return self.embed + def share(self): + address = self.embed.get_address() + self._model = self._group.get_store().create_model(address) + self._model.set_value('current_address', address) + self._model.add_listener(self.__shared_address_changed_cb) + + bus = dbus.SessionBus() + proxy_obj = bus.get_object('com.redhat.Sugar.Chat', '/com/redhat/Sugar/Chat') + chat_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.ChatShell') + chat_shell.send_message('' + + self.embed.get_title() + '') + def __title_cb(self, embed): self.activity_set_tab_text(embed.get_title()) + # Temporary hack, we need an UI + self._model.set_value('current_address', self.embed.get_address()) + + def _load_shared_address(self): + address = self._model.get_value("current_address") + if address != self.embed.get_address(): + self.embed.load_address(address) + + def __shared_address_changed_cb(self, model, key): + self._load_shared_address() def activity_on_close_from_user(self): self.activity_shutdown() @@ -229,7 +265,6 @@ class WebActivity(activity.Activity): def activity_on_disconnected_from_shell(self): gtk.main_quit() - gc.collect() class BrowserShell(dbus.service.Object): instance = None @@ -249,6 +284,7 @@ class BrowserShell(dbus.service.Object): dbus.service.Object.__init__(self, bus_name, object_path) self.__browsers = [] + self._group = LocalGroup() def open_web_activity(self): web_activity = WebActivity() @@ -267,17 +303,13 @@ class BrowserShell(dbus.service.Object): @dbus.service.method('com.redhat.Sugar.BrowserShell') def open_browser(self, uri): - browser = BrowserActivity(uri) + browser = BrowserActivity(self._group, uri) self.__browsers.append(browser) browser.activity_connect_to_shell() def main(): BrowserShell.get_instance().open_web_activity() - - try: - gtk.main() - except KeyboardInterrupt: - pass + gtk.main() -if __name__=="__main__": - main() +if __name__ == "__main__": + main() diff --git a/sugar/chat/BuddyList.py b/sugar/chat/BuddyList.py deleted file mode 100644 index d35fa847..00000000 --- a/sugar/chat/BuddyList.py +++ /dev/null @@ -1,121 +0,0 @@ -# -*- tab-width: 4; indent-tabs-mode: t -*- - -import presence -import avahi - -ACTION_BUDDY_ADDED = "added" -ACTION_BUDDY_REMOVED = "removed" - - -class Buddy(object): - def __init__(self, nick, realname, servicename, host, address, port, key=None): - self._nick = nick - self._realname = realname - self._servicename = servicename - self._key = key - self._host = host - self._address = str(address) - self._port = int(port) - self._chat = None - - def set_chat(self, chat): - self._chat = chat - - def chat(self): - return self._chat - - def nick(self): - return self._nick - - def realname(self): - return self._realname - - def servicename(self): - return self._servicename - - def host(self): - return self._host - - def address(self): - return self._address - - def port(self): - return self._port - - def key(self): - return self._key - -class BuddyList(object): - """ Manage a list of buddies """ - - def __init__(self, servicename): - self._listeners = [] - self._buddies = {} - self._servicename = servicename - self._pdiscovery = presence.PresenceDiscovery() - self._pdiscovery.add_service_listener(self._on_service_change) - - def start(self): - self._pdiscovery.start() - - def add_buddy_listener(self, listener): - self._listeners.append(listener) - - def _add_buddy(self, host, address, port, servicename, data): - # Ignore ourselves - if servicename == self._servicename: - return - - if len(data) > 0 and 'name' in data.keys(): - buddy = self._find_buddy_by_service_name(servicename) - if not buddy: - buddy = Buddy(data['name'], data['realname'], servicename, host, address, port) - self._buddies[data['name']] = buddy - self._notify_listeners(ACTION_BUDDY_ADDED, buddy) - - def _remove_buddy(self, buddy): - nick = buddy.nick() - self._notify_listeners(ACTION_BUDDY_REMOVED, buddy) - del self._buddies[nick] - - def _find_buddy_by_service_name(self, servicename): - for buddy in self._buddies.values(): - if buddy.servicename() == servicename: - return buddy - return None - - def find_buddy_by_address(self, address): - for buddy_name in self._buddies.keys(): - buddy = self._buddies[buddy_name] - if buddy.address() == address: - return buddy - return None - - def _notify_listeners(self, action, buddy): - for listener in self._listeners: - listener(action, buddy) - - def _on_service_change(self, action, interface, protocol, name, stype, domain, flags): - if stype != presence.OLPC_CHAT_SERVICE: - return - if action == presence.ACTION_SERVICE_NEW: - self._pdiscovery.resolve_service(interface, protocol, name, stype, domain, self._on_service_resolved) - elif action == presence.ACTION_SERVICE_REMOVED: - buddy = self._find_buddy_by_service_name(name) - if buddy: - self._remove_buddy(buddy) - - def _pair_to_dict(self, l): - res = {} - for el in l: - tmp = el.split('=', 1) - if len(tmp) > 1: - res[tmp[0]] = tmp[1] - else: - res[tmp[0]] = '' - return res - - def _on_service_resolved(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): - data = self._pair_to_dict(avahi.txt_array_to_string_array(txt)) - self._add_buddy(host, address, port, name, data) - diff --git a/sugar/chat/chat.py b/sugar/chat/chat.py index 2bc72305..fef2c838 100755 --- a/sugar/chat/chat.py +++ b/sugar/chat/chat.py @@ -10,9 +10,11 @@ pygtk.require('2.0') import gtk, gobject from sugar.shell import activity -from sugar.p2p.Group import * -from sugar.p2p.StreamReader import * -from sugar.p2p.StreamWriter import * +from sugar.p2p.Group import Group +from sugar.p2p.Group import LocalGroup +from sugar.p2p.Service import Service +from sugar.p2p.StreamReader import StreamReader +from sugar.p2p.StreamWriter import StreamWriter import sugar.env import richtext @@ -71,14 +73,14 @@ class Chat(activity.Activity): self._hbox = gtk.HBox(False, 12) self._hbox.set_border_width(12) - [chat_vbox, buffer] = self._create_chat() + [chat_vbox, buf] = self._create_chat() self._hbox.pack_start(chat_vbox) chat_vbox.show() vbox.pack_start(self._hbox) self._hbox.show() - toolbar = self._create_toolbar(buffer) + toolbar = self._create_toolbar(buf) vbox.pack_start(toolbar, False) toolbar.show() @@ -130,37 +132,37 @@ class Chat(activity.Activity): button.set_menu(menu) def activity_on_close_from_user(self): - print "act %d: in activity_on_close_from_user"%self.activity_get_id() + print "act %d: in activity_on_close_from_user" % self.activity_get_id() self.activity_shutdown() def activity_on_lost_focus(self): - print "act %d: in activity_on_lost_focus"%self.activity_get_id() + print "act %d: in activity_on_lost_focus" % self.activity_get_id() def activity_on_got_focus(self): - print "act %d: in activity_on_got_focus"%self.activity_get_id() - self._controller.notify_activate(self) + print "act %d: in activity_on_got_focus" % self.activity_get_id() + # FIXME self._controller.notify_activate(self) def recv_message(self, buddy, msg): self._insert_rich_message(buddy.get_nick_name(), msg) self._controller.notify_new_message(self, buddy) def _insert_rich_message(self, nick, msg): - buffer = self._chat_view.get_buffer() - aniter = buffer.get_end_iter() - buffer.insert(aniter, nick + ": ") + buf = self._chat_view.get_buffer() + aniter = buf.get_end_iter() + buf.insert(aniter, nick + ": ") serializer = richtext.RichTextSerializer() - serializer.deserialize(msg, buffer) + serializer.deserialize(msg, buf) - aniter = buffer.get_end_iter() - buffer.insert(aniter, "\n") + aniter = buf.get_end_iter() + buf.insert(aniter, "\n") def _local_message(self, success, text): if not success: message = "Error: %s\n" % text - buffer = self._chat_view.get_buffer() - aniter = buffer.get_end_iter() - buffer.insert(aniter, message) + buf = self._chat_view.get_buffer() + aniter = buf.get_end_iter() + buf.insert(aniter, message) else: owner = self._controller.get_group().get_owner() self._insert_rich_message(owner.get_nick_name(), text) @@ -325,11 +327,11 @@ class GroupChat(Chat): if buddy.get_nick_name() == self._group.get_owner().get_nick_name(): # Do not show ourself in the buddy list pass - elif action == BUDDY_JOIN: + elif action == Group.BUDDY_JOIN: aniter = self._buddy_list_model.append(None) self._buddy_list_model.set(aniter, self._MODEL_COL_NICK, buddy.get_nick_name(), self._MODEL_COL_ICON, None, self._MODEL_COL_BUDDY, buddy) - elif action == BUDDY_LEAVE: + elif action == Group.BUDDY_LEAVE: aniter = self._get_iter_for_buddy(buddy) if aniter: self._buddy_list_model.remove(aniter) @@ -346,7 +348,7 @@ class GroupChat(Chat): aniter = self._get_iter_for_buddy(buddy) self._buddy_list_model.set(aniter, self._MODEL_COL_ICON, self._pixbuf_new_message) - def notify_activate(self, chat): + def notify_activate(self, chat, buddy): aniter = self._get_iter_for_buddy(buddy) self._buddy_list_model.set(aniter, self._MODEL_COL_ICON, self._pixbuf_active_chat) @@ -387,19 +389,16 @@ class ChatShell(dbus.service.Object): dbus.service.Object.__init__(self, bus_name, object_path) def open_group_chat(self): - group_chat = GroupChat() - group_chat.activity_connect_to_shell() + self._group_chat = GroupChat() + self._group_chat.activity_connect_to_shell() @dbus.service.method('com.redhat.Sugar.ChatShell') def send_message(self, message): - pass + self._group_chat.send_message(message) def main(): ChatShell.get_instance().open_group_chat() - try: - gtk.main() - except KeyboardInterrupt: - pass + gtk.main() if __name__ == "__main__": main() diff --git a/sugar/chat/richtext.py b/sugar/chat/richtext.py index 0ac70b16..ecb15c2e 100644 --- a/sugar/chat/richtext.py +++ b/sugar/chat/richtext.py @@ -45,7 +45,7 @@ class RichTextView(gtk.TextView): def __motion_notify_cb(self, widget, event): if event.is_hint: - [x, y, state] = event.window.get_pointer(); + event.window.get_pointer(); it = self.__get_event_iter(event) if it: @@ -134,9 +134,9 @@ class RichTextBuffer(gtk.TextBuffer): 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) + pos_end = pos.copy() + pos_end.backward_chars(length) + self.apply_tag_by_name(tag, pos, pos_end) class RichTextToolbar(gtk.Toolbar): def __init__(self, buf): @@ -205,6 +205,7 @@ class RichTextToolbar(gtk.Toolbar): 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 = [] @@ -286,7 +287,7 @@ class RichTextSerializer: def serialize(self, buf): self.buf = buf - xml = "" + res = "" next_it = buf.get_start_iter() while not next_it.is_end(): @@ -299,29 +300,29 @@ class RichTextSerializer: for tag in it.get_toggled_tags(False): while 1: open_tag = self._open_tags.pop() - xml += self.serialize_tag_end(tag) + 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) - xml += self.serialize_tag_start(tag, it) + res += self.serialize_tag_start(tag, it) for tag in it.get_toggled_tags(True): self._open_tags.append(tag) - xml += self.serialize_tag_start(tag, it) + res += self.serialize_tag_start(tag, it) - xml += buf.get_text(it, next_it, False) + res += buf.get_text(it, next_it, False) if next_it.is_end(): self._open_tags.reverse() for tag in self._open_tags: - xml += self.serialize_tag_end(tag) + res += self.serialize_tag_end(tag) - xml += "" + res += "" - return xml + return res def deserialize(self, xml_string, buf): parser = xml.sax.make_parser() @@ -330,11 +331,11 @@ class RichTextSerializer: parser.feed(xml_string) parser.close() -def test_quit(window, rich_buf): - print RichTextSerializer().serialize(rich_buf) +def test_quit(w, rb): + print RichTextSerializer().serialize(rb) gtk.main_quit() -def link_clicked(view, address): +def link_clicked(v, address): print "Link clicked " + address if __name__ == "__main__": @@ -350,15 +351,15 @@ if __name__ == "__main__": rich_buf = view.get_buffer() - xml_string = "" + test_xml = "" - xml_string += "Testone\n" - xml_string += "Test two" - xml_string += "Test three" - xml_string += "Test link" - xml_string += "" + test_xml += "Testone\n" + test_xml += "Test two" + test_xml += "Test three" + test_xml += "Test link" + test_xml += "" - RichTextSerializer().deserialize(xml_string, rich_buf) + RichTextSerializer().deserialize(test_xml, rich_buf) toolbar = RichTextToolbar(rich_buf) vbox.pack_start(toolbar, False) diff --git a/sugar/env.py b/sugar/env.py index e30daa2d..3b812d32 100644 --- a/sugar/env.py +++ b/sugar/env.py @@ -1,9 +1,7 @@ -import os - try: - from sugar.__uninstalled__ import * + from sugar.__uninstalled__ import internal_get_data_file except ImportError: - from sugar.__installed__ import * + from sugar.__installed__ import internal_get_data_file def get_data_file(filename): return internal_get_data_file(filename) diff --git a/sugar/p2p/Buddy.py b/sugar/p2p/Buddy.py index 19d7c0ef..8263a8e3 100644 --- a/sugar/p2p/Buddy.py +++ b/sugar/p2p/Buddy.py @@ -1,7 +1,7 @@ import pwd import os -from Service import * +from Service import Service PRESENCE_SERVICE_TYPE = "_olpc_presence._tcp" PRESENCE_SERVICE_PORT = 6000 diff --git a/sugar/p2p/Group.py b/sugar/p2p/Group.py index dedbc1ed..faa6041c 100644 --- a/sugar/p2p/Group.py +++ b/sugar/p2p/Group.py @@ -1,21 +1,26 @@ -import avahi - +from Buddy import Buddy +from Buddy import Owner +from Buddy import PRESENCE_SERVICE_TYPE +from Service import Service +from sugar.p2p.model.Store import Store import presence -from Buddy import * -from Service import * - -SERVICE_ADDED = "service_added" -SERVICE_REMOVED = "service_removed" - -BUDDY_JOIN = "buddy_join" -BUDDY_LEAVE = "buddy_leave" class Group: + SERVICE_ADDED = "service_added" + SERVICE_REMOVED = "service_removed" + + BUDDY_JOIN = "buddy_join" + BUDDY_LEAVE = "buddy_leave" + def __init__(self): self._service_listeners = [] self._presence_listeners = [] + self._store = Store(self) - def join(self, buddy): + def get_store(self): + return self._store + + def join(self): pass def add_service_listener(self, listener): @@ -26,19 +31,19 @@ class Group: def _notify_service_added(self, service): for listener in self._service_listeners: - listener(SERVICE_ADDED, buddy) + listener(Group.SERVICE_ADDED, service) - def _notify_service_removed(self, service): + def _notify_service_removed(self, service_id): for listener in self._service_listeners: - listener(SERVICE_REMOVED,buddy) + listener(Group.SERVICE_REMOVED, service_id) def _notify_buddy_join(self, buddy): for listener in self._presence_listeners: - listener(BUDDY_JOIN, buddy) + listener(Group.BUDDY_JOIN, buddy) def _notify_buddy_leave(self, buddy): for listener in self._presence_listeners: - listener(BUDDY_LEAVE, buddy) + listener(Group.BUDDY_LEAVE, buddy) class LocalGroup(Group): def __init__(self): @@ -59,16 +64,19 @@ class LocalGroup(Group): self._services[sid] = service self._notify_service_added(service) - def remove_service(self, sid): - self._notify_service_removed(service) - del self._services[sid] + def remove_service(self, service_id): + self._notify_service_removed(service_id) + del self._services[service_id] def join(self): self._owner = Owner(self) self._owner.register() def get_service(self, name, stype): - return self._services[(name, stype)] + if self._services.has_key((name, stype)): + return self._services[(name, stype)] + else: + return None def get_buddy(self, name): return self._buddies[name] @@ -95,8 +103,8 @@ class LocalGroup(Group): def _on_service_resolved(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): - service = Service(name, stype, address, port) - if stype == PRESENCE_SERVICE_TYPE: - self._add_buddy(Buddy(service, name)) - elif stype.startswith("_olpc"): - self.add_service(service) + service = Service(name, stype, address, port) + if stype == PRESENCE_SERVICE_TYPE: + self._add_buddy(Buddy(service, name)) + elif stype.startswith("_olpc"): + self.add_service(service) diff --git a/sugar/p2p/NotificationListener.py b/sugar/p2p/NotificationListener.py new file mode 100644 index 00000000..e490f390 --- /dev/null +++ b/sugar/p2p/NotificationListener.py @@ -0,0 +1,27 @@ +from Service import Service +import network + +class NotificationListener: + TYPE = "_olpc_model_notification._udp" + ADDRESS = "224.0.0.222" + PORT = 6300 + + def __init__(self, group, name): + server = network.GroupServer(NotificationListener.TYPE, + NotificationListener.PORT, + self._recv_multicast) + server.start() + + service = Service(name, NotificationListener.TYPE, + NotificationListener.ADDRESS, + NotificationListener.PORT, True) + service.register(group) + + self._listeners = {} + + def add_listener(self, listener): + self._listeners.add(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 new file mode 100644 index 00000000..017183c9 --- /dev/null +++ b/sugar/p2p/Notifier.py @@ -0,0 +1,11 @@ +import network + +class Notifier: + def __init__(self, group, name): + service = group.get_service(name) + 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/StreamReader.py b/sugar/p2p/StreamReader.py index c108547b..d3dbf83e 100644 --- a/sugar/p2p/StreamReader.py +++ b/sugar/p2p/StreamReader.py @@ -1,3 +1,5 @@ +import socket + import network class StreamReaderRequestHandler(object): @@ -5,7 +7,6 @@ class StreamReaderRequestHandler(object): self._reader = reader def message(self, nick_name, message): - address = network.get_authinfo() self._reader.recv(nick_name, message) return True @@ -37,7 +38,7 @@ class StreamReader: p2p_server = network.GlibXMLRPCServer(("", port)) p2p_server.register_instance(StreamReaderRequestHandler(self)) started = True - except: + except(socket.error): port = port + 1 tries = tries - 1 self._service.set_port(port) diff --git a/sugar/p2p/StreamWriter.py b/sugar/p2p/StreamWriter.py index f30801ea..6007a934 100644 --- a/sugar/p2p/StreamWriter.py +++ b/sugar/p2p/StreamWriter.py @@ -31,7 +31,7 @@ class StreamWriter: nick_name = self._group.get_owner().get_nick_name() self._uclient.message(nick_name, data) return True - except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError), e: + except (socket.error, xmlrpclib.Fault, xmlrpclib.ProtocolError): traceback.print_exc() return False diff --git a/sugar/p2p/model/AbstractModel.py b/sugar/p2p/model/AbstractModel.py new file mode 100644 index 00000000..a5fbe493 --- /dev/null +++ b/sugar/p2p/model/AbstractModel.py @@ -0,0 +1,10 @@ +class AbstractModel: + def __init__(self): + self._listeners = [] + + def add_listener(self, listener): + self._listeners.append(listener) + + def _notify_model_change(self, key): + for listener in self._listeners: + listener(self, key) diff --git a/sugar/p2p/model/LocalModel.py b/sugar/p2p/model/LocalModel.py new file mode 100644 index 00000000..89171924 --- /dev/null +++ b/sugar/p2p/model/LocalModel.py @@ -0,0 +1,55 @@ +import socket + +from sugar.p2p.Service import Service +from sugar.p2p.model.AbstractModel import AbstractModel +from sugar.p2p import network + +class ModelRequestHandler(object): + def __init__(self, model): + self._model = model + + def get_value(self, key): + return self._model.get_value(key) + + def set_value(self, key, value): + return self._model.set_value(key, value) + +class LocalModel(AbstractModel): + SERVICE_TYPE = "_olpc_model._tcp" + SERVICE_PORT = 6300 + + def __init__(self, group, model_id): + AbstractModel.__init__(self) + self._group = group + self._model_id = model_id + self._values = {} + + self._setup_service() + + def get_value(self, key): + return self._values[key] + + def set_value(self, key, value): + self._values[key] = value + self._notify_model_change(key) + + def _setup_service(self): + service = Service(self._model_id, LocalModel.SERVICE_TYPE, '', + LocalModel.SERVICE_PORT) + self._setup_server(service) + service.register(self._group) + + # FIXME this is duplicated with StreamReader + def _setup_server(self, service): + started = False + tries = 10 + port = service.get_port() + while not started and tries > 0: + try: + p2p_server = network.GlibXMLRPCServer(("", port)) + p2p_server.register_instance(ModelRequestHandler(self)) + started = True + except(socket.error): + port = port + 1 + tries = tries - 1 + service.set_port(port) diff --git a/sugar/p2p/model/Makefile.am b/sugar/p2p/model/Makefile.am new file mode 100644 index 00000000..c828a086 --- /dev/null +++ b/sugar/p2p/model/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/sugar/p2p/model +sugar_PYTHON = \ + __init__.py \ + LocalModel.py \ + RemoteModel.py \ + Store.py diff --git a/sugar/p2p/model/RemoteModel.py b/sugar/p2p/model/RemoteModel.py new file mode 100644 index 00000000..b966b435 --- /dev/null +++ b/sugar/p2p/model/RemoteModel.py @@ -0,0 +1,26 @@ +import xmlrpclib + +from sugar.p2p.NotificationListener import NotificationListener +from sugar.p2p.model.AbstractModel import AbstractModel + +class RemoteModel(AbstractModel): + def __init__(self, service): + AbstractModel.__init__(self) + + self._service = service + + addr = "http://%s:%d" % (service.get_address(), service.get_port()) + self._client = xmlrpclib.ServerProxy(addr) + + self._setup_notification_listener() + + def get_value(self, key): + return self._client.get_value(key) + + def set_value(self, key, value): + self._client.set_value(key, value) + + def _setup_notification_listener(self): + name = self._service.get_name() + self._notification = NotificationListener(self._group, name) + self._notification.add_listener(self._notify_model_change) diff --git a/sugar/p2p/model/Store.py b/sugar/p2p/model/Store.py new file mode 100644 index 00000000..d39783f4 --- /dev/null +++ b/sugar/p2p/model/Store.py @@ -0,0 +1,22 @@ +from sugar.p2p.model.RemoteModel import RemoteModel +from sugar.p2p.model.LocalModel import LocalModel + +class Store: + def __init__(self, group): + self._group = group + self._local_models = {} + + def create_model(self, model_id): + model = LocalModel(self._group, model_id) + self._local_models[model_id] = model + return model + + def get_model(self, model_id): + if self._local_models.has_key(model_id): + return self._local_models(model_id) + else: + service = self._group.get_service(model_id, LocalModel.SERVICE_TYPE) + if service: + return RemoteModel(service) + else: + return None diff --git a/sugar/p2p/network.py b/sugar/p2p/network.py index c88ede6c..4c054fea 100644 --- a/sugar/p2p/network.py +++ b/sugar/p2p/network.py @@ -1,10 +1,6 @@ -# -*- tab-width: 4; indent-tabs-mode: t -*- - import socket import threading import traceback -import select -import time import xmlrpclib import sys @@ -133,10 +129,6 @@ class GroupServer(object): # Set some options to make it multicast-friendly self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - try: - self._listen_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except: - pass self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 20) self._listen_sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) diff --git a/sugar/p2p/presence.py b/sugar/p2p/presence.py index e16fc928..4c5c086f 100644 --- a/sugar/p2p/presence.py +++ b/sugar/p2p/presence.py @@ -65,7 +65,7 @@ class PresenceDiscovery(object): self._service_type_browsers[(interface, protocol, domain)] = b - def new_domain(self,interface, protocol, domain, flags): + def new_domain(self, interface, protocol, domain, flags): if domain != "local": return self.browse_domain(interface, protocol, domain) @@ -84,7 +84,7 @@ class PresenceAnnounce(object): self._hostname = "%s:%s" % (self.server.GetHostName(), rs_port) rs_name = self._hostname - info = ["%s=%s" % (k,v) for k,v in kwargs.items()] + info = ["%s=%s" % (k, v) for k, v in kwargs.items()] g.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, 0, rs_name, rs_service, "", "", # domain, host (let the system figure it out) dbus.UInt16(rs_port), info,) diff --git a/sugar/shell/activity.py b/sugar/shell/activity.py index 6f1f3fc1..4678a9c0 100644 --- a/sugar/shell/activity.py +++ b/sugar/shell/activity.py @@ -1,15 +1,11 @@ # -*- tab-width: 4; indent-tabs-mode: t -*- -import string - -import gc import dbus import dbus.service import dbus.glib -import gobject import pygtk pygtk.require('2.0') -import gtk,sys +import gtk class Activity(dbus.service.Object): @@ -36,21 +32,21 @@ class Activity(dbus.service.Object): "com.redhat.Sugar.Shell.ActivityContainer") self.__activity_id = self.__activity_container.add_activity("") - self.__object_path = "/com/redhat/Sugar/Shell/Activities/%d"%self.__activity_id + self.__object_path = "/com/redhat/Sugar/Shell/Activities/%d" % self.__activity_id - print "object_path = %s"%self.__object_path + print "object_path = %s" % self.__object_path self.__activity_object = dbus.Interface(self.__bus.get_object("com.redhat.Sugar.Shell", self.__object_path), \ "com.redhat.Sugar.Shell.ActivityHost") self.__window_id = self.__activity_object.get_host_xembed_id() - print "XEMBED window_id = %d"%self.__window_id + print "XEMBED window_id = %d" % self.__window_id self.__plug = gtk.Plug(self.__window_id) # Now let the Activity register a peer service so the Shell can poke it - self.__peer_service_name = "com.redhat.Sugar.Activity%d"%self.__activity_id - self.__peer_object_name = "/com/redhat/Sugar/Activity/%d"%self.__activity_id + self.__peer_service_name = "com.redhat.Sugar.Activity%d" % self.__activity_id + self.__peer_object_name = "/com/redhat/Sugar/Activity/%d" % self.__activity_id self.__service = dbus.service.BusName(self.__peer_service_name, bus=self.__bus) dbus.service.Object.__init__(self, self.__service, self.__peer_object_name) @@ -86,7 +82,7 @@ class Activity(dbus.service.Object): pixarray = [] pixstr = pixbuf.get_pixels(); for c in pixstr: - pixarray.append(c) + pixarray.append(c) self.__activity_object.set_tab_icon(pixarray, \ pixbuf.get_colorspace(), \ pixbuf.get_has_alpha(), \ @@ -163,16 +159,16 @@ class Activity(dbus.service.Object): # pure virtual methods def activity_on_connected_to_shell(self): - print "act %d: you need to override activity_on_connected_to_shell"%self.activity_get_id() + print "act %d: you need to override activity_on_connected_to_shell" % self.activity_get_id() def activity_on_disconnected_from_shell(self): - print "act %d: you need to override activity_on_disconnected_from_shell"%self.activity_get_id() - + print "act %d: you need to override activity_on_disconnected_from_shell" % self.activity_get_id() + def activity_on_close_from_user(self): - print "act %d: you need to override activity_on_close_from_user"%self.activity_get_id() + print "act %d: you need to override activity_on_close_from_user" % self.activity_get_id() def activity_on_lost_focus(self): - print "act %d: you need to override activity_on_lost_focus"%self.activity_get_id() + print "act %d: you need to override activity_on_lost_focus" % self.activity_get_id() def activity_on_got_focus(self): - print "act %d: you need to override activity_on_got_focus"%self.activity_get_id() + print "act %d: you need to override activity_on_got_focus" % self.activity_get_id() diff --git a/sugar/shell/shell.py b/sugar/shell/shell.py index d5423079..0dd3ea95 100755 --- a/sugar/shell/shell.py +++ b/sugar/shell/shell.py @@ -1,12 +1,9 @@ #!/usr/bin/python # -*- tab-width: 4; indent-tabs-mode: t -*- -import string - import dbus import dbus.service import dbus.glib -import gobject import pygtk pygtk.require('2.0') import gtk @@ -27,7 +24,7 @@ class ActivityHost(dbus.service.Object): self.activity_id = activity_counter activity_counter += 1 - self.dbus_object_name = "/com/redhat/Sugar/Shell/Activities/%d"%self.activity_id + self.dbus_object_name = "/com/redhat/Sugar/Shell/Activities/%d" % self.activity_id #print "object name = %s"%self.dbus_object_name dbus.service.Object.__init__(self, activity_container.service, self.dbus_object_name) @@ -143,7 +140,7 @@ class ActivityHost(dbus.service.Object): #print " data = ", data pixstr = "" for c in data: - pixstr += chr(c) + pixstr += chr(c) pixbuf = gtk.gdk.pixbuf_new_from_data(pixstr, colorspace, has_alpha, bits_per_sample, width, height, rowstride) #print pixbuf @@ -225,10 +222,11 @@ class ActivityContainer(dbus.service.Object): self.window.add(self.notebook) self.window.connect("destroy", lambda w: gtk.main_quit()) - self.window.show() self.current_activity = None + def show(self): + self.window.show() def __focus_reply_cb(self): pass @@ -283,10 +281,10 @@ class ActivityContainer(dbus.service.Object): return activity.get_host_activity_id() def __print_activities(self): - print "__print_activities: %d activities registered"%len(self.activities) + print "__print_activities: %d activities registered" % len(self.activities) i = 0 for owner, activity in self.activities: - print " %d: owner=%s activity_object_name=%s"%(i, owner, activity.dbus_object_name) + print " %d: owner=%s activity_object_name=%s" % (i, owner, activity.dbus_object_name) i += 1 @@ -295,11 +293,9 @@ def main(): service = dbus.service.BusName("com.redhat.Sugar.Shell", bus=session_bus) activityContainer = ActivityContainer(service, session_bus) + activityContainer.show() - try: - gtk.main() - except KeyboardInterrupt: - pass + gtk.main() -if __name__=="__main__": - main() +if __name__ == "__main__": + main()