def _new_tube_cb(self, id, initiator, type, service, params, state): logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) self.tube = SugarTubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) super(CollaborationWrapper, self).__init__(self.tube, PATH) self.tube.watch_participants(self.participant_change_cb)
def _new_tube_cb(self, id, initiator, type, service, params, state): _logger.debug( 'New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == IFACE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube( id) self._tube_conn = SugarTubeConnection( self._connection, self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self._text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) self._tube_conn.add_signal_receiver(self._send_message_cb, 'SendMessage', sender_keyword='sender') self._dbus_object = ShareableObject(self._tube_conn, self._service_path)
def _new_tube_cb(self, id, initiator, type, service, params, state): logging.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == TelepathyGLib.TubeType.DBUS and service == self.service): if state == TelepathyGLib.TubeState.LOCAL_PENDING: self._tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES] \ .AcceptDBusTube(id) tube_conn = SugarTubeConnection(self._conn, self._tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES], id, group_iface=self._text_chan[TelepathyGLib.IFACE_CHANNEL_INTERFACE_GROUP]) self._share(tube_conn, self.__initiator)
def _new_tube_cb(self, id, initiator, type, service, params, state): _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == IFACE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self._tubes_chan[ telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) self._tube_conn = SugarTubeConnection( self._connection, self._tubes_chan[ telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self._text_chan[ telepathy.CHANNEL_INTERFACE_GROUP]) self._tube_conn.add_signal_receiver( self._send_message_cb, 'SendMessage', sender_keyword='sender') self._dbus_object = ShareableObject(self._tube_conn, self._service_path)
class CollaborationWrapper(ExportedGObject): ''' A wrapper for the collaboration bureaucracy''' def __init__(self, activity, buddy_joined_cb, buddy_left_cb, play_cb, undostack, bootstrap): self.activity = activity self.buddy_joined = buddy_joined_cb self.buddy_left = buddy_left_cb self.Play_cb = play_cb self.undostack = undostack self.bootstrap = bootstrap self.world = False self.entered = False self.presence_service = presenceservice.get_instance() self.owner = self.presence_service.get_owner() def _shared_cb(self, activity): self.activity.gameToolbar.grey_out_size_change() self.activity.gameToolbar.grey_out_restart() self.activity.gameToolbar.grey_out_ai() self.activity.undo_button.hide() self.activity.board.set_sensitive(False) self._sharing_setup() self.tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) self.is_initiator = True def _joined_cb(self, activity): self.activity.gameToolbar.grey_out_size_change() self.activity.gameToolbar.grey_out_restart() self.activity.gameToolbar.grey_out_ai() self.activity.undo_button.hide() self.activity.board.set_sensitive(False) self._sharing_setup() self.tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) self.is_initiator = False self.activity.board.set_sensitive(True) def _sharing_setup(self): if self.activity._shared_activity is None: logger.error('Failed to share or join activity') return self.conn = self.activity._shared_activity.telepathy_conn self.tubes_chan = self.activity._shared_activity.telepathy_tubes_chan self.text_chan = self.activity._shared_activity.telepathy_text_chan self.tubes_chan[ TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) self.activity._shared_activity.connect( 'buddy-joined', self._buddy_joined_cb) self.activity._shared_activity.connect( 'buddy-left', self._buddy_left_cb) # Optional - included for example: # Find out who's already in the shared activity: for buddy in self.activity._shared_activity.get_joined_buddies(): logger.debug( 'Buddy %s is already in the activity', buddy.props.nick) def participant_change_cb(self, added, removed): logger.debug('Tube: Added participants: %r', added) logger.debug('Tube: Removed participants: %r', removed) for handle, bus_name in added: buddy = self._get_buddy(handle) if buddy is not None: logger.debug( 'Tube: Handle %u (Buddy %s) was added', handle, buddy.props.nick) for handle in removed: buddy = self._get_buddy(handle) if buddy is not None: logger.debug('Buddy %s was removed' % buddy.props.nick) if not self.entered: if self.is_initiator: logger.debug( "I'm initiating the tube, will watch for hellos.") self.add_hello_handler() else: logger.debug('Hello, everyone! What did I miss?') self.Hello() self.entered = True # This is sent to all participants whenever we join an activity @signal(dbus_interface=IFACE, signature='') def Hello(self): """Say Hello to whoever else is in the tube.""" logger.debug('I said Hello.') # This is called by whoever receives our Hello signal # This method receives the current game state and puts us in sync # with the rest of the participants. # The current game state is represented by the game object @method(dbus_interface=IFACE, in_signature='a(ii)si', out_signature='') def World(self, undostack, taken_color, size): """To be called on the incoming XO after they Hello.""" if not self.world: logger.debug( 'Somebody called World and sent me undostack: %s', undostack) self.activity.board_size_change(None, size) self.bootstrap(list(undostack)) self.activity.set_player_color( self.activity.invert_color(taken_color)) # self.players = players # now I can World others self.add_hello_handler() else: self.world = True logger.debug("I've already been welcomed, doing nothing") @signal(dbus_interface=IFACE, signature='ii') def Play(self, x, y): """Say Hello to whoever else is in the tube.""" logger.debug('Signaling players of stone placement at:%s x %s.', x, y) def add_hello_handler(self): logger.debug('Adding hello handler.') self.tube.add_signal_receiver( self.hello_signal_cb, 'Hello', IFACE, path=PATH, sender_keyword='sender') self.tube.add_signal_receiver( self.play_signal_cb, 'Play', IFACE, path=PATH, sender_keyword='sender') def hello_signal_cb(self, sender=None): """Somebody Helloed me. World them.""" if sender == self.tube.get_unique_name(): # sender is my bus name, so ignore my own signal return logger.debug('Newcomer %s has joined', sender) logger.debug('Welcoming newcomer and sending them the game state') # Strip the undostack to reduce net traffic =) strippedstack = [] for pos, color, captures in self.undostack: strippedstack.append(pos) # FIXME: A spectator needs to send the color # that was taken, not its own self.tube.get_object(sender, PATH).World( strippedstack, self.activity.get_playercolor(), self.activity.size, dbus_interface=IFACE) self.activity.board.set_sensitive(True) def play_signal_cb(self, x, y, sender=None): """Somebody placed a stone. """ if sender == self.tube.get_unique_name(): # sender is my bus name, so ignore my own signal return logger.debug('Buddy %s placed a stone at %s x %s', sender, x, y) # Call our Play callback self.Play_cb(x, y, sender) def _list_tubes_error_cb(self, e): logger.error('ListTubes() failed: %s', e) def _list_tubes_reply_cb(self, tubes): for tube_info in tubes: self._new_tube_cb(*tube_info) def _new_tube_cb(self, id, initiator, type, service, params, state): logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == TelepathyGLib.TubeType.DBUS and service == SERVICE): if state == TelepathyGLib.TubeState.LOCAL_PENDING: self.tubes_chan[ TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].AcceptDBusTube(id) self.tube = SugarTubeConnection( self.conn, self.tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[ TelepathyGLib.IFACE_CHANNEL_INTERFACE_GROUP]) super(CollaborationWrapper, self).__init__(self.tube, PATH) self.tube.watch_participants(self.participant_change_cb) def _buddy_joined_cb(self, activity, buddy): """Called when a buddy joins the shared activity. """ logger.debug('Buddy %s joined', buddy.props.nick) self.buddy_joined(buddy) def _buddy_left_cb(self, activity, buddy): """Called when a buddy leaves the shared activity. """ self.buddy_left(buddy) def _get_buddy(self, cs_handle): """Get a Buddy from a channel specific handle.""" logger.debug('Trying to find owner of handle %u...', cs_handle) group = self.text_chan[TelepathyGLib.IFACE_CHANNEL_INTERFACE_GROUP] my_csh = group.GetSelfHandle() logger.debug('My handle in that group is %u', my_csh) if my_csh == cs_handle: handle = self.conn.GetSelfHandle() logger.debug('CS handle %u belongs to me, %u', cs_handle, handle) elif group.GetGroupFlags() & \ TelepathyGLib.ChannelGroupFlags.CHANNEL_SPECIFIC_HANDLES: handle = group.GetHandleOwners([cs_handle])[0] logger.debug('CS handle %u belongs to %u', cs_handle, handle) else: handle = cs_handle logger.debug('non-CS handle %u belongs to itself', handle) # XXX: deal with failure to get the handle owner assert handle != 0 return self.presence_service.get_buddy_by_telepathy_handle( self.conn.service_name, self.conn.object_path, handle)
class ShareableActivity(activity.Activity): ''' A shareable activity. Signals to connect to for more notifications: self.get_shared_activity().connect('buddy-joined', ...) self.get_shared_activity().connect('buddy-left', ...) ''' def __init__(self, handle, *args, **kwargs): ''' Initialize the ShareableActivity class. Kwargs: service_path ''' activity.Activity.__init__(self, handle, *args, **kwargs) self._sync_hid = None self._message_cbs = {} self._connection = None self._tube_conn = None self._pservice = presenceservice.get_instance() self._owner = self._pservice.get_owner() self._owner_id = str(self._owner.props.nick) self._service_path = kwargs.get('service_path', self._generate_service_path()) self._dbus_object = None _logger.debug('Setting service name %s, service path %s', IFACE, self._service_path) self._connect_to_ps() def get_shared_activity(self): '''Get shared_activity object; works for different API versions.''' try: return self.shared_activity except: return self._shared_activity def get_owner(self): '''Return buddy object of the owner.''' return self._owner def get_owner_id(self): '''Return id (nickname) of the owner.''' return self._owner_id def get_bus_name(self): ''' Return the DBus bus name for the tube we're using, or None if there is no tube yet. ''' if self._tube_conn is not None: return self._tube_conn.get_unique_name() else: return None def _generate_service_path(self): bundle_id = self.get_bundle_id() last = bundle_id.split('.')[-1] instance_id = self.get_id() return '/org/laptop/ShareableActivity/%s/%s' % (last, instance_id) def _connect_to_ps(self): ''' Connect to the presence service. ''' if self.get_shared_activity(): self.connect('joined', self._sa_joined_cb) if self.get_shared(): self._sa_joined_cb() else: self.connect('shared', self._sa_shared_cb) def _setup_shared_activity(self): ''' Setup sharing stuff: get channels etc. ''' sa = self.get_shared_activity() if sa is None: _logger.error('_setup_shared_activity(): no shared_activity yet!') return False self._connection = sa.telepathy_conn self._tubes_chan = sa.telepathy_tubes_chan self._text_chan = sa.telepathy_text_chan self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) def _sa_shared_cb(self, activity): self._setup_shared_activity() self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(IFACE, {}) def _sa_joined_cb(self, activity): """Callback for when we join an existing activity.""" _logger.info('Joined existing activity') self._request_sync = True self._setup_shared_activity() self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) def _list_tubes_reply_cb(self, tubes): """Callback for when requesting an existing tube""" _logger.debug('_list_tubes_reply_cb(): %r', tubes) for tube_info in tubes: self._new_tube_cb(*tube_info) def _list_tubes_error_cb(self, e): _logger.error('ListTubes() failed: %s', e) def _new_tube_cb(self, id, initiator, type, service, params, state): _logger.debug( 'New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == IFACE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube( id) self._tube_conn = SugarTubeConnection( self._connection, self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self._text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) self._tube_conn.add_signal_receiver(self._send_message_cb, 'SendMessage', sender_keyword='sender') self._dbus_object = ShareableObject(self._tube_conn, self._service_path) def buddy_joined(self, activity, buddy): ''' Override to take action when a buddy joins. ''' _logger.debug('Buddy joined: %s', buddy) def buddy_left(self, activity, buddy): ''' Override to take action when a buddy left. ''' _logger.debug('Buddy left: %s', buddy) def connect_message(self, msg, func): ''' Connect function 'func' so that it's called when message <msg> is received. The function will receive keyword arguments sent with the message. ''' self._message_cbs[msg] = func def message_received(self, msg, **kwargs): ''' Override to take action when a message is received. This function will not be called for message handlers already registered with connect_message(). ''' _logger.debug('Received message: %s(%r)', msg, kwargs) def send_message(self, msg, **kwargs): ''' Send a message to all connected buddies. ''' if self._dbus_object is not None: _logger.debug('Sending message: %s(%r)', msg, kwargs) self._dbus_object.SendMessage(msg, kwargs) else: _logger.debug('Not shared, not sending message %s(%r)', msg, kwargs) def send_message_to(self, buddy, msg, **kwargs): ''' Send a message to one particular buddy. ''' if self._dbus_object is not None: _logger.debug('Sending message to %s: %s(%r)', buddy, msg, kwargs) # FIXME: convert to busname self._dbus_object.SendMessageTo(buddy, msg, kwargs) else: _logger.debug('Not shared, not sending message %s(%r) to %s', msg, kwargs, buddy) def _dispatch_message(self, msg, kwargs): passkwargs = {} for k, v in kwargs.iteritems(): passkwargs[str(k)] = v if msg in self._message_cbs: func = self._message_cbs[msg] func(**passkwargs) else: self.message_received(msg, **passkwargs) def _send_message_cb(self, msg, kwargs, sender=None): '''Callback to filter message signals.''' _logger.debug('Sender: %s, owner: %s, owner_id: %s, busname: %s', sender, self.get_owner(), self.get_owner_id(), self.get_bus_name()) if sender == self.get_bus_name(): return kwargs['sender'] = sender self._dispatch_message(msg, kwargs) def _send_message_to_cb(self, to, msg, kwargs, sender=None): '''Callback to filter message signals.''' if to != self.get_bus_name(): return kwargs['sender'] = sender kwargs['to'] = to self._dispatch_message(msg, kwargs) # FIXME: build a standard system to sync state from a single buddy def request_sync(self): if self._sync_hid is not None: return self._syncreq_buddy = 0 self._sync_hid = GObject.timeout_add(2000, self._request_sync_cb) self._request_sync_cb() def _request_sync_cb(self): if self._syncreq_buddy <= len(self._connected_buddies): self._sync_hid = None return False self._syncreq_buddy += 1
class CollaborationWrapper(ExportedGObject): ''' A wrapper for the collaboration bureaucracy''' def __init__(self, activity, buddy_joined_cb, buddy_left_cb, play_cb, undostack, bootstrap): self.activity = activity self.buddy_joined = buddy_joined_cb self.buddy_left = buddy_left_cb self.Play_cb = play_cb self.undostack = undostack self.bootstrap = bootstrap self.world = False self.entered = False self.presence_service = presenceservice.get_instance() self.owner = self.presence_service.get_owner() def _shared_cb(self, activity): self.activity.gameToolbar.grey_out_size_change() self.activity.gameToolbar.grey_out_restart() self.activity.gameToolbar.grey_out_ai() self.activity.undo_button.hide() self.activity.board.set_sensitive(False) self._sharing_setup() self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) self.is_initiator = True def _joined_cb(self, activity): self.activity.gameToolbar.grey_out_size_change() self.activity.gameToolbar.grey_out_restart() self.activity.gameToolbar.grey_out_ai() self.activity.undo_button.hide() self.activity.board.set_sensitive(False) self._sharing_setup() self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) self.is_initiator = False self.activity.board.set_sensitive(True) def _sharing_setup(self): if self.activity._shared_activity is None: logger.error('Failed to share or join activity') return self.conn = self.activity._shared_activity.telepathy_conn self.tubes_chan = self.activity._shared_activity.telepathy_tubes_chan self.text_chan = self.activity._shared_activity.telepathy_text_chan self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) self.activity._shared_activity.connect('buddy-joined', self._buddy_joined_cb) self.activity._shared_activity.connect('buddy-left', self._buddy_left_cb) # Optional - included for example: # Find out who's already in the shared activity: for buddy in self.activity._shared_activity.get_joined_buddies(): logger.debug('Buddy %s is already in the activity', buddy.props.nick) def participant_change_cb(self, added, removed): logger.debug('Tube: Added participants: %r', added) logger.debug('Tube: Removed participants: %r', removed) for handle, bus_name in added: buddy = self._get_buddy(handle) if buddy is not None: logger.debug('Tube: Handle %u (Buddy %s) was added', handle, buddy.props.nick) for handle in removed: buddy = self._get_buddy(handle) if buddy is not None: logger.debug('Buddy %s was removed' % buddy.props.nick) if not self.entered: if self.is_initiator: logger.debug("I'm initiating the tube, will " "watch for hellos.") self.add_hello_handler() else: logger.debug('Hello, everyone! What did I miss?') self.Hello() self.entered = True # This is sent to all participants whenever we join an activity @signal(dbus_interface=IFACE, signature='') def Hello(self): """Say Hello to whoever else is in the tube.""" logger.debug('I said Hello.') # This is called by whoever receives our Hello signal # This method receives the current game state and puts us in sync # with the rest of the participants. # The current game state is represented by the game object @method(dbus_interface=IFACE, in_signature='a(ii)si', out_signature='') def World(self, undostack, taken_color, size): """To be called on the incoming XO after they Hello.""" if not self.world: logger.debug('Somebody called World and sent me undostack: %s', undostack) self.activity.board_size_change(None, size) self.bootstrap(list(undostack)) self.activity.set_player_color(self.activity.invert_color(taken_color)) #self.players = players # now I can World others self.add_hello_handler() else: self.world = True logger.debug("I've already been welcomed, doing nothing") @signal(dbus_interface=IFACE, signature='ii') def Play(self, x, y): """Say Hello to whoever else is in the tube.""" logger.debug('Signaling players of stone placement at:%s x %s.', x, y) def add_hello_handler(self): logger.debug('Adding hello handler.') self.tube.add_signal_receiver(self.hello_signal_cb, 'Hello', IFACE, path=PATH, sender_keyword='sender') self.tube.add_signal_receiver(self.play_signal_cb, 'Play', IFACE, path=PATH, sender_keyword='sender') def hello_signal_cb(self, sender=None): """Somebody Helloed me. World them.""" if sender == self.tube.get_unique_name(): # sender is my bus name, so ignore my own signal return logger.debug('Newcomer %s has joined', sender) logger.debug('Welcoming newcomer and sending them the game state') # Strip the undostack to reduce net traffic =) strippedstack = [] for pos, color, captures in self.undostack: strippedstack.append(pos) # FIXME: A spectator needs to send the color that was taken, not its own self.tube.get_object(sender, PATH).World(strippedstack, self.activity.get_playercolor(), self.activity.size, dbus_interface=IFACE) self.activity.board.set_sensitive(True) def play_signal_cb(self, x, y, sender=None): """Somebody placed a stone. """ if sender == self.tube.get_unique_name(): # sender is my bus name, so ignore my own signal return logger.debug('Buddy %s placed a stone at %s x %s', sender, x, y) # Call our Play callback self.Play_cb(x, y, sender) def _list_tubes_error_cb(self, e): logger.error('ListTubes() failed: %s', e) def _list_tubes_reply_cb(self, tubes): for tube_info in tubes: self._new_tube_cb(*tube_info) def _new_tube_cb(self, id, initiator, type, service, params, state): logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) self.tube = SugarTubeConnection(self.conn, self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) super(CollaborationWrapper, self).__init__(self.tube, PATH) self.tube.watch_participants(self.participant_change_cb) def _buddy_joined_cb (self, activity, buddy): """Called when a buddy joins the shared activity. """ logger.debug('Buddy %s joined', buddy.props.nick) self.buddy_joined(buddy) def _buddy_left_cb (self, activity, buddy): """Called when a buddy leaves the shared activity. """ self.buddy_left(buddy) def _get_buddy(self, cs_handle): """Get a Buddy from a channel specific handle.""" logger.debug('Trying to find owner of handle %u...', cs_handle) group = self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP] my_csh = group.GetSelfHandle() logger.debug('My handle in that group is %u', my_csh) if my_csh == cs_handle: handle = self.conn.GetSelfHandle() logger.debug('CS handle %u belongs to me, %u', cs_handle, handle) elif group.GetGroupFlags() & telepathy.CHANNEL_GROUP_FLAG_CHANNEL_SPECIFIC_HANDLES: handle = group.GetHandleOwners([cs_handle])[0] logger.debug('CS handle %u belongs to %u', cs_handle, handle) else: handle = cs_handle logger.debug('non-CS handle %u belongs to itself', handle) # XXX: deal with failure to get the handle owner assert handle != 0 return self.presence_service.get_buddy_by_telepathy_handle( self.conn.service_name, self.conn.object_path, handle)
class ShareableActivity(activity.Activity): ''' A shareable activity. Signals to connect to for more notifications: self.get_shared_activity().connect('buddy-joined', ...) self.get_shared_activity().connect('buddy-left', ...) ''' def __init__(self, handle, *args, **kwargs): ''' Initialize the ShareableActivity class. Kwargs: service_path ''' activity.Activity.__init__(self, handle, *args, **kwargs) self._sync_hid = None self._message_cbs = {} self._connection = None self._tube_conn = None self._pservice = presenceservice.get_instance() self._owner = self._pservice.get_owner() self._owner_id = str(self._owner.props.nick) self._service_path = kwargs.get('service_path', self._generate_service_path()) self._dbus_object = None _logger.debug('Setting service name %s, service path %s', IFACE, self._service_path) self._connect_to_ps() def get_shared_activity(self): '''Get shared_activity object; works for different API versions.''' try: return self.shared_activity except: return self._shared_activity def get_owner(self): '''Return buddy object of the owner.''' return self._owner def get_owner_id(self): '''Return id (nickname) of the owner.''' return self._owner_id def get_bus_name(self): ''' Return the DBus bus name for the tube we're using, or None if there is no tube yet. ''' if self._tube_conn is not None: return self._tube_conn.get_unique_name() else: return None def _generate_service_path(self): bundle_id = self.get_bundle_id() last = bundle_id.split('.')[-1] instance_id = self.get_id() return '/org/laptop/ShareableActivity/%s/%s' % (last, instance_id) def _connect_to_ps(self): ''' Connect to the presence service. ''' if self.get_shared_activity(): self.connect('joined', self._sa_joined_cb) if self.get_shared(): self._sa_joined_cb() else: self.connect('shared', self._sa_shared_cb) def _setup_shared_activity(self): ''' Setup sharing stuff: get channels etc. ''' sa = self.get_shared_activity() if sa is None: _logger.error('_setup_shared_activity(): no shared_activity yet!') return False self._connection = sa.telepathy_conn self._tubes_chan = sa.telepathy_tubes_chan self._text_chan = sa.telepathy_text_chan self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( 'NewTube', self._new_tube_cb) def _sa_shared_cb(self, activity): self._setup_shared_activity() self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( IFACE, {}) def _sa_joined_cb(self, activity): """Callback for when we join an existing activity.""" _logger.info('Joined existing activity') self._request_sync = True self._setup_shared_activity() self._tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) def _list_tubes_reply_cb(self, tubes): """Callback for when requesting an existing tube""" _logger.debug('_list_tubes_reply_cb(): %r', tubes) for tube_info in tubes: self._new_tube_cb(*tube_info) def _list_tubes_error_cb(self, e): _logger.error('ListTubes() failed: %s', e) def _new_tube_cb(self, id, initiator, type, service, params, state): _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if (type == telepathy.TUBE_TYPE_DBUS and service == IFACE): if state == telepathy.TUBE_STATE_LOCAL_PENDING: self._tubes_chan[ telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) self._tube_conn = SugarTubeConnection( self._connection, self._tubes_chan[ telepathy.CHANNEL_TYPE_TUBES], id, group_iface=self._text_chan[ telepathy.CHANNEL_INTERFACE_GROUP]) self._tube_conn.add_signal_receiver( self._send_message_cb, 'SendMessage', sender_keyword='sender') self._dbus_object = ShareableObject(self._tube_conn, self._service_path) def buddy_joined(self, activity, buddy): ''' Override to take action when a buddy joins. ''' _logger.debug('Buddy joined: %s', buddy) def buddy_left(self, activity, buddy): ''' Override to take action when a buddy left. ''' _logger.debug('Buddy left: %s', buddy) def connect_message(self, msg, func): ''' Connect function 'func' so that it's called when message <msg> is received. The function will receive keyword arguments sent with the message. ''' self._message_cbs[msg] = func def message_received(self, msg, **kwargs): ''' Override to take action when a message is received. This function will not be called for message handlers already registered with connect_message(). ''' _logger.debug('Received message: %s(%r)', msg, kwargs) def send_message(self, msg, **kwargs): ''' Send a message to all connected buddies. ''' if self._dbus_object is not None: _logger.debug('Sending message: %s(%r)', msg, kwargs) self._dbus_object.SendMessage(msg, kwargs) else: _logger.debug('Not shared, not sending message %s(%r)', msg, kwargs) def send_message_to(self, buddy, msg, **kwargs): ''' Send a message to one particular buddy. ''' if self._dbus_object is not None: _logger.debug('Sending message to %s: %s(%r)', buddy, msg, kwargs) # FIXME: convert to busname self._dbus_object.SendMessageTo(buddy, msg, kwargs) else: _logger.debug('Not shared, not sending message %s(%r) to %s', msg, kwargs, buddy) def _dispatch_message(self, msg, kwargs): passkwargs = {} for k, v in kwargs.iteritems(): passkwargs[str(k)] = v if msg in self._message_cbs: func = self._message_cbs[msg] func(**passkwargs) else: self.message_received(msg, **passkwargs) def _send_message_cb(self, msg, kwargs, sender=None): '''Callback to filter message signals.''' _logger.debug( 'Sender: %s, owner: %s, owner_id: %s, busname: %s', sender, self.get_owner(), self.get_owner_id(), self.get_bus_name()) if sender == self.get_bus_name(): return kwargs['sender'] = sender self._dispatch_message(msg, kwargs) def _send_message_to_cb(self, to, msg, kwargs, sender=None): '''Callback to filter message signals.''' if to != self.get_bus_name(): return kwargs['sender'] = sender kwargs['to'] = to self._dispatch_message(msg, kwargs) # FIXME: build a standard system to sync state from a single buddy def request_sync(self): if self._sync_hid is not None: return self._syncreq_buddy = 0 self._sync_hid = GObject.timeout_add(2000, self._request_sync_cb) self._request_sync_cb() def _request_sync_cb(self): if self._syncreq_buddy <= len(self._connected_buddies): self._sync_hid = None return False self._syncreq_buddy += 1