From 08c791849f8d50454017c25a7048def4918512a6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 2 Mar 2007 11:10:44 -0500 Subject: [PATCH] Convert buddies to GObjects; ensure signals don't emit before buddy is valid --- services/presence2/buddy.py | 171 +++++++++++++++---------- services/presence2/linklocal_plugin.py | 3 + services/presence2/presenceservice.py | 40 +++--- services/presence2/server_plugin.py | 4 +- 4 files changed, 131 insertions(+), 87 deletions(-) diff --git a/services/presence2/buddy.py b/services/presence2/buddy.py index 1f17210c..a49e1785 100644 --- a/services/presence2/buddy.py +++ b/services/presence2/buddy.py @@ -28,32 +28,91 @@ class NotFoundError(dbus.DBusException): dbus.DBusException.__init__(self) self._dbus_error_name = _PRESENCE_INTERFACE + '.NotFound' -class Buddy(dbus.service.Object): +class DBusGObjectMetaclass(gobject.GObjectMeta, dbus.service.InterfaceType): pass +class DBusGObject(dbus.service.Object, gobject.GObject): __metaclass__ = DBusGObjectMetaclass + + +class Buddy(DBusGObject): """Represents another person on the network and keeps track of the activities and resources they make available for sharing.""" - def __init__(self, bus_name, object_id, key=None): + __gtype_name__ = "Buddy" + + __gsignals__ = { + 'validity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_BOOLEAN])) + } + + __gproperties__ = { + 'key' : (str, None, None, None, + gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY), + 'icon' : (str, None, None, None, gobject.PARAM_READWRITE), + 'nick' : (str, None, None, None, gobject.PARAM_READWRITE), + 'color' : (str, None, None, None, gobject.PARAM_READWRITE), + 'current-activity' : (str, None, None, None, gobject.PARAM_READWRITE), + 'valid' : (bool, None, None, False, gobject.PARAM_READABLE), + 'owner' : (bool, None, None, False, gobject.PARAM_READABLE) + } + + def __init__(self, bus_name, object_id, **kwargs): if not bus_name: raise ValueError("DBus bus name must be valid") if not object_id or not isinstance(object_id, int): raise ValueError("object id must be a valid number") - if not key: - raise ValueError("key must be valid") self._bus_name = bus_name self._object_id = object_id self._object_path = _BUDDY_PATH + str(self._object_id) - dbus.service.Object.__init__(self, self._bus_name, self._object_path) self._activities = {} # Activity ID -> Activity self.handles = {} # tp client -> handle - self._key = key - self._icon = None - self._nick = None - self._color = None - self._current_activity = None + self._valid = False + self._owner = False + self._key = None + + gobject.GObject.__init__(self, **kwargs) + if not self._key: + raise RuntimeError("public key required") + + def do_get_property(self, pspec): + if pspec.name == "key": + return self._key + elif pspec.name == "icon": + return self._icon + elif pspec.name == "nick": + return self._nick + elif pspec.name == "color": + return self._color + elif pspec.name == "current-activity": + if not self._current_activity: + return None + if not self._activities.has_key(self._current_activity): + return None + return self._activities[self._current_activity] + elif pspec.name == "valid": + return self._valid + elif pspec.name == "owner": + return self._owner + + def do_set_property(self, pspec, value): + if pspec.name == "icon": + if value != self._icon: + self._icon = value + self.IconChanged(value) + elif pspec.name == "nick": + self._nick = value + elif pspec.name == "color": + self._color = value + elif pspec.name == "current-activity": + self._current_activity = value + elif pspec.name == "key": + if self._key: + raise RuntimeError("key already set") + self._key = value + + self._update_validity() # dbus signals @dbus.service.signal(_BUDDY_INTERFACE, @@ -80,10 +139,9 @@ class Buddy(dbus.service.Object): @dbus.service.method(_BUDDY_INTERFACE, in_signature="", out_signature="ay") def GetIcon(self): - icon = self.get_icon() - if not icon: + if not self.props.icon: return "" - return icon + return self.props.icon @dbus.service.method(_BUDDY_INTERFACE, in_signature="", out_signature="ao") @@ -97,10 +155,10 @@ class Buddy(dbus.service.Object): in_signature="", out_signature="a{sv}") def GetProperties(self): props = {} - props['nick'] = self.get_nick() - props['owner'] = self.is_owner() - props['key'] = self.get_key() - color = self.get_color() + props['nick'] = self.props.nick + props['owner'] = self.props.owner + props['key'] = self.props.key + color = self.props.color if color: props['color'] = color return props @@ -132,78 +190,53 @@ class Buddy(dbus.service.Object): acts.append(act) return acts - def get_icon(self): - """Return the buddies icon, if any.""" - return self._icon - - def get_nick(self): - return self._nick - - def get_color(self): - return self._color - - def get_current_activity(self): - if not self._current_activity: - return None - if not self._activities.has_key(self._current_activity): - return None - return self._activities[self._current_activity] - - def set_icon(self, icon): - """Can only set icon for other buddies. The Owner - takes care of setting it's own icon.""" - if icon != self._icon: - self._icon = icon - self.IconChanged(icon) - - def _set_nick(self, nick): - self._nick = nick - - def _set_color(self, color): - self._color = color - def set_properties(self, properties): if "nick" in properties.keys(): - self._set_nick(properties["nick"]) + self._nick = properties["nick"] if "color" in properties.keys(): - self._set_color(properties["color"]) - self.PropertyChanged(properties) + self._color = properties["color"] - def is_owner(self): - return False + # Try emitting PropertyChanged before updating validity + # to avoid leaking a PropertyChanged signal before the buddy is + # actually valid the first time after creation + if self._valid: + self.PropertyChanged(properties) - def set_key(self, key): - self._key = key + self._update_validity() + + def _update_validity(self): + try: + old_valid = self._valid + if self._color and self._nick and self._key: + self._valid = True + else: + self._valid = False + + if old_valid != self._valid: + self.emit("validity-changed", self._valid) + except AttributeError: + self._valid = False - def get_key(self): - return self._key class Owner(Buddy): """Class representing the owner of the machine. This is the client portion of the Owner, paired with the server portion in Owner.py.""" - def __init__(self, ps, bus_name, object_id): + def __init__(self, bus_name, object_id): key = profile.get_pubkey() - Buddy.__init__(self, bus_name, object_id, key=key) + nick = profile.get_nick_name() + color = profile.get_color().to_string() - self._ps = ps - self._name = profile.get_nick_name() - self._color = profile.get_color().to_string() + Buddy.__init__(self, bus_name, object_id, key=key, nick=nick, color=color) + self._owner = True # dbus methods @dbus.service.method(_OWNER_INTERFACE, in_signature="ay", out_signature="") def SetIcon(self, icon_data): - self.set_icon(icon_data) + self.props.icon = icon_data @dbus.service.method(_OWNER_INTERFACE, in_signature="a{sv}", out_signature="") def SetProperties(self, prop): self.set_properties(self, prop) - # methods - def is_owner(self): - return True - - def set_icon(self, icon): - self._icon = icon - diff --git a/services/presence2/linklocal_plugin.py b/services/presence2/linklocal_plugin.py index 01f2cf03..5492246c 100644 --- a/services/presence2/linklocal_plugin.py +++ b/services/presence2/linklocal_plugin.py @@ -21,3 +21,6 @@ class LinkLocalPlugin(gobject.GObject): def __init__(self, registry): gobject.GObject.__init__(self) self._registry = registry + + def cleanup(self): + pass diff --git a/services/presence2/presenceservice.py b/services/presence2/presenceservice.py index cb9ad4f0..7ffd923c 100644 --- a/services/presence2/presenceservice.py +++ b/services/presence2/presenceservice.py @@ -51,8 +51,8 @@ class PresenceService(dbus.service.Object): # Create the Owner object objid = self._get_next_object_id() - self._owner = Owner(self, self._bus_name, objid) - self._buddies[self._owner.get_key()] = self._owner + self._owner = Owner(self._bus_name, objid) + self._buddies[self._owner.props.key] = self._owner self._registry = ManagerRegistry() self._registry.LoadManagers() @@ -86,30 +86,34 @@ class PresenceService(dbus.service.Object): # we don't know yet this buddy objid = self._get_next_object_id() buddy = Buddy(self._bus_name, objid, key=key) - print "New buddy %s" % key + buddy.connect("validity-changed", self._buddy_validity_changed_cb) self._buddies[key] = buddy - new_buddy = True buddies = self._handles[tp] buddies[handle] = buddy - # store the handle of the buddy for this CM buddy.handles[tp] = handle - if new_buddy: - self.BuddyAppeared(buddy.object_path()) buddy.set_properties(props) - print "New buddy properties %s" % props - + + def _buddy_validity_changed_cb(self, buddy, valid): + if valid: + self.BuddyAppeared(buddy.object_path()) + print "New Buddy: %s (%s)" % (buddy.props.nick, buddy.props.color) + else: + self.BuddyDisappeared(buddy.object_path()) + print "Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color) + def _contact_offline(self, tp, handle): buddy = self._handles[tp].pop(handle) - key = buddy.get_key() + key = buddy.props.key # the handle of the buddy for this CM is not valid anymore buddy.handles.pop(tp) if not buddy.handles: - self.BuddyDisappeared(buddy.object_path()) - print "Buddy %s gone" % buddy.get_key() + if buddy.props.valid: + self.BuddyDisappeared(buddy.object_path()) + print "Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color) self._buddies.pop(key) def _get_next_object_id(self): @@ -119,15 +123,15 @@ class PresenceService(dbus.service.Object): def _avatar_updated(self, tp, handle, avatar): buddy = self._handles[tp].get(handle) - if buddy and not buddy.is_owner(): - print "Buddy %s icon updated" % buddy.get_key() - buddy.set_icon(avatar) + if buddy and not buddy.props.owner: + print "Buddy %s icon updated" % buddy.props.key + buddy.props.icon = avatar def _properties_changed(self, tp, handle, prop): buddy = self._handles[tp].get(handle) if buddy: buddy.set_properties(prop) - print "Buddy %s properties updated" % buddy.get_key() + print "Buddy %s properties updated" % buddy.props.key def _activities_changed(self, tp, handle, prop): pass @@ -185,6 +189,9 @@ class PresenceService(dbus.service.Object): def ShareActivity(self, actid, atype, name, properties): raise NotImplementedError("not implemented yet") + def cleanup(self): + for tp in self._handles: + tp.cleanup() def main(): loop = gobject.MainLoop() @@ -192,6 +199,7 @@ def main(): try: loop.run() except KeyboardInterrupt: + ps.cleanup() print 'Ctrl+C pressed, exiting...' if __name__ == "__main__": diff --git a/services/presence2/server_plugin.py b/services/presence2/server_plugin.py index 65d0cddc..9990c19f 100644 --- a/services/presence2/server_plugin.py +++ b/services/presence2/server_plugin.py @@ -194,7 +194,7 @@ class ServerPlugin(gobject.GObject): self._conn._valid_interfaces.add(CONN_INTERFACE_BUDDY_INFO) if CONN_INTERFACE_BUDDY_INFO not in self._conn.get_valid_interfaces(): print 'OLPC information not available' - self.disconnect() + self.cleanup() return self._conn[CONN_INTERFACE_BUDDY_INFO].connect_to_signal('PropertiesChanged', self._properties_changed_cb) @@ -267,7 +267,7 @@ class ServerPlugin(gobject.GObject): else: self._conn[CONN_INTERFACE].Connect() - def disconnect(self): + def cleanup(self): self._conn[CONN_INTERFACE].Disconnect() def _contact_offline(self, handle):