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 --- diff --git a/services/clipboard/ClipboardService.py b/services/clipboard/ClipboardService.py index 7389c58..d8a151d 100644 --- a/services/clipboard/ClipboardService.py +++ b/services/clipboard/ClipboardService.py @@ -24,53 +24,53 @@ from sugar import env class ClipboardDBusServiceHelper(dbus.service.Object): - _CLIPBOARD_DBUS_INTERFACE = "org.laptop.Clipboard" - _CLIPBOARD_OBJECT_PATH = "/org/laptop/Clipboard" + _CLIPBOARD_DBUS_INTERFACE = "org.laptop.Clipboard" + _CLIPBOARD_OBJECT_PATH = "/org/laptop/Clipboard" - def __init__(self, parent): - self._parent = parent + def __init__(self, parent): + self._parent = parent - bus = dbus.SessionBus() - bus_name = dbus.service.BusName(self._CLIPBOARD_DBUS_INTERFACE, bus=bus) - dbus.service.Object.__init__(self, bus_name, self._CLIPBOARD_OBJECT_PATH) - - @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, - in_signature="sss", out_signature="") - def add_object(self, name, mimeType, fileName): - self.object_added(name, mimeType, fileName) - logging.debug('Added object of type ' + mimeType + ' with path at ' + fileName) + bus = dbus.SessionBus() + bus_name = dbus.service.BusName(self._CLIPBOARD_DBUS_INTERFACE, bus=bus) + dbus.service.Object.__init__(self, bus_name, self._CLIPBOARD_OBJECT_PATH) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="sss", out_signature="") + def add_object(self, name, mimeType, fileName): + self.object_added(name, mimeType, fileName) + logging.debug('Added object of type ' + mimeType + ' with path at ' + fileName) - @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, - in_signature="s", out_signature="") - def delete_object(self, fileName): - self.object_deleted(fileName) - logging.debug('Deleted object with path at ' + fileName) - - @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, - in_signature="si", out_signature="") - def set_object_state(self, fileName, percent): - logging.debug('Changed object with path at ' + fileName + ' with percent ' + str(percent)) - self.object_state_changed(fileName, percent) + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="s", out_signature="") + def delete_object(self, fileName): + self.object_deleted(fileName) + logging.debug('Deleted object with path at ' + fileName) + + @dbus.service.method(_CLIPBOARD_DBUS_INTERFACE, + in_signature="si", out_signature="") + def set_object_state(self, fileName, percent): + logging.debug('Changed object with path at ' + fileName + ' with percent ' + str(percent)) + self.object_state_changed(fileName, percent) - @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="sss") - def object_added(self, name, mimeType, fileName): - pass + @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="sss") + def object_added(self, name, mimeType, fileName): + pass - @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="s") - def object_deleted(self, fileName): - pass + @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="s") + def object_deleted(self, fileName): + pass - @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="si") - def object_state_changed(self, fileName, percent): - pass + @dbus.service.signal(_CLIPBOARD_DBUS_INTERFACE, signature="si") + def object_state_changed(self, fileName, percent): + pass class ClipboardService(object): - def __init__(self): - self._dbus_helper = ClipboardDBusServiceHelper(self) + def __init__(self): + self._dbus_helper = ClipboardDBusServiceHelper(self) - def run(self): - loop = gobject.MainLoop() - try: - loop.run() - except KeyboardInterrupt: - print 'Ctrl+C pressed, exiting...' + def run(self): + loop = gobject.MainLoop() + try: + loop.run() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' diff --git a/services/nm/bubble.py b/services/nm/bubble.py index 24e68ab..bb44cd0 100644 --- a/services/nm/bubble.py +++ b/services/nm/bubble.py @@ -22,111 +22,111 @@ import gtk import hippo class Bubble(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'NetworkBubble' - - __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 - - 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(self._fill_color) - cr.set_source_rgb(*color) - cr.fill_preserve(); - - color = self._int_to_rgb(self._stroke_color) - cr.set_source_rgb(*color) - cr.set_line_width(line_width) - cr.stroke(); - - if self._percent > 0: - 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)) - - x = prog_x - y = prog_y - width = prog_width - height = prog_height - - 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(self._progress_color) - cr.set_source_rgb(*color) - cr.fill_preserve(); + __gtype_name__ = 'NetworkBubble' + + __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 + + 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(self._fill_color) + cr.set_source_rgb(*color) + cr.fill_preserve(); + + color = self._int_to_rgb(self._stroke_color) + cr.set_source_rgb(*color) + cr.set_line_width(line_width) + cr.stroke(); + + if self._percent > 0: + 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)) + + x = prog_x + y = prog_y + width = prog_width + height = prog_height + + 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(self._progress_color) + cr.set_source_rgb(*color) + cr.fill_preserve(); diff --git a/services/nm/nmclient.py b/services/nm/nmclient.py index 9d96a2a..055b4ba 100644 --- a/services/nm/nmclient.py +++ b/services/nm/nmclient.py @@ -40,15 +40,15 @@ IW_AUTH_ALG_SHARED_KEY = 0x00000002 NM_DEVICE_STAGE_STRINGS=("Unknown", - "Prepare", - "Config", - "Need Users Key", - "IP Config", - "IP Config Get", - "IP Config Commit", - "Activated", - "Failed", - "Cancled" + "Prepare", + "Config", + "Need Users Key", + "IP Config", + "IP Config Get", + "IP Config Commit", + "Activated", + "Failed", + "Cancled" ) NM_SERVICE = 'org.freedesktop.NetworkManager' @@ -70,278 +70,278 @@ sys_bus = dbus.SystemBus() class Network(gobject.GObject): - __gsignals__ = { - 'init-failed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) - } - - def __init__(self, op): - gobject.GObject.__init__(self) - self._op = op - self._ssid = None - self._mode = None - self._strength = 0 - self._valid = False - - obj = sys_bus.get_object(NM_SERVICE, self._op) - net = dbus.Interface(obj, NM_IFACE_DEVICES) - net.getProperties(reply_handler=self._update_reply_cb, - error_handler=self._update_error_cb) - - def _update_reply_cb(self, *props): - self._ssid = props[1] - self._strength = props[3] - self._mode = props[6] - self._valid = True - logging.debug("Net(%s): ssid '%s', mode %d, strength %d" % (self._op, - self._ssid, self._mode, self._strength)) - - def _update_error_cb(self, err): - logging.debug("Net(%s): failed to update. (%s)" % (self._op, err)) - self._valid = False - self.emit('init-failed') - - def get_ssid(self): - return self._ssid - - def get_op(self): - return self._op - - def get_strength(self): - return self._strength - - def set_strength(self, strength): - self._strength = strength - - def is_valid(self): - return self._valid - - def add_to_menu(self, menu, callback, dev): - strength = self._strength - if strength > 100: - strength = 100 - elif strength < 0: - strength = 0 - item = NetworkMenuItem(text=self._ssid, percent=strength) - item.connect('button-press-event', callback, (dev, self)) - menu.add_item(item) + __gsignals__ = { + 'init-failed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) + } + + def __init__(self, op): + gobject.GObject.__init__(self) + self._op = op + self._ssid = None + self._mode = None + self._strength = 0 + self._valid = False + + obj = sys_bus.get_object(NM_SERVICE, self._op) + net = dbus.Interface(obj, NM_IFACE_DEVICES) + net.getProperties(reply_handler=self._update_reply_cb, + error_handler=self._update_error_cb) + + def _update_reply_cb(self, *props): + self._ssid = props[1] + self._strength = props[3] + self._mode = props[6] + self._valid = True + logging.debug("Net(%s): ssid '%s', mode %d, strength %d" % (self._op, + self._ssid, self._mode, self._strength)) + + def _update_error_cb(self, err): + logging.debug("Net(%s): failed to update. (%s)" % (self._op, err)) + self._valid = False + self.emit('init-failed') + + def get_ssid(self): + return self._ssid + + def get_op(self): + return self._op + + def get_strength(self): + return self._strength + + def set_strength(self, strength): + self._strength = strength + + def is_valid(self): + return self._valid + + def add_to_menu(self, menu, callback, dev): + strength = self._strength + if strength > 100: + strength = 100 + elif strength < 0: + strength = 0 + item = NetworkMenuItem(text=self._ssid, percent=strength) + item.connect('button-press-event', callback, (dev, self)) + menu.add_item(item) class Device(gobject.GObject): - __gsignals__ = { - 'init-failed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'strength-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - - def __init__(self, op): - gobject.GObject.__init__(self) - self._op = op - self._iface = None - self._type = DEVICE_TYPE_UNKNOWN - self._udi = None - self._active = False - self._strength = 0 - self._link = False - self._valid = False - self._networks = {} - self._active_net = None - self._caps = 0 - - obj = sys_bus.get_object(NM_SERVICE, self._op) - dev = dbus.Interface(obj, NM_IFACE_DEVICES) - dev.getProperties(reply_handler=self._update_reply_cb, - error_handler=self._update_error_cb) - - def _update_reply_cb(self, *props): - self._iface = props[1] - self._type = props[2] - self._udi = props[3] - self._active = props[4] - if self._active: - self.emit('activated') - self._link = props[15] - self._caps = props[17] - - if self._type == DEVICE_TYPE_802_11_WIRELESS: - old_strength = self._strength - self._strength = props[14] - if self._strength != old_strength: - self.emit('strength-changed', self._strength) - self._update_networks(props[20], props[19]) - - self._valid = True - - def _update_networks(self, net_ops, active_op): - for op in net_ops: - net = Network(op) - self._networks[op] = net - net.connect('init-failed', self._net_init_failed) - if op == active_op: - self._active_net = op - - def _update_error_cb(self, err): - logging.debug("Device(%s): failed to update. (%s)" % (self._op, err)) - self._valid = False - self.emit('init-failed') - - def _net_init_failed(self, net): - net_op = net.get_op() - if not self._networks.has_key(net_op): - return - if net_op == self._active_net: - self._active_net = None - del self._networks[net_op] - - def _add_to_menu_wired(self, menu, callback): - item = NetworkMenuItem(_("Wired Network"), stylesheet="nm.Bubble.Wired", - hi_stylesheet="nm.Bubble.Wired.Hi", - act_stylesheet="nm.Bubble.Wired.Activated") - item.connect('button-press-event', callback, (self, None)) - menu.add_item(item) - - def _add_to_menu_wireless(self, menu, callback, active_only): - act_net = None - if self._active_net and self._networks.has_key(self._active_net): - act_net = self._networks[self._active_net] - - # Only add the active network if active_only == True - if active_only: - if act_net: - act_net.add_to_menu(menu, callback, self) - return - - # Otherwise, add all networks _except_ the active one - for net in self._networks.values(): - if not net.is_valid(): - continue - if act_net == net: - continue - net.add_to_menu(menu, callback, self) - - def add_to_menu(self, menu, callback, active_only=False): - if self._type == DEVICE_TYPE_802_3_ETHERNET: - self._add_to_menu_wired(menu, callback) - elif self._type == DEVICE_TYPE_802_11_WIRELESS: - self._add_to_menu_wireless(menu, callback, active_only) - - def get_op(self): - return self._op - - def get_network(self, op): - if self._networks.has_key(op): - return self._networks[op] - return None - - def get_network_ops(self): - return self._networks.keys() - - def get_strength(self): - return self._strength - - def set_strength(self, strength): - if strength == self._strength: - return False - - if strength >= 0 and strength <= 100: - self._strength = strength - else: - self._strength = 0 - - self.emit('strength-changed', self._strength) - - def network_appeared(self, network): - if self._networks.has_key(network): - return - net = Network(network) - self._networks[network] = net - net.connect('init-failed', self._net_init_failed) - - def network_disappeared(self, network): - if not self._networks.has_key(network): - return - if network == self._active_net: - self._active_net = None - del self._networks[network] - - def get_active(self): - return self._active - - def set_active(self, active, ssid=None): - self._active = active - if self._type == DEVICE_TYPE_802_11_WIRELESS: - if not ssid: - self._active_net = None - else: - for (op, net) in self._networks.items(): - if net.get_ssid() == ssid: - self._active_net = op - - def get_type(self): - return self._type - - def is_valid(self): - return self._valid - - def set_carrier(self, on): - self._link = on - - def get_capabilities(self): - return self._caps + __gsignals__ = { + 'init-failed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'strength-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, op): + gobject.GObject.__init__(self) + self._op = op + self._iface = None + self._type = DEVICE_TYPE_UNKNOWN + self._udi = None + self._active = False + self._strength = 0 + self._link = False + self._valid = False + self._networks = {} + self._active_net = None + self._caps = 0 + + obj = sys_bus.get_object(NM_SERVICE, self._op) + dev = dbus.Interface(obj, NM_IFACE_DEVICES) + dev.getProperties(reply_handler=self._update_reply_cb, + error_handler=self._update_error_cb) + + def _update_reply_cb(self, *props): + self._iface = props[1] + self._type = props[2] + self._udi = props[3] + self._active = props[4] + if self._active: + self.emit('activated') + self._link = props[15] + self._caps = props[17] + + if self._type == DEVICE_TYPE_802_11_WIRELESS: + old_strength = self._strength + self._strength = props[14] + if self._strength != old_strength: + self.emit('strength-changed', self._strength) + self._update_networks(props[20], props[19]) + + self._valid = True + + def _update_networks(self, net_ops, active_op): + for op in net_ops: + net = Network(op) + self._networks[op] = net + net.connect('init-failed', self._net_init_failed) + if op == active_op: + self._active_net = op + + def _update_error_cb(self, err): + logging.debug("Device(%s): failed to update. (%s)" % (self._op, err)) + self._valid = False + self.emit('init-failed') + + def _net_init_failed(self, net): + net_op = net.get_op() + if not self._networks.has_key(net_op): + return + if net_op == self._active_net: + self._active_net = None + del self._networks[net_op] + + def _add_to_menu_wired(self, menu, callback): + item = NetworkMenuItem(_("Wired Network"), stylesheet="nm.Bubble.Wired", + hi_stylesheet="nm.Bubble.Wired.Hi", + act_stylesheet="nm.Bubble.Wired.Activated") + item.connect('button-press-event', callback, (self, None)) + menu.add_item(item) + + def _add_to_menu_wireless(self, menu, callback, active_only): + act_net = None + if self._active_net and self._networks.has_key(self._active_net): + act_net = self._networks[self._active_net] + + # Only add the active network if active_only == True + if active_only: + if act_net: + act_net.add_to_menu(menu, callback, self) + return + + # Otherwise, add all networks _except_ the active one + for net in self._networks.values(): + if not net.is_valid(): + continue + if act_net == net: + continue + net.add_to_menu(menu, callback, self) + + def add_to_menu(self, menu, callback, active_only=False): + if self._type == DEVICE_TYPE_802_3_ETHERNET: + self._add_to_menu_wired(menu, callback) + elif self._type == DEVICE_TYPE_802_11_WIRELESS: + self._add_to_menu_wireless(menu, callback, active_only) + + def get_op(self): + return self._op + + def get_network(self, op): + if self._networks.has_key(op): + return self._networks[op] + return None + + def get_network_ops(self): + return self._networks.keys() + + def get_strength(self): + return self._strength + + def set_strength(self, strength): + if strength == self._strength: + return False + + if strength >= 0 and strength <= 100: + self._strength = strength + else: + self._strength = 0 + + self.emit('strength-changed', self._strength) + + def network_appeared(self, network): + if self._networks.has_key(network): + return + net = Network(network) + self._networks[network] = net + net.connect('init-failed', self._net_init_failed) + + def network_disappeared(self, network): + if not self._networks.has_key(network): + return + if network == self._active_net: + self._active_net = None + del self._networks[network] + + def get_active(self): + return self._active + + def set_active(self, active, ssid=None): + self._active = active + if self._type == DEVICE_TYPE_802_11_WIRELESS: + if not ssid: + self._active_net = None + else: + for (op, net) in self._networks.items(): + if net.get_ssid() == ssid: + self._active_net = op + + def get_type(self): + return self._type + + def is_valid(self): + return self._valid + + def set_carrier(self, on): + self._link = on + + def get_capabilities(self): + return self._caps nm_bubble_wireless = { - 'fill-color' : 0x646464FF, - 'stroke-color' : 0x646464FF, - 'progress-color': 0x333333FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit * 1.5 + 'fill-color' : 0x646464FF, + 'stroke-color' : 0x646464FF, + 'progress-color': 0x333333FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 } nm_bubble_wireless_hi = { - 'fill-color' : 0x979797FF, - 'stroke-color' : 0x979797FF, - 'progress-color': 0x666666FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit * 1.5 + 'fill-color' : 0x979797FF, + 'stroke-color' : 0x979797FF, + 'progress-color': 0x666666FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 } nm_bubble_wireless_activated = { - 'fill-color' : 0xA7A7A7FF, - 'stroke-color' : 0xA7A7A7FF, - 'progress-color': 0x777777FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit * 1.5 + 'fill-color' : 0xA7A7A7FF, + 'stroke-color' : 0xA7A7A7FF, + 'progress-color': 0x777777FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 } nm_bubble_wired = { - 'fill-color' : 0x000000FF, - 'stroke-color' : 0x000000FF, - 'progress-color': 0x000000FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit * 1.5 + 'fill-color' : 0x000000FF, + 'stroke-color' : 0x000000FF, + 'progress-color': 0x000000FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 } nm_bubble_wired_hi = { - 'fill-color' : 0x333333FF, - 'stroke-color' : 0x333333FF, - 'progress-color': 0x000000FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit * 1.5 + 'fill-color' : 0x333333FF, + 'stroke-color' : 0x333333FF, + 'progress-color': 0x000000FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 } nm_bubble_wired_activated = { - 'fill-color' : 0x444444FF, - 'stroke-color' : 0x444444FF, - 'progress-color': 0x000000FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit * 1.5 + 'fill-color' : 0x444444FF, + 'stroke-color' : 0x444444FF, + 'progress-color': 0x000000FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 } nm_menu_item_title = { - 'xalign': hippo.ALIGNMENT_START, - 'padding-left': 5, - 'color' : 0xFFFFFFFF, - 'font' : style.get_font_description('Bold', 1.2) + 'xalign': hippo.ALIGNMENT_START, + 'padding-left': 5, + 'color' : 0xFFFFFFFF, + 'font' : style.get_font_description('Bold', 1.2) } @@ -354,63 +354,63 @@ style.register_stylesheet("nm.Bubble.Wired.Activated", nm_bubble_wired_activated style.register_stylesheet("nm.MenuItem.Title", nm_menu_item_title) class NetworkMenuItem(Bubble): - def __init__(self, text, percent=0, stylesheet="nm.Bubble.Wireless", - hi_stylesheet="nm.Bubble.Wireless.Hi", - act_stylesheet="nm.Bubble.Wireless.Activated"): - Bubble.__init__(self, percent=percent) - self._hover = False - self._default_stylesheet = stylesheet - self._hi_stylesheet = hi_stylesheet - self._act_stylesheet = act_stylesheet - style.apply_stylesheet(self, stylesheet) - - text_item = hippo.CanvasText(text=text) - style.apply_stylesheet(text_item, 'nm.MenuItem.Title') - self.append(text_item) - - self.connect('motion-notify-event', self._motion_notify_event_cb) - # Disable active hilight for now... - #self.connect('button-press-event', self._button_press_event_cb) - - def _motion_notify_event_cb(self, widget, event): - if event.detail == hippo.MOTION_DETAIL_ENTER: - if not self._hover: - self._hover = True - style.apply_stylesheet(self, self._hi_stylesheet) - elif event.detail == hippo.MOTION_DETAIL_LEAVE: - if self._hover: - self._hover = False - style.apply_stylesheet(self, self._default_stylesheet) - return True - - def _button_press_event_cb(self, widget, event): - style.apply_stylesheet(self, self._act_stylesheet) - return False + def __init__(self, text, percent=0, stylesheet="nm.Bubble.Wireless", + hi_stylesheet="nm.Bubble.Wireless.Hi", + act_stylesheet="nm.Bubble.Wireless.Activated"): + Bubble.__init__(self, percent=percent) + self._hover = False + self._default_stylesheet = stylesheet + self._hi_stylesheet = hi_stylesheet + self._act_stylesheet = act_stylesheet + style.apply_stylesheet(self, stylesheet) + + text_item = hippo.CanvasText(text=text) + style.apply_stylesheet(text_item, 'nm.MenuItem.Title') + self.append(text_item) + + self.connect('motion-notify-event', self._motion_notify_event_cb) + # Disable active hilight for now... + #self.connect('button-press-event', self._button_press_event_cb) + + def _motion_notify_event_cb(self, widget, event): + if event.detail == hippo.MOTION_DETAIL_ENTER: + if not self._hover: + self._hover = True + style.apply_stylesheet(self, self._hi_stylesheet) + elif event.detail == hippo.MOTION_DETAIL_LEAVE: + if self._hover: + self._hover = False + style.apply_stylesheet(self, self._default_stylesheet) + return True + + def _button_press_event_cb(self, widget, event): + style.apply_stylesheet(self, self._act_stylesheet) + return False class NetworkMenu(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): - gtk.Window.__init__(self, gtk.WINDOW_POPUP) + def __init__(self): + 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) - def add_separator(self): - separator = hippo.CanvasBox() - style.apply_stylesheet(separator, 'menu.Separator') - self._root.append(separator) + def add_separator(self): + separator = hippo.CanvasBox() + style.apply_stylesheet(separator, 'menu.Separator') + self._root.append(separator) - def add_item(self, item): - self._root.append(item) + def add_item(self, item): + self._root.append(item) @@ -429,488 +429,488 @@ ICON_WIRELESS_61_80 = "stock-net-wireless-61-80" ICON_WIRELESS_81_100 = "stock-net-wireless-81-100" class NMClientApp: - def __init__(self): - self.nminfo = None - self._nm_present = False - self._nm_state = NM_STATE_UNKNOWN - self._update_timer = 0 - self._active_device = None - self._devices = {} - self._key_dialog = None - - self._icon_theme = gtk.icon_theme_get_default() - self._icons = {} - self._cur_icon = None - try: - self._icons = self._load_icons() - except RuntimeError: - logging.debug("Couldn't find required icon resources, will exit.") - os._exit(1) - self._setup_trayicon() - - 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) - - try: - self.nminfo = nminfo.NMInfo(self) - except RuntimeError: - pass - self._setup_dbus() - if self._nm_present: - self._get_nm_state() - self._get_initial_devices() - - def _get_one_icon_pixbuf(self, name): - info = self._icon_theme.lookup_icon(name, 75, 0) - if not info or not info.get_filename(): - raise RuntimeError - return gtk.gdk.pixbuf_new_from_file(info.get_filename()) - - def _load_icons(self): - icons = {} - icons[ICON_WIRED] = self._get_one_icon_pixbuf(ICON_WIRED) - icons[ICON_WIRELESS_00] = self._get_one_icon_pixbuf(ICON_WIRELESS_00) - icons[ICON_WIRELESS_01_20] = self._get_one_icon_pixbuf(ICON_WIRELESS_01_20) - icons[ICON_WIRELESS_21_40] = self._get_one_icon_pixbuf(ICON_WIRELESS_21_40) - icons[ICON_WIRELESS_41_60] = self._get_one_icon_pixbuf(ICON_WIRELESS_41_60) - icons[ICON_WIRELESS_61_80] = self._get_one_icon_pixbuf(ICON_WIRELESS_61_80) - icons[ICON_WIRELESS_81_100] = self._get_one_icon_pixbuf(ICON_WIRELESS_81_100) - return icons - - def _get_nm_state(self): - # Grab NM's state - self._nm_obj.state(reply_handler=self._get_state_reply_cb, \ - error_handler=self._get_state_error_cb) - - def _get_state_reply_cb(self, state): - if self._nm_state != state: - self._schedule_icon_update(immediate=True) - self._nm_state = state - - def _get_state_error_cb(self, err): - logging.debug("Failed to get NetworkManager state! %s" % err) - - def _get_icon(self): - act_dev = None - if self._active_device and self._devices.has_key(self._active_device): - act_dev = self._devices[self._active_device] - - pixbuf = None - if not self._nm_present \ - or not act_dev \ - or self._nm_state == NM_STATE_UNKNOWN \ - or self._nm_state == NM_STATE_ASLEEP \ - or self._nm_state == NM_STATE_DISCONNECTED: - pixbuf = self._icons[ICON_WIRELESS_00] - elif act_dev.get_type() == DEVICE_TYPE_802_3_ETHERNET: - pixbuf = self._icons[ICON_WIRED] - elif act_dev.get_type() == DEVICE_TYPE_802_11_WIRELESS: - strength = act_dev.get_strength() - if strength <= 0: - pixbuf = self._icons[ICON_WIRELESS_00] - elif strength >= 1 and strength <= 20: - pixbuf = self._icons[ICON_WIRELESS_01_20] - elif strength >= 21 and strength <= 40: - pixbuf = self._icons[ICON_WIRELESS_21_40] - elif strength >= 41 and strength <= 60: - pixbuf = self._icons[ICON_WIRELESS_41_60] - elif strength >= 61 and strength <= 80: - pixbuf = self._icons[ICON_WIRELESS_61_80] - elif strength >= 81 and strength: - pixbuf = self._icons[ICON_WIRELESS_81_100] - - if not pixbuf: - pixbuf = self._icons[ICON_WIRELESS_00] - return pixbuf - - def _setup_trayicon(self): - pixbuf = self._get_icon() - self._trayicon = gtk.status_icon_new_from_pixbuf(pixbuf) - self._trayicon.connect("popup_menu", self._status_icon_clicked) - self._trayicon.connect("activate", self._status_icon_clicked) - self._schedule_icon_update() - - def _status_icon_clicked(self, button=0, time=None): - self._timeline.play(None, 'popup') - - def _get_menu_position(self, menu, item): - (screen, rect, orientation) = item.get_geometry() - [item_x, item_y, item_w, item_h] = rect - [menu_w, menu_h] = menu.size_request() - - x = item_x + item_w - menu_w - y = item_y + item_h - - x = min(x, screen.get_width() - menu_w) - x = max(0, x) - - y = min(y, screen.get_height() - menu_h) - y = max(0, y) - - return (x, y) - - def do_popup(self, current, n_frames): - if self._menu: - return - - 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) - (x, y) = self._get_menu_position(self._menu, self._trayicon) - self._menu.move(x, y) - self._menu.show_all() - - def do_popdown(self, current, frame): - if self._menu: - self._menu.destroy() - self._menu = None - - def _popdown(self): - self._timeline.play('popdown', 'popdown') - - 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._popdown() - - def _create_menu(self): - menu = NetworkMenu() - - # Active device goes above the separator - act_dev = None - if self._active_device and self._devices.has_key(self._active_device): - act_dev = self._devices[self._active_device] - - if act_dev: - act_dev.add_to_menu(menu, self._menu_item_clicked_cb, active_only=True) - menu.add_separator() - - # Wired devices first, if they don't support carrier detect - for dev in self._devices.values(): - if not dev.is_valid(): - continue - if dev.get_type() != DEVICE_TYPE_802_3_ETHERNET: - continue - if dev.get_capabilities() & NM_DEVICE_CAP_CARRIER_DETECT: - continue - if dev == act_dev: - continue - dev.add_to_menu(menu, self._menu_item_clicked_cb) - - # Wireless devices second - for dev in self._devices.values(): - if not dev.is_valid(): - continue - if dev.get_type() != DEVICE_TYPE_802_11_WIRELESS: - continue - dev.add_to_menu(menu, self._menu_item_clicked_cb) - - return menu - - def _update_icon(self): - pixbuf = self._get_icon() - if self._cur_icon != pixbuf: - self._trayicon.set_from_pixbuf(pixbuf) - self._cur_icon = pixbuf - - blink = False - if self._nm_state == NM_STATE_CONNECTING: - blink = True - self._trayicon.set_blinking(blink) - - self._update_timer = 0 - return False - - def _schedule_icon_update(self, immediate=False): - if immediate and self._update_timer: - gobject.source_remove(self._update_timer) - self._update_timer = 0 - - if self._update_timer != 0: - # There is already an update scheduled - return - - if immediate: - self._update_timer = gobject.idle_add(self._update_icon) - else: - self._update_timer = gobject.timeout_add(2000, self._update_icon) - - def _get_initial_devices_reply_cb(self, ops): - for op in ops: - self._add_device(op) - - def _dev_init_failed_cb(self, dev): - # Device failed to initialize, likely due to dbus errors or something - op = dev.get_op() - self._remove_device(op) - - def _get_initial_devices_error_cb(self, err): - logging.debug("Error updating devices (%s)" % err) - - def _get_initial_devices(self): - self._nm_obj.getDevices(reply_handler=self._get_initial_devices_reply_cb, \ - error_handler=self._get_initial_devices_error_cb) - - def _add_device(self, dev_op): - if self._devices.has_key(dev_op): - return - dev = Device(dev_op) - self._devices[dev_op] = dev - dev.connect('init-failed', self._dev_init_failed_cb) - dev.connect('activated', self._dev_activated_cb) - dev.connect('strength-changed', self._dev_strength_changed_cb) - - def _remove_device(self, dev_op): - if not self._devices.has_key(dev_op): - return - if self._active_device == dev_op: - self._active_device = None - dev = self._devices[dev_op] - dev.disconnect('activated') - dev.disconnect('init-failed') - dev.disconnect('strength-changed') - del self._devices[dev_op] - self._schedule_icon_update(immediate=True) - - def _dev_activated_cb(self, dev): - op = dev.get_op() - if not self._devices.has_key(op): - return - if not dev.get_active(): - return - self._active_device = op - self._schedule_icon_update(immediate=True) - - def _dev_strength_changed_cb(self, dev, strength): - op = dev.get_op() - if not self._devices.has_key(op): - return - if not dev.get_active(): - return - self._schedule_icon_update() - - def get_device(self, dev_op): - if not self._devices.has_key(dev_op): - return None - return self._devices[dev_op] - - def _setup_dbus(self): - self._sig_handlers = { - 'StateChange': self.state_change_sig_handler, - 'DeviceAdded': self.device_added_sig_handler, - 'DeviceRemoved': self.device_removed_sig_handler, - 'DeviceActivationStage': self.device_activation_stage_sig_handler, - 'DeviceActivating': self.device_activating_sig_handler, - 'DeviceNowActive': self.device_now_active_sig_handler, - 'DeviceNoLongerActive': self.device_no_longer_active_sig_handler, - 'DeviceCarrierOn': self.device_carrier_on_sig_handler, - 'DeviceCarrierOff': self.device_carrier_off_sig_handler, - 'DeviceStrengthChanged': self.wireless_device_strength_changed_sig_handler, - 'WirelessNetworkAppeared': self.wireless_network_appeared_sig_handler, - 'WirelessNetworkDisappeared': self.wireless_network_disappeared_sig_handler, - 'WirelessNetworkStrengthChanged': self.wireless_network_strength_changed_sig_handler - } - - self._nm_proxy = sys_bus.get_object(NM_SERVICE, NM_PATH) - self._nm_obj = dbus.Interface(self._nm_proxy, NM_IFACE) - - sys_bus.add_signal_receiver(self.name_owner_changed_sig_handler, - signal_name="NameOwnerChanged", - dbus_interface="org.freedesktop.DBus") - - sys_bus.add_signal_receiver(self.catchall_signal_handler, - dbus_interface=NM_IFACE) - - sys_bus.add_signal_receiver(self.catchall_signal_handler, - dbus_interface=NM_IFACE + 'Devices') - - for (signal, handler) in self._sig_handlers.items(): - sys_bus.add_signal_receiver(handler, signal_name=signal, dbus_interface=NM_IFACE) - - # Find out whether or not NM is running - try: - bus_object = sys_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') - name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \ - dbus_interface='org.freedesktop.DBus') - if name: - self._nm_present = True - except dbus.DBusException: - pass - - @dbus.decorators.explicitly_pass_message - def catchall_signal_handler(self, *args, **keywords): - dbus_message = keywords['dbus_message'] - mem = dbus_message.get_member() - iface = dbus_message.get_interface() - - if iface == NM_IFACE and mem in self._sig_handlers.keys(): - return - - logging.debug('Caught signal %s.%s' % (dbus_message.get_interface(), mem)) - for arg in args: - logging.debug(' ' + str(arg)) - - def _menu_item_clicked_cb(self, widget, event, dev_data): - (device, network) = dev_data - net_op = "" - if network: - net_op = network.get_op() - try: - # NM 0.6.4 and earlier have a bug which returns an - # InvalidArguments error if no security information is passed - # for wireless networks - self._nm_obj.setActiveDevice(device.get_op(), network.get_ssid()) - except dbus.DBusException, e: - if str(e).find("invalid arguments"): - pass - else: - raise dbus.DBusException(e) - - self._popdown() - - def get_key_for_network(self, net, async_cb, async_err_cb): - # Throw up a dialog asking for the key here, and set - # the authentication algorithm to the given one, if any - # - # Key needs to be limited to _either_ 10 or 26 digits long, - # and contain _only_ _hex_ digits, 0-9 or a-f - # - # Auth algorithm should be a dropdown of: [Open System, Shared Key], - # mapping to the values [IW_AUTH_ALG_OPEN_SYSTEM, IW_AUTH_ALG_SHARED_KEY] - # above - - self._key_dialog = WEPKeyDialog(net, async_cb, async_err_cb) - self._key_dialog.connect("response", self._key_dialog_response_cb) - self._key_dialog.connect("destroy", self._key_dialog_destroy_cb) - self._key_dialog.show_all() - - def _key_dialog_destroy_cb(self, widget, foo=None): - if widget != self._key_dialog: - return - self._key_dialog_response_cb(widget, gtk.RESPONSE_CANCEL) - - def _key_dialog_response_cb(self, widget, response_id): - if widget != self._key_dialog: - return - key = self._key_dialog.get_key() - wep_auth_alg = self._key_dialog.get_auth_alg() - net = self._key_dialog.get_network() - (async_cb, async_err_cb) = self._key_dialog.get_callbacks() - - # Clear self._key_dialog before we call destroy(), otherwise - # the destroy will trigger and we'll get called again by - # self._key_dialog_destroy_cb - self._key_dialog = None - widget.destroy() - - if response_id == gtk.RESPONSE_OK: - self.nminfo.get_key_for_network_cb( - net, key, wep_auth_alg, async_cb, async_err_cb, canceled=False) - else: - self.nminfo.get_key_for_network_cb( - net, None, None, async_cb, async_err_cb, canceled=True) - - def cancel_get_key_for_network(self): - # Close the wireless key dialog and just have it return - # with the 'canceled' argument set to true - if not self._key_dialog: - return - self._key_dialog_destroy_cb(self._key_dialog) - - def device_activation_stage_sig_handler(self, device, stage): - logging.debug('Device Activation Stage "%s" for device %s' % (NM_DEVICE_STAGE_STRINGS[stage], device)) - - def state_change_sig_handler(self, state): - self._nm_state = state - self._schedule_icon_update(immediate=True) - - def device_activating_sig_handler(self, device): - self._active_device = device - - def device_now_active_sig_handler(self, device, ssid=None): - if not self._devices.has_key(device): - return - self._active_device = device - self._devices[device].set_active(True, ssid) - self._schedule_icon_update(immediate=True) - - def device_no_longer_active_sig_handler(self, device): - if not self._devices.has_key(device): - return - if self._active_device == device: - self._active_device = None - self._devices[device].set_active(False) - self._schedule_icon_update(immediate=True) - - def name_owner_changed_sig_handler(self, name, old, new): - if name != NM_SERVICE: - return - if (old and len(old)) and (not new and not len(new)): - # NM went away - self._nm_present = False - self._schedule_icon_update(immediate=True) - for op in self._devices.keys(): - del self._devices[op] - self._devices = {} - self._active_device = None - self._nm_state = NM_STATE_UNKNOWN - elif (not old and not len(old)) and (new and len(new)): - # NM started up - self._nm_present = True - self._get_nm_state() - self._get_initial_devices() - - def device_added_sig_handler(self, device): - self._add_device(device) - - def device_removed_sig_handler(self, device): - self._remove_device(device) - - def wireless_network_appeared_sig_handler(self, device, network): - if not self._devices.has_key(device): - return - self._devices[device].network_appeared(network) - - def wireless_network_disappeared_sig_handler(self, device, network): - if not self._devices.has_key(device): - return - self._devices[device].network_disappeared(network) - - def wireless_device_strength_changed_sig_handler(self, device, strength): - if not self._devices.has_key(device): - return - self._devices[device].set_strength(strength) - - def wireless_network_strength_changed_sig_handler(self, device, network, strength): - if not self._devices.has_key(device): - return - net = self._devices[device].get_network(network) - if net: - net.set_strength(strength) - - def device_carrier_on_sig_handler(self, device): - if not self._devices.has_key(device): - return - self._devices[device].set_carrier(True) - - def device_carrier_off_sig_handler(self, device): - if not self._devices.has_key(device): - return - self._devices[device].set_carrier(False) - - def run(self): - loop = gobject.MainLoop() - try: - loop.run() - except KeyboardInterrupt: - pass + def __init__(self): + self.nminfo = None + self._nm_present = False + self._nm_state = NM_STATE_UNKNOWN + self._update_timer = 0 + self._active_device = None + self._devices = {} + self._key_dialog = None + + self._icon_theme = gtk.icon_theme_get_default() + self._icons = {} + self._cur_icon = None + try: + self._icons = self._load_icons() + except RuntimeError: + logging.debug("Couldn't find required icon resources, will exit.") + os._exit(1) + self._setup_trayicon() + + 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) + + try: + self.nminfo = nminfo.NMInfo(self) + except RuntimeError: + pass + self._setup_dbus() + if self._nm_present: + self._get_nm_state() + self._get_initial_devices() + + def _get_one_icon_pixbuf(self, name): + info = self._icon_theme.lookup_icon(name, 75, 0) + if not info or not info.get_filename(): + raise RuntimeError + return gtk.gdk.pixbuf_new_from_file(info.get_filename()) + + def _load_icons(self): + icons = {} + icons[ICON_WIRED] = self._get_one_icon_pixbuf(ICON_WIRED) + icons[ICON_WIRELESS_00] = self._get_one_icon_pixbuf(ICON_WIRELESS_00) + icons[ICON_WIRELESS_01_20] = self._get_one_icon_pixbuf(ICON_WIRELESS_01_20) + icons[ICON_WIRELESS_21_40] = self._get_one_icon_pixbuf(ICON_WIRELESS_21_40) + icons[ICON_WIRELESS_41_60] = self._get_one_icon_pixbuf(ICON_WIRELESS_41_60) + icons[ICON_WIRELESS_61_80] = self._get_one_icon_pixbuf(ICON_WIRELESS_61_80) + icons[ICON_WIRELESS_81_100] = self._get_one_icon_pixbuf(ICON_WIRELESS_81_100) + return icons + + def _get_nm_state(self): + # Grab NM's state + self._nm_obj.state(reply_handler=self._get_state_reply_cb, \ + error_handler=self._get_state_error_cb) + + def _get_state_reply_cb(self, state): + if self._nm_state != state: + self._schedule_icon_update(immediate=True) + self._nm_state = state + + def _get_state_error_cb(self, err): + logging.debug("Failed to get NetworkManager state! %s" % err) + + def _get_icon(self): + act_dev = None + if self._active_device and self._devices.has_key(self._active_device): + act_dev = self._devices[self._active_device] + + pixbuf = None + if not self._nm_present \ + or not act_dev \ + or self._nm_state == NM_STATE_UNKNOWN \ + or self._nm_state == NM_STATE_ASLEEP \ + or self._nm_state == NM_STATE_DISCONNECTED: + pixbuf = self._icons[ICON_WIRELESS_00] + elif act_dev.get_type() == DEVICE_TYPE_802_3_ETHERNET: + pixbuf = self._icons[ICON_WIRED] + elif act_dev.get_type() == DEVICE_TYPE_802_11_WIRELESS: + strength = act_dev.get_strength() + if strength <= 0: + pixbuf = self._icons[ICON_WIRELESS_00] + elif strength >= 1 and strength <= 20: + pixbuf = self._icons[ICON_WIRELESS_01_20] + elif strength >= 21 and strength <= 40: + pixbuf = self._icons[ICON_WIRELESS_21_40] + elif strength >= 41 and strength <= 60: + pixbuf = self._icons[ICON_WIRELESS_41_60] + elif strength >= 61 and strength <= 80: + pixbuf = self._icons[ICON_WIRELESS_61_80] + elif strength >= 81 and strength: + pixbuf = self._icons[ICON_WIRELESS_81_100] + + if not pixbuf: + pixbuf = self._icons[ICON_WIRELESS_00] + return pixbuf + + def _setup_trayicon(self): + pixbuf = self._get_icon() + self._trayicon = gtk.status_icon_new_from_pixbuf(pixbuf) + self._trayicon.connect("popup_menu", self._status_icon_clicked) + self._trayicon.connect("activate", self._status_icon_clicked) + self._schedule_icon_update() + + def _status_icon_clicked(self, button=0, time=None): + self._timeline.play(None, 'popup') + + def _get_menu_position(self, menu, item): + (screen, rect, orientation) = item.get_geometry() + [item_x, item_y, item_w, item_h] = rect + [menu_w, menu_h] = menu.size_request() + + x = item_x + item_w - menu_w + y = item_y + item_h + + x = min(x, screen.get_width() - menu_w) + x = max(0, x) + + y = min(y, screen.get_height() - menu_h) + y = max(0, y) + + return (x, y) + + def do_popup(self, current, n_frames): + if self._menu: + return + + 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) + (x, y) = self._get_menu_position(self._menu, self._trayicon) + self._menu.move(x, y) + self._menu.show_all() + + def do_popdown(self, current, frame): + if self._menu: + self._menu.destroy() + self._menu = None + + def _popdown(self): + self._timeline.play('popdown', 'popdown') + + 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._popdown() + + def _create_menu(self): + menu = NetworkMenu() + + # Active device goes above the separator + act_dev = None + if self._active_device and self._devices.has_key(self._active_device): + act_dev = self._devices[self._active_device] + + if act_dev: + act_dev.add_to_menu(menu, self._menu_item_clicked_cb, active_only=True) + menu.add_separator() + + # Wired devices first, if they don't support carrier detect + for dev in self._devices.values(): + if not dev.is_valid(): + continue + if dev.get_type() != DEVICE_TYPE_802_3_ETHERNET: + continue + if dev.get_capabilities() & NM_DEVICE_CAP_CARRIER_DETECT: + continue + if dev == act_dev: + continue + dev.add_to_menu(menu, self._menu_item_clicked_cb) + + # Wireless devices second + for dev in self._devices.values(): + if not dev.is_valid(): + continue + if dev.get_type() != DEVICE_TYPE_802_11_WIRELESS: + continue + dev.add_to_menu(menu, self._menu_item_clicked_cb) + + return menu + + def _update_icon(self): + pixbuf = self._get_icon() + if self._cur_icon != pixbuf: + self._trayicon.set_from_pixbuf(pixbuf) + self._cur_icon = pixbuf + + blink = False + if self._nm_state == NM_STATE_CONNECTING: + blink = True + self._trayicon.set_blinking(blink) + + self._update_timer = 0 + return False + + def _schedule_icon_update(self, immediate=False): + if immediate and self._update_timer: + gobject.source_remove(self._update_timer) + self._update_timer = 0 + + if self._update_timer != 0: + # There is already an update scheduled + return + + if immediate: + self._update_timer = gobject.idle_add(self._update_icon) + else: + self._update_timer = gobject.timeout_add(2000, self._update_icon) + + def _get_initial_devices_reply_cb(self, ops): + for op in ops: + self._add_device(op) + + def _dev_init_failed_cb(self, dev): + # Device failed to initialize, likely due to dbus errors or something + op = dev.get_op() + self._remove_device(op) + + def _get_initial_devices_error_cb(self, err): + logging.debug("Error updating devices (%s)" % err) + + def _get_initial_devices(self): + self._nm_obj.getDevices(reply_handler=self._get_initial_devices_reply_cb, \ + error_handler=self._get_initial_devices_error_cb) + + def _add_device(self, dev_op): + if self._devices.has_key(dev_op): + return + dev = Device(dev_op) + self._devices[dev_op] = dev + dev.connect('init-failed', self._dev_init_failed_cb) + dev.connect('activated', self._dev_activated_cb) + dev.connect('strength-changed', self._dev_strength_changed_cb) + + def _remove_device(self, dev_op): + if not self._devices.has_key(dev_op): + return + if self._active_device == dev_op: + self._active_device = None + dev = self._devices[dev_op] + dev.disconnect('activated') + dev.disconnect('init-failed') + dev.disconnect('strength-changed') + del self._devices[dev_op] + self._schedule_icon_update(immediate=True) + + def _dev_activated_cb(self, dev): + op = dev.get_op() + if not self._devices.has_key(op): + return + if not dev.get_active(): + return + self._active_device = op + self._schedule_icon_update(immediate=True) + + def _dev_strength_changed_cb(self, dev, strength): + op = dev.get_op() + if not self._devices.has_key(op): + return + if not dev.get_active(): + return + self._schedule_icon_update() + + def get_device(self, dev_op): + if not self._devices.has_key(dev_op): + return None + return self._devices[dev_op] + + def _setup_dbus(self): + self._sig_handlers = { + 'StateChange': self.state_change_sig_handler, + 'DeviceAdded': self.device_added_sig_handler, + 'DeviceRemoved': self.device_removed_sig_handler, + 'DeviceActivationStage': self.device_activation_stage_sig_handler, + 'DeviceActivating': self.device_activating_sig_handler, + 'DeviceNowActive': self.device_now_active_sig_handler, + 'DeviceNoLongerActive': self.device_no_longer_active_sig_handler, + 'DeviceCarrierOn': self.device_carrier_on_sig_handler, + 'DeviceCarrierOff': self.device_carrier_off_sig_handler, + 'DeviceStrengthChanged': self.wireless_device_strength_changed_sig_handler, + 'WirelessNetworkAppeared': self.wireless_network_appeared_sig_handler, + 'WirelessNetworkDisappeared': self.wireless_network_disappeared_sig_handler, + 'WirelessNetworkStrengthChanged': self.wireless_network_strength_changed_sig_handler + } + + self._nm_proxy = sys_bus.get_object(NM_SERVICE, NM_PATH) + self._nm_obj = dbus.Interface(self._nm_proxy, NM_IFACE) + + sys_bus.add_signal_receiver(self.name_owner_changed_sig_handler, + signal_name="NameOwnerChanged", + dbus_interface="org.freedesktop.DBus") + + sys_bus.add_signal_receiver(self.catchall_signal_handler, + dbus_interface=NM_IFACE) + + sys_bus.add_signal_receiver(self.catchall_signal_handler, + dbus_interface=NM_IFACE + 'Devices') + + for (signal, handler) in self._sig_handlers.items(): + sys_bus.add_signal_receiver(handler, signal_name=signal, dbus_interface=NM_IFACE) + + # Find out whether or not NM is running + try: + bus_object = sys_bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') + name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \ + dbus_interface='org.freedesktop.DBus') + if name: + self._nm_present = True + except dbus.DBusException: + pass + + @dbus.decorators.explicitly_pass_message + def catchall_signal_handler(self, *args, **keywords): + dbus_message = keywords['dbus_message'] + mem = dbus_message.get_member() + iface = dbus_message.get_interface() + + if iface == NM_IFACE and mem in self._sig_handlers.keys(): + return + + logging.debug('Caught signal %s.%s' % (dbus_message.get_interface(), mem)) + for arg in args: + logging.debug(' ' + str(arg)) + + def _menu_item_clicked_cb(self, widget, event, dev_data): + (device, network) = dev_data + net_op = "" + if network: + net_op = network.get_op() + try: + # NM 0.6.4 and earlier have a bug which returns an + # InvalidArguments error if no security information is passed + # for wireless networks + self._nm_obj.setActiveDevice(device.get_op(), network.get_ssid()) + except dbus.DBusException, e: + if str(e).find("invalid arguments"): + pass + else: + raise dbus.DBusException(e) + + self._popdown() + + def get_key_for_network(self, net, async_cb, async_err_cb): + # Throw up a dialog asking for the key here, and set + # the authentication algorithm to the given one, if any + # + # Key needs to be limited to _either_ 10 or 26 digits long, + # and contain _only_ _hex_ digits, 0-9 or a-f + # + # Auth algorithm should be a dropdown of: [Open System, Shared Key], + # mapping to the values [IW_AUTH_ALG_OPEN_SYSTEM, IW_AUTH_ALG_SHARED_KEY] + # above + + self._key_dialog = WEPKeyDialog(net, async_cb, async_err_cb) + self._key_dialog.connect("response", self._key_dialog_response_cb) + self._key_dialog.connect("destroy", self._key_dialog_destroy_cb) + self._key_dialog.show_all() + + def _key_dialog_destroy_cb(self, widget, foo=None): + if widget != self._key_dialog: + return + self._key_dialog_response_cb(widget, gtk.RESPONSE_CANCEL) + + def _key_dialog_response_cb(self, widget, response_id): + if widget != self._key_dialog: + return + key = self._key_dialog.get_key() + wep_auth_alg = self._key_dialog.get_auth_alg() + net = self._key_dialog.get_network() + (async_cb, async_err_cb) = self._key_dialog.get_callbacks() + + # Clear self._key_dialog before we call destroy(), otherwise + # the destroy will trigger and we'll get called again by + # self._key_dialog_destroy_cb + self._key_dialog = None + widget.destroy() + + if response_id == gtk.RESPONSE_OK: + self.nminfo.get_key_for_network_cb( + net, key, wep_auth_alg, async_cb, async_err_cb, canceled=False) + else: + self.nminfo.get_key_for_network_cb( + net, None, None, async_cb, async_err_cb, canceled=True) + + def cancel_get_key_for_network(self): + # Close the wireless key dialog and just have it return + # with the 'canceled' argument set to true + if not self._key_dialog: + return + self._key_dialog_destroy_cb(self._key_dialog) + + def device_activation_stage_sig_handler(self, device, stage): + logging.debug('Device Activation Stage "%s" for device %s' % (NM_DEVICE_STAGE_STRINGS[stage], device)) + + def state_change_sig_handler(self, state): + self._nm_state = state + self._schedule_icon_update(immediate=True) + + def device_activating_sig_handler(self, device): + self._active_device = device + + def device_now_active_sig_handler(self, device, ssid=None): + if not self._devices.has_key(device): + return + self._active_device = device + self._devices[device].set_active(True, ssid) + self._schedule_icon_update(immediate=True) + + def device_no_longer_active_sig_handler(self, device): + if not self._devices.has_key(device): + return + if self._active_device == device: + self._active_device = None + self._devices[device].set_active(False) + self._schedule_icon_update(immediate=True) + + def name_owner_changed_sig_handler(self, name, old, new): + if name != NM_SERVICE: + return + if (old and len(old)) and (not new and not len(new)): + # NM went away + self._nm_present = False + self._schedule_icon_update(immediate=True) + for op in self._devices.keys(): + del self._devices[op] + self._devices = {} + self._active_device = None + self._nm_state = NM_STATE_UNKNOWN + elif (not old and not len(old)) and (new and len(new)): + # NM started up + self._nm_present = True + self._get_nm_state() + self._get_initial_devices() + + def device_added_sig_handler(self, device): + self._add_device(device) + + def device_removed_sig_handler(self, device): + self._remove_device(device) + + def wireless_network_appeared_sig_handler(self, device, network): + if not self._devices.has_key(device): + return + self._devices[device].network_appeared(network) + + def wireless_network_disappeared_sig_handler(self, device, network): + if not self._devices.has_key(device): + return + self._devices[device].network_disappeared(network) + + def wireless_device_strength_changed_sig_handler(self, device, strength): + if not self._devices.has_key(device): + return + self._devices[device].set_strength(strength) + + def wireless_network_strength_changed_sig_handler(self, device, network, strength): + if not self._devices.has_key(device): + return + net = self._devices[device].get_network(network) + if net: + net.set_strength(strength) + + def device_carrier_on_sig_handler(self, device): + if not self._devices.has_key(device): + return + self._devices[device].set_carrier(True) + + def device_carrier_off_sig_handler(self, device): + if not self._devices.has_key(device): + return + self._devices[device].set_carrier(False) + + def run(self): + loop = gobject.MainLoop() + try: + loop.run() + except KeyboardInterrupt: + pass diff --git a/services/nm/nminfo.py b/services/nm/nminfo.py index a86ec1a..f512a8b 100644 --- a/services/nm/nminfo.py +++ b/services/nm/nminfo.py @@ -26,65 +26,65 @@ import logging import nmclient try: - from sugar import env + from sugar import env except ImportError: - pass + pass NM_INFO_IFACE='org.freedesktop.NetworkManagerInfo' NM_INFO_PATH='/org/freedesktop/NetworkManagerInfo' class NoNetworks(dbus.DBusException): - def __init__(self): - dbus.DBusException.__init__(self) - self._dbus_error_name = NM_INFO_IFACE + '.NoNetworks' + def __init__(self): + dbus.DBusException.__init__(self) + self._dbus_error_name = NM_INFO_IFACE + '.NoNetworks' class CanceledKeyRequestError(dbus.DBusException): - def __init__(self): - dbus.DBusException.__init__(self) - self._dbus_error_name = NM_INFO_IFACE + '.CanceledError' + def __init__(self): + dbus.DBusException.__init__(self) + self._dbus_error_name = NM_INFO_IFACE + '.CanceledError' class NetworkInvalidError(Exception): - pass + pass class NMConfig(ConfigParser.ConfigParser): - def get_bool(self, section, name): - opt = self.get(section, name) - if type(opt) == type(""): - if opt.lower() == 'yes' or opt.lower() == 'true': - return True - elif opt.lower() == 'no' or opt.lower() == 'false': - return False - raise ValueError("Invalid format for %s/%s. Should be one of [yes, no, true, false]." % (section, name)) - - def get_list(self, section, name): - opt = self.get(section, name) - if type(opt) == type(""): - if not len(opt): - return [] - try: - return opt.split() - except Exception: - pass - raise ValueError("Invalid format for %s/%s. Should be a space-separate list." % (section, name)) - - def get_int(self, section, name): - opt = self.get(section, name) - try: - return int(opt) - except Exception: - pass - raise ValueError("Invalid format for %s/%s. Should be a valid integer." % (section, name)) - - def get_float(self, section, name): - opt = self.get(section, name) - try: - return float(opt) - except Exception: - pass - raise ValueError("Invalid format for %s/%s. Should be a valid float." % (section, name)) + def get_bool(self, section, name): + opt = self.get(section, name) + if type(opt) == type(""): + if opt.lower() == 'yes' or opt.lower() == 'true': + return True + elif opt.lower() == 'no' or opt.lower() == 'false': + return False + raise ValueError("Invalid format for %s/%s. Should be one of [yes, no, true, false]." % (section, name)) + + def get_list(self, section, name): + opt = self.get(section, name) + if type(opt) == type(""): + if not len(opt): + return [] + try: + return opt.split() + except Exception: + pass + raise ValueError("Invalid format for %s/%s. Should be a space-separate list." % (section, name)) + + def get_int(self, section, name): + opt = self.get(section, name) + try: + return int(opt) + except Exception: + pass + raise ValueError("Invalid format for %s/%s. Should be a valid integer." % (section, name)) + + def get_float(self, section, name): + opt = self.get(section, name) + try: + return float(opt) + except Exception: + pass + raise ValueError("Invalid format for %s/%s. Should be a valid float." % (section, name)) IW_AUTH_CIPHER_NONE = 0x00000001 @@ -102,366 +102,366 @@ NETWORK_TYPE_INVALID = 2 class Security(object): - def __init__(self, we_cipher): - self._we_cipher = we_cipher - - def read_from_config(self, cfg, name): - pass - - def read_from_args(self, args): - pass - - def new_from_config(cfg, name): - security = None - try: - we_cipher = cfg.get_int(name, "we_cipher") - if we_cipher == IW_AUTH_CIPHER_NONE: - security = Security(we_cipher) - elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104: - security = WEPSecurity(we_cipher) - else: - # FIXME: find a way to make WPA config option matrix not - # make you want to throw up - raise ValueError("Unsupported security combo") - security.read_from_config(cfg, name) - except (ConfigParser.NoOptionError, ValueError), e: - return None - return security - new_from_config = staticmethod(new_from_config) - - def new_from_args(we_cipher, args): - security = None - try: - if we_cipher == IW_AUTH_CIPHER_NONE: - security = Security(we_cipher) - elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104: - security = WEPSecurity(we_cipher) - else: - # FIXME: find a way to make WPA config option matrix not - # make you want to throw up - raise ValueError("Unsupported security combo") - security.read_from_args(args) - except ValueError, e: - logging.debug("Error reading security information: %s" % e) - del security - return None - return security - new_from_args = staticmethod(new_from_args) - - def get_properties(self): - return [dbus.Int32(self._we_cipher)] - - def write_to_config(self, section, config): - config.set(section, "we_cipher", self._we_cipher) + def __init__(self, we_cipher): + self._we_cipher = we_cipher + + def read_from_config(self, cfg, name): + pass + + def read_from_args(self, args): + pass + + def new_from_config(cfg, name): + security = None + try: + we_cipher = cfg.get_int(name, "we_cipher") + if we_cipher == IW_AUTH_CIPHER_NONE: + security = Security(we_cipher) + elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104: + security = WEPSecurity(we_cipher) + else: + # FIXME: find a way to make WPA config option matrix not + # make you want to throw up + raise ValueError("Unsupported security combo") + security.read_from_config(cfg, name) + except (ConfigParser.NoOptionError, ValueError), e: + return None + return security + new_from_config = staticmethod(new_from_config) + + def new_from_args(we_cipher, args): + security = None + try: + if we_cipher == IW_AUTH_CIPHER_NONE: + security = Security(we_cipher) + elif we_cipher == IW_AUTH_CIPHER_WEP40 or we_cipher == IW_AUTH_CIPHER_WEP104: + security = WEPSecurity(we_cipher) + else: + # FIXME: find a way to make WPA config option matrix not + # make you want to throw up + raise ValueError("Unsupported security combo") + security.read_from_args(args) + except ValueError, e: + logging.debug("Error reading security information: %s" % e) + del security + return None + return security + new_from_args = staticmethod(new_from_args) + + def get_properties(self): + return [dbus.Int32(self._we_cipher)] + + def write_to_config(self, section, config): + config.set(section, "we_cipher", self._we_cipher) class WEPSecurity(Security): - def read_from_args(self, args): - if len(args) != 2: - raise ValueError("not enough arguments") - key = args[0] - auth_alg = args[1] - if isinstance(key, unicode): - key = key.encode() - if not isinstance(key, str): - raise ValueError("wrong argument type for key") - if not isinstance(auth_alg, int): - raise ValueError("wrong argument type for auth_alg") - self._key = key - self._auth_alg = auth_alg - - def read_from_config(self, cfg, name): - # Key should be a hex encoded string - self._key = cfg.get(name, "key") - if self._we_cipher == IW_AUTH_CIPHER_WEP40 and len(self._key) != 10: - raise ValueError("Key length not right for 40-bit WEP") - if self._we_cipher == IW_AUTH_CIPHER_WEP104 and len(self._key) != 26: - raise ValueError("Key length not right for 104-bit WEP") - - try: - a = binascii.a2b_hex(self._key) - except TypeError: - raise ValueError("Key was not a hexadecimal string.") - - self._auth_alg = cfg.get_int(name, "auth_alg") - if self._auth_alg != IW_AUTH_ALG_OPEN_SYSTEM and self._auth_alg != IW_AUTH_ALG_SHARED_KEY: - raise ValueError("Invalid authentication algorithm %d" % self._auth_alg) - - def get_properties(self): - args = Security.get_properties(self) - args.append(dbus.String(self._key)) - args.append(dbus.Int32(self._auth_alg)) - return args - - def write_to_config(self, section, config): - Security.write_to_config(self, section, config) - config.set(section, "key", self._key) - config.set(section, "auth_alg", self._auth_alg) + def read_from_args(self, args): + if len(args) != 2: + raise ValueError("not enough arguments") + key = args[0] + auth_alg = args[1] + if isinstance(key, unicode): + key = key.encode() + if not isinstance(key, str): + raise ValueError("wrong argument type for key") + if not isinstance(auth_alg, int): + raise ValueError("wrong argument type for auth_alg") + self._key = key + self._auth_alg = auth_alg + + def read_from_config(self, cfg, name): + # Key should be a hex encoded string + self._key = cfg.get(name, "key") + if self._we_cipher == IW_AUTH_CIPHER_WEP40 and len(self._key) != 10: + raise ValueError("Key length not right for 40-bit WEP") + if self._we_cipher == IW_AUTH_CIPHER_WEP104 and len(self._key) != 26: + raise ValueError("Key length not right for 104-bit WEP") + + try: + a = binascii.a2b_hex(self._key) + except TypeError: + raise ValueError("Key was not a hexadecimal string.") + + self._auth_alg = cfg.get_int(name, "auth_alg") + if self._auth_alg != IW_AUTH_ALG_OPEN_SYSTEM and self._auth_alg != IW_AUTH_ALG_SHARED_KEY: + raise ValueError("Invalid authentication algorithm %d" % self._auth_alg) + + def get_properties(self): + args = Security.get_properties(self) + args.append(dbus.String(self._key)) + args.append(dbus.Int32(self._auth_alg)) + return args + + def write_to_config(self, section, config): + Security.write_to_config(self, section, config) + config.set(section, "key", self._key) + config.set(section, "auth_alg", self._auth_alg) class Network: - def __init__(self, ssid): - self.ssid = ssid - self.timestamp = int(time.time()) - self.bssids = [] - self.we_cipher = 0 - self._security = None - - def get_properties(self): - bssid_list = dbus.Array([], signature="s") - for item in self.bssids: - bssid_list.append(dbus.String(item)) - args = [dbus.String(self.ssid), dbus.Int32(self.timestamp), dbus.Boolean(True), bssid_list] - args += self._security.get_properties() - return tuple(args) - - def get_security(self): - return self._security.get_properties() - - def set_security(self, security): - self._security = security - - def read_from_args(self, auto, bssid, we_cipher, args): - if auto == False: - self.timestamp = int(time.time()) - if not bssid in self.bssids: - self.bssids.append(bssid) - - self._security = Security.new_from_args(we_cipher, args) - if not self._security: - raise NetworkInvalidError("Invalid security information") - - def read_from_config(self, config): - try: - self.timestamp = config.get_int(self.ssid, "timestamp") - except (ConfigParser.NoOptionError, ValueError), e: - raise NetworkInvalidError(e) - - self._security = Security.new_from_config(config, self.ssid) - if not self._security: - raise NetworkInvalidError(e) - - # The following don't need to be present - try: - self.bssids = config.get_list(self.ssid, "bssids") - except (ConfigParser.NoOptionError, ValueError), e: - pass - - def write_to_config(self, config): - try: - config.add_section(self.ssid) - config.set(self.ssid, "timestamp", self.timestamp) - if len(self.bssids) > 0: - opt = " " - opt.join(self.bssids) - config.set(self.ssid, "bssids", opt) - self._security.write_to_config(self.ssid, config) - except Exception, e: - logging.debug("Error writing '%s': %s" % (self.ssid, e)) + def __init__(self, ssid): + self.ssid = ssid + self.timestamp = int(time.time()) + self.bssids = [] + self.we_cipher = 0 + self._security = None + + def get_properties(self): + bssid_list = dbus.Array([], signature="s") + for item in self.bssids: + bssid_list.append(dbus.String(item)) + args = [dbus.String(self.ssid), dbus.Int32(self.timestamp), dbus.Boolean(True), bssid_list] + args += self._security.get_properties() + return tuple(args) + + def get_security(self): + return self._security.get_properties() + + def set_security(self, security): + self._security = security + + def read_from_args(self, auto, bssid, we_cipher, args): + if auto == False: + self.timestamp = int(time.time()) + if not bssid in self.bssids: + self.bssids.append(bssid) + + self._security = Security.new_from_args(we_cipher, args) + if not self._security: + raise NetworkInvalidError("Invalid security information") + + def read_from_config(self, config): + try: + self.timestamp = config.get_int(self.ssid, "timestamp") + except (ConfigParser.NoOptionError, ValueError), e: + raise NetworkInvalidError(e) + + self._security = Security.new_from_config(config, self.ssid) + if not self._security: + raise NetworkInvalidError(e) + + # The following don't need to be present + try: + self.bssids = config.get_list(self.ssid, "bssids") + except (ConfigParser.NoOptionError, ValueError), e: + pass + + def write_to_config(self, config): + try: + config.add_section(self.ssid) + config.set(self.ssid, "timestamp", self.timestamp) + if len(self.bssids) > 0: + opt = " " + opt.join(self.bssids) + config.set(self.ssid, "bssids", opt) + self._security.write_to_config(self.ssid, config) + except Exception, e: + logging.debug("Error writing '%s': %s" % (self.ssid, e)) class NotFoundError(dbus.DBusException): - pass + pass class UnsupportedError(dbus.DBusException): - pass + pass class NMInfoDBusServiceHelper(dbus.service.Object): - def __init__(self, parent): - self._parent = parent - bus = dbus.SystemBus() - - # If NMI is already around, don't grab the NMI service - bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') - name = None - try: - name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \ - dbus_interface='org.freedesktop.DBus') - except dbus.DBusException: - pass - if name: - logging.debug("NMI service already owned by %s, won't claim it." % name) - raise RuntimeError - - bus_name = dbus.service.BusName(NM_INFO_IFACE, bus=bus) - dbus.service.Object.__init__(self, bus_name, NM_INFO_PATH) - - @dbus.service.method(NM_INFO_IFACE, in_signature='i', out_signature='as') - def getNetworks(self, net_type): - ssids = self._parent.get_networks(net_type) - if len(ssids) > 0: - return dbus.Array(ssids) - - raise NoNetworks() - - @dbus.service.method(NM_INFO_IFACE, in_signature='si', async_callbacks=('async_cb', 'async_err_cb')) - def getNetworkProperties(self, ssid, net_type, async_cb, async_err_cb): - self._parent.get_network_properties(ssid, net_type, async_cb, async_err_cb) - - @dbus.service.method(NM_INFO_IFACE) - def updateNetworkInfo(self, ssid, bauto, bssid, cipher, *args): - self._parent.update_network_info(ssid, bauto, bssid, cipher, args) - - @dbus.service.method(NM_INFO_IFACE, async_callbacks=('async_cb', 'async_err_cb')) - def getKeyForNetwork(self, dev_path, net_path, ssid, attempt, new_key, async_cb, async_err_cb): - self._parent.get_key_for_network(dev_path, net_path, ssid, - attempt, new_key, async_cb, async_err_cb) - - @dbus.service.method(NM_INFO_IFACE) - def cancelGetKeyForNetwork(self): - self._parent.cancel_get_key_for_network() + def __init__(self, parent): + self._parent = parent + bus = dbus.SystemBus() + + # If NMI is already around, don't grab the NMI service + bus_object = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus') + name = None + try: + name = bus_object.GetNameOwner("org.freedesktop.NetworkManagerInfo", \ + dbus_interface='org.freedesktop.DBus') + except dbus.DBusException: + pass + if name: + logging.debug("NMI service already owned by %s, won't claim it." % name) + raise RuntimeError + + bus_name = dbus.service.BusName(NM_INFO_IFACE, bus=bus) + dbus.service.Object.__init__(self, bus_name, NM_INFO_PATH) + + @dbus.service.method(NM_INFO_IFACE, in_signature='i', out_signature='as') + def getNetworks(self, net_type): + ssids = self._parent.get_networks(net_type) + if len(ssids) > 0: + return dbus.Array(ssids) + + raise NoNetworks() + + @dbus.service.method(NM_INFO_IFACE, in_signature='si', async_callbacks=('async_cb', 'async_err_cb')) + def getNetworkProperties(self, ssid, net_type, async_cb, async_err_cb): + self._parent.get_network_properties(ssid, net_type, async_cb, async_err_cb) + + @dbus.service.method(NM_INFO_IFACE) + def updateNetworkInfo(self, ssid, bauto, bssid, cipher, *args): + self._parent.update_network_info(ssid, bauto, bssid, cipher, args) + + @dbus.service.method(NM_INFO_IFACE, async_callbacks=('async_cb', 'async_err_cb')) + def getKeyForNetwork(self, dev_path, net_path, ssid, attempt, new_key, async_cb, async_err_cb): + self._parent.get_key_for_network(dev_path, net_path, ssid, + attempt, new_key, async_cb, async_err_cb) + + @dbus.service.method(NM_INFO_IFACE) + def cancelGetKeyForNetwork(self): + self._parent.cancel_get_key_for_network() class NMInfo(object): - def __init__(self, client): - try: - profile_path = env.get_profile_path() - except NameError: - home = os.path.expanduser("~") - profile_path = os.path.join(home, ".sugar", "default") - self._cfg_file = os.path.join(profile_path, "nm", "networks.cfg") - self._nmclient = client - self._allowed_networks = self._read_config() - self._dbus_helper = NMInfoDBusServiceHelper(self) - - def save_config(self): - self._write_config(self._allowed_networks) - - def _read_config(self): - if not os.path.exists(os.path.dirname(self._cfg_file)): - os.makedirs(os.path.dirname(self._cfg_file), 0755) - if not os.path.exists(self._cfg_file): - self._write_config({}) - return {} - - config = NMConfig() - config.read(self._cfg_file) - networks = {} - for name in config.sections(): - if not isinstance(name, unicode): - name = unicode(name) - net = Network(name) - try: - net.read_from_config(config) - networks[name] = net - except NetworkInvalidError, e: - logging.debug("Error: invalid stored network config: %s" % e) - del net - del config - return networks - - def _write_config(self, networks): - fp = open(self._cfg_file, 'w') - config = NMConfig() - for net in networks.values(): - net.write_to_config(config) - config.write(fp) - fp.close() - del config - - def get_networks(self, net_type): - if net_type != NETWORK_TYPE_ALLOWED: - raise ValueError("Bad network type") - nets = [] - for net in self._allowed_networks.values(): - nets.append(net.ssid) - logging.debug("Returning networks: %s" % nets) - return nets - - def get_network_properties(self, ssid, net_type, async_cb, async_err_cb): - if not isinstance(ssid, unicode): - async_err_cb(ValueError("Invalid arguments; ssid must be unicode.")) - if net_type != NETWORK_TYPE_ALLOWED: - async_err_cb(ValueError("Bad network type")) - if not self._allowed_networks.has_key(ssid): - async_err_cb(NotFoundError("Network '%s' not found." % ssid)) - network = self._allowed_networks[ssid] - props = network.get_properties() - - # DBus workaround: the normal method return handler wraps - # the returned arguments in a tuple and then converts that to a - # struct, but NetworkManager expects a plain list of arguments. - # It turns out that the async callback method return code _doesn't_ - # wrap the returned arguments in a tuple, so as a workaround use - # the async callback stuff here even though we're not doing it - # asynchronously. - async_cb(*props) - - def update_network_info(self, ssid, auto, bssid, we_cipher, args): - if not isinstance(ssid, unicode): - raise ValueError("Invalid arguments; ssid must be unicode.") - if self._allowed_networks.has_key(ssid): - del self._allowed_networks[ssid] - net = Network(ssid) - try: - net.read_from_args(auto, bssid, we_cipher, args) - logging.debug("Updated network information for '%s'." % ssid) - self._allowed_networks[ssid] = net - self.save_config() - except NetworkInvalidError, e: - logging.debug("Error updating network information: %s" % e) - del net - - def get_key_for_network(self, dev_op, net_op, ssid, attempt, new_key, async_cb, async_err_cb): - if not isinstance(ssid, unicode): - raise ValueError("Invalid arguments; ssid must be unicode.") - if self._allowed_networks.has_key(ssid) and not new_key: - # We've got the info already - net = self._allowed_networks[ssid] - async_cb(tuple(net.get_security())) - return - - # Otherwise, ask the user for it - net = None - dev = self._nmclient.get_device(dev_op) - if not dev: - async_err_cb(NotFoundError("Device was unknown.")) - return - - if dev.get_type() == nmclient.DEVICE_TYPE_802_3_ETHERNET: - # We don't support wired 802.1x yet... - async_err_cb(UnsupportedError("Device type is unsupported by NMI.")) - return - - net = dev.get_network(net_op) - if not net: - async_err_cb(NotFoundError("Network was unknown.")) - return - - self._nmclient.get_key_for_network(net, async_cb, async_err_cb) - - def get_key_for_network_cb(self, net, key, auth_alg, async_cb, async_err_cb, canceled=False): - """ - Called by the NMClient when the Wireless Network Key dialog - is closed. - """ - if canceled: - e = CanceledKeyRequestError("Request was canceled.") - # key dialog dialog was canceled; send the error back to NM - async_err_cb(e) - return - - if not key or not auth_alg: - # no key returned, *** BUG ***; the key dialog - # should always return either a key + auth_alg, or a - #cancel error - raise RuntimeError("No key or auth alg given! Bug!") - - we_cipher = None - if len(key) == 26: - we_cipher = IW_AUTH_CIPHER_WEP104 - elif len(key) == 10: - we_cipher = IW_AUTH_CIPHER_WEP40 - else: - raise RuntimeError("Invalid key length!") - - # Stuff the returned key and auth algorithm into a security object - # and return it to NetworkManager - sec = Security.new_from_args(we_cipher, (key, auth_alg)) - if not sec: - raise RuntimeError("Invalid security arguments.") - props = sec.get_properties() - a = tuple(props) - async_cb(*a) - - def cancel_get_key_for_network(self): - # Tell the NMClient to close the key request dialog - self._nmclient.cancel_get_key_for_network() + def __init__(self, client): + try: + profile_path = env.get_profile_path() + except NameError: + home = os.path.expanduser("~") + profile_path = os.path.join(home, ".sugar", "default") + self._cfg_file = os.path.join(profile_path, "nm", "networks.cfg") + self._nmclient = client + self._allowed_networks = self._read_config() + self._dbus_helper = NMInfoDBusServiceHelper(self) + + def save_config(self): + self._write_config(self._allowed_networks) + + def _read_config(self): + if not os.path.exists(os.path.dirname(self._cfg_file)): + os.makedirs(os.path.dirname(self._cfg_file), 0755) + if not os.path.exists(self._cfg_file): + self._write_config({}) + return {} + + config = NMConfig() + config.read(self._cfg_file) + networks = {} + for name in config.sections(): + if not isinstance(name, unicode): + name = unicode(name) + net = Network(name) + try: + net.read_from_config(config) + networks[name] = net + except NetworkInvalidError, e: + logging.debug("Error: invalid stored network config: %s" % e) + del net + del config + return networks + + def _write_config(self, networks): + fp = open(self._cfg_file, 'w') + config = NMConfig() + for net in networks.values(): + net.write_to_config(config) + config.write(fp) + fp.close() + del config + + def get_networks(self, net_type): + if net_type != NETWORK_TYPE_ALLOWED: + raise ValueError("Bad network type") + nets = [] + for net in self._allowed_networks.values(): + nets.append(net.ssid) + logging.debug("Returning networks: %s" % nets) + return nets + + def get_network_properties(self, ssid, net_type, async_cb, async_err_cb): + if not isinstance(ssid, unicode): + async_err_cb(ValueError("Invalid arguments; ssid must be unicode.")) + if net_type != NETWORK_TYPE_ALLOWED: + async_err_cb(ValueError("Bad network type")) + if not self._allowed_networks.has_key(ssid): + async_err_cb(NotFoundError("Network '%s' not found." % ssid)) + network = self._allowed_networks[ssid] + props = network.get_properties() + + # DBus workaround: the normal method return handler wraps + # the returned arguments in a tuple and then converts that to a + # struct, but NetworkManager expects a plain list of arguments. + # It turns out that the async callback method return code _doesn't_ + # wrap the returned arguments in a tuple, so as a workaround use + # the async callback stuff here even though we're not doing it + # asynchronously. + async_cb(*props) + + def update_network_info(self, ssid, auto, bssid, we_cipher, args): + if not isinstance(ssid, unicode): + raise ValueError("Invalid arguments; ssid must be unicode.") + if self._allowed_networks.has_key(ssid): + del self._allowed_networks[ssid] + net = Network(ssid) + try: + net.read_from_args(auto, bssid, we_cipher, args) + logging.debug("Updated network information for '%s'." % ssid) + self._allowed_networks[ssid] = net + self.save_config() + except NetworkInvalidError, e: + logging.debug("Error updating network information: %s" % e) + del net + + def get_key_for_network(self, dev_op, net_op, ssid, attempt, new_key, async_cb, async_err_cb): + if not isinstance(ssid, unicode): + raise ValueError("Invalid arguments; ssid must be unicode.") + if self._allowed_networks.has_key(ssid) and not new_key: + # We've got the info already + net = self._allowed_networks[ssid] + async_cb(tuple(net.get_security())) + return + + # Otherwise, ask the user for it + net = None + dev = self._nmclient.get_device(dev_op) + if not dev: + async_err_cb(NotFoundError("Device was unknown.")) + return + + if dev.get_type() == nmclient.DEVICE_TYPE_802_3_ETHERNET: + # We don't support wired 802.1x yet... + async_err_cb(UnsupportedError("Device type is unsupported by NMI.")) + return + + net = dev.get_network(net_op) + if not net: + async_err_cb(NotFoundError("Network was unknown.")) + return + + self._nmclient.get_key_for_network(net, async_cb, async_err_cb) + + def get_key_for_network_cb(self, net, key, auth_alg, async_cb, async_err_cb, canceled=False): + """ + Called by the NMClient when the Wireless Network Key dialog + is closed. + """ + if canceled: + e = CanceledKeyRequestError("Request was canceled.") + # key dialog dialog was canceled; send the error back to NM + async_err_cb(e) + return + + if not key or not auth_alg: + # no key returned, *** BUG ***; the key dialog + # should always return either a key + auth_alg, or a + #cancel error + raise RuntimeError("No key or auth alg given! Bug!") + + we_cipher = None + if len(key) == 26: + we_cipher = IW_AUTH_CIPHER_WEP104 + elif len(key) == 10: + we_cipher = IW_AUTH_CIPHER_WEP40 + else: + raise RuntimeError("Invalid key length!") + + # Stuff the returned key and auth algorithm into a security object + # and return it to NetworkManager + sec = Security.new_from_args(we_cipher, (key, auth_alg)) + if not sec: + raise RuntimeError("Invalid security arguments.") + props = sec.get_properties() + a = tuple(props) + async_cb(*a) + + def cancel_get_key_for_network(self): + # Tell the NMClient to close the key request dialog + self._nmclient.cancel_get_key_for_network() diff --git a/services/nm/wepkeydialog.py b/services/nm/wepkeydialog.py index 40c8a0b..fc35ee1 100644 --- a/services/nm/wepkeydialog.py +++ b/services/nm/wepkeydialog.py @@ -22,60 +22,60 @@ IW_AUTH_ALG_OPEN_SYSTEM = 0x00000001 IW_AUTH_ALG_SHARED_KEY = 0x00000002 class WEPKeyDialog(gtk.Dialog): - def __init__(self, net, async_cb, async_err_cb): - gtk.Dialog.__init__(self) - self.set_title("Wireless Key Required") + def __init__(self, net, async_cb, async_err_cb): + gtk.Dialog.__init__(self) + self.set_title("Wireless Key Required") - self._net = net - self._async_cb = async_cb - self._async_err_cb = async_err_cb + self._net = net + self._async_cb = async_cb + self._async_err_cb = async_err_cb - self.set_has_separator(False) + self.set_has_separator(False) - label = gtk.Label("A wireless encryption key is required for\n" \ - " the wireless network '%s'." % net.get_ssid()) - self.vbox.pack_start(label) + label = gtk.Label("A wireless encryption key is required for\n" \ + " the wireless network '%s'." % net.get_ssid()) + self.vbox.pack_start(label) - self._entry = gtk.Entry() - self._entry.props.visibility = False - self._entry.connect('changed', self._entry_changed_cb) - self.vbox.pack_start(self._entry) - self.vbox.show_all() + self._entry = gtk.Entry() + self._entry.props.visibility = False + self._entry.connect('changed', self._entry_changed_cb) + self.vbox.pack_start(self._entry) + self.vbox.show_all() - self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_OK, gtk.RESPONSE_OK) + self.add_buttons(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, + gtk.STOCK_OK, gtk.RESPONSE_OK) - self.set_default_response(gtk.RESPONSE_OK) - self._update_response_sensitivity() + self.set_default_response(gtk.RESPONSE_OK) + self._update_response_sensitivity() - def get_key(self): - return self._entry.get_text() + def get_key(self): + return self._entry.get_text() - def get_auth_alg(self): - return IW_AUTH_ALG_OPEN_SYSTEM + def get_auth_alg(self): + return IW_AUTH_ALG_OPEN_SYSTEM - def get_network(self): - return self._net + def get_network(self): + return self._net - def get_callbacks(self): - return (self._async_cb, self._async_err_cb) + def get_callbacks(self): + return (self._async_cb, self._async_err_cb) - def _entry_changed_cb(self, entry): - self._update_response_sensitivity() + def _entry_changed_cb(self, entry): + self._update_response_sensitivity() - def _update_response_sensitivity(self): - key = self.get_key() + def _update_response_sensitivity(self): + key = self.get_key() - is_hex = True - for c in key: - if not 'a' <= c <= 'f' and not '0' <= c <= '9': - is_hex = False + is_hex = True + for c in key: + if not 'a' <= c <= 'f' and not '0' <= c <= '9': + is_hex = False - valid_len = (len(key) == 10 or len(key) == 26) - self.set_response_sensitive(gtk.RESPONSE_OK, is_hex and valid_len) + valid_len = (len(key) == 10 or len(key) == 26) + self.set_response_sensitive(gtk.RESPONSE_OK, is_hex and valid_len) if __name__ == "__main__": - dialog = WEPKeyDialog() - dialog.run() + dialog = WEPKeyDialog() + dialog.run() - print dialog.get_key() + print dialog.get_key() diff --git a/services/presence/Activity.py b/services/presence/Activity.py index 065fb49..031c036 100644 --- a/services/presence/Activity.py +++ b/services/presence/Activity.py @@ -22,155 +22,155 @@ ACTIVITY_DBUS_INTERFACE = "org.laptop.Presence.Activity" class ActivityDBusHelper(dbus.service.Object): - def __init__(self, parent, bus_name, object_path): - self._parent = parent - self._bus_name = bus_name - self._object_path = object_path - dbus.service.Object.__init__(self, bus_name, self._object_path) - - @dbus.service.method(ACTIVITY_DBUS_INTERFACE, - in_signature="s", out_signature="ao") - def getServicesOfType(self, stype): - ret = [] - for serv in self._parent.get_services_of_type(stype): - ret.append(serv.object_path()) - return ret - - @dbus.service.method(ACTIVITY_DBUS_INTERFACE, - in_signature="", out_signature="ao") - def getServices(self): - ret = [] - for serv in self._parent.get_services(): - ret.append(serv.object_path()) - return ret - - @dbus.service.method(ACTIVITY_DBUS_INTERFACE, - in_signature="", out_signature="s") - def getId(self): - return self._parent.get_id() - - @dbus.service.method(ACTIVITY_DBUS_INTERFACE, - in_signature="", out_signature="s") - def getColor(self): - return self._parent.get_color() - - @dbus.service.method(ACTIVITY_DBUS_INTERFACE, - in_signature="", out_signature="ao") - def getJoinedBuddies(self): - ret = [] - for buddy in self._parent.get_joined_buddies(): - ret.append(buddy.object_path()) - return ret - - @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, - signature="o") - def ServiceAppeared(self, object_path): - pass - - @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, - signature="o") - def ServiceDisappeared(self, object_path): - pass - - @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, - signature="o") - def BuddyJoined(self, object_path): - pass - - @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, - signature="o") - def BuddyLeft(self, object_path): - pass + def __init__(self, parent, bus_name, object_path): + self._parent = parent + self._bus_name = bus_name + self._object_path = object_path + dbus.service.Object.__init__(self, bus_name, self._object_path) + + @dbus.service.method(ACTIVITY_DBUS_INTERFACE, + in_signature="s", out_signature="ao") + def getServicesOfType(self, stype): + ret = [] + for serv in self._parent.get_services_of_type(stype): + ret.append(serv.object_path()) + return ret + + @dbus.service.method(ACTIVITY_DBUS_INTERFACE, + in_signature="", out_signature="ao") + def getServices(self): + ret = [] + for serv in self._parent.get_services(): + ret.append(serv.object_path()) + return ret + + @dbus.service.method(ACTIVITY_DBUS_INTERFACE, + in_signature="", out_signature="s") + def getId(self): + return self._parent.get_id() + + @dbus.service.method(ACTIVITY_DBUS_INTERFACE, + in_signature="", out_signature="s") + def getColor(self): + return self._parent.get_color() + + @dbus.service.method(ACTIVITY_DBUS_INTERFACE, + in_signature="", out_signature="ao") + def getJoinedBuddies(self): + ret = [] + for buddy in self._parent.get_joined_buddies(): + ret.append(buddy.object_path()) + return ret + + @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, + signature="o") + def ServiceAppeared(self, object_path): + pass + + @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, + signature="o") + def ServiceDisappeared(self, object_path): + pass + + @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, + signature="o") + def BuddyJoined(self, object_path): + pass + + @dbus.service.signal(ACTIVITY_DBUS_INTERFACE, + signature="o") + def BuddyLeft(self, object_path): + pass class Activity(object): - def __init__(self, bus_name, object_id, initial_service): - if not initial_service.get_activity_id(): - raise ValueError("Service must have a valid Activity ID") - self._activity_id = initial_service.get_activity_id() - - self._buddies = [] - self._services = {} # service type -> list of Services - self._color = None - self._valid = False - - self._object_id = object_id - self._object_path = "/org/laptop/Presence/Activities/%d" % self._object_id - self._dbus_helper = ActivityDBusHelper(self, bus_name, self._object_path) - - self.add_service(initial_service) - - def object_path(self): - return dbus.ObjectPath(self._object_path) - - def is_valid(self): - """An activity is only valid when it's color is available.""" - return self._valid - - def get_id(self): - return self._activity_id - - def get_color(self): - return self._color - - def get_services(self): - ret = [] - for serv_list in self._services.values(): - for service in serv_list: - if service not in ret: - ret.append(service) - return ret - - def get_services_of_type(self, stype): - if self._services.has_key(stype): - return self._services[stype] - return [] - - def get_joined_buddies(self): - buddies = [] - for serv_list in self._services.values(): - for serv in serv_list: - owner = serv.get_owner() - if owner and not owner in buddies and owner.is_valid(): - buddies.append(owner) - return buddies - - def add_service(self, service): - stype = service.get_type() - if not self._services.has_key(stype): - self._services[stype] = [] - - if not self._color: - color = service.get_one_property('color') - if color: - self._color = color - self._valid = True - - # Send out the BuddyJoined signal if this is the first - # service from the buddy that we've seen - buddies = self.get_joined_buddies() - serv_owner = service.get_owner() - if serv_owner and serv_owner not in buddies and serv_owner.is_valid(): - self._dbus_helper.BuddyJoined(serv_owner.object_path()) - serv_owner.add_activity(self) - - if not service in self._services[stype]: - self._services[stype].append(service) - self._dbus_helper.ServiceAppeared(service.object_path()) - - def remove_service(self, service): - stype = service.get_type() - if not self._services.has_key(stype): - return - self._services[stype].remove(service) - self._dbus_helper.ServiceDisappeared(service.object_path()) - if len(self._services[stype]) == 0: - del self._services[stype] - - # Send out the BuddyLeft signal if this is the last - # service from the buddy - buddies = self.get_joined_buddies() - serv_owner = service.get_owner() - if serv_owner and serv_owner not in buddies and serv_owner.is_valid(): - serv_owner.remove_activity(self) - self._dbus_helper.BuddyLeft(serv_owner.object_path()) + def __init__(self, bus_name, object_id, initial_service): + if not initial_service.get_activity_id(): + raise ValueError("Service must have a valid Activity ID") + self._activity_id = initial_service.get_activity_id() + + self._buddies = [] + self._services = {} # service type -> list of Services + self._color = None + self._valid = False + + self._object_id = object_id + self._object_path = "/org/laptop/Presence/Activities/%d" % self._object_id + self._dbus_helper = ActivityDBusHelper(self, bus_name, self._object_path) + + self.add_service(initial_service) + + def object_path(self): + return dbus.ObjectPath(self._object_path) + + def is_valid(self): + """An activity is only valid when it's color is available.""" + return self._valid + + def get_id(self): + return self._activity_id + + def get_color(self): + return self._color + + def get_services(self): + ret = [] + for serv_list in self._services.values(): + for service in serv_list: + if service not in ret: + ret.append(service) + return ret + + def get_services_of_type(self, stype): + if self._services.has_key(stype): + return self._services[stype] + return [] + + def get_joined_buddies(self): + buddies = [] + for serv_list in self._services.values(): + for serv in serv_list: + owner = serv.get_owner() + if owner and not owner in buddies and owner.is_valid(): + buddies.append(owner) + return buddies + + def add_service(self, service): + stype = service.get_type() + if not self._services.has_key(stype): + self._services[stype] = [] + + if not self._color: + color = service.get_one_property('color') + if color: + self._color = color + self._valid = True + + # Send out the BuddyJoined signal if this is the first + # service from the buddy that we've seen + buddies = self.get_joined_buddies() + serv_owner = service.get_owner() + if serv_owner and serv_owner not in buddies and serv_owner.is_valid(): + self._dbus_helper.BuddyJoined(serv_owner.object_path()) + serv_owner.add_activity(self) + + if not service in self._services[stype]: + self._services[stype].append(service) + self._dbus_helper.ServiceAppeared(service.object_path()) + + def remove_service(self, service): + stype = service.get_type() + if not self._services.has_key(stype): + return + self._services[stype].remove(service) + self._dbus_helper.ServiceDisappeared(service.object_path()) + if len(self._services[stype]) == 0: + del self._services[stype] + + # Send out the BuddyLeft signal if this is the last + # service from the buddy + buddies = self.get_joined_buddies() + serv_owner = service.get_owner() + if serv_owner and serv_owner not in buddies and serv_owner.is_valid(): + serv_owner.remove_activity(self) + self._dbus_helper.BuddyLeft(serv_owner.object_path()) diff --git a/services/presence/Buddy.py b/services/presence/Buddy.py index a49b46a..31d8fe1 100644 --- a/services/presence/Buddy.py +++ b/services/presence/Buddy.py @@ -30,433 +30,433 @@ _BUDDY_KEY_COLOR = 'color' _BUDDY_KEY_CURACT = 'curact' class NotFoundError(Exception): - pass + pass class BuddyDBusHelper(dbus.service.Object): - def __init__(self, parent, bus_name, object_path): - self._parent = parent - self._bus_name = bus_name - self._object_path = object_path - dbus.service.Object.__init__(self, bus_name, self._object_path) - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="o") - def ServiceAppeared(self, object_path): - pass - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="o") - def ServiceDisappeared(self, object_path): - pass - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="") - def Disappeared(self): - pass - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="ao") - def CurrentActivityChanged(self, activities): - pass - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="") - def IconChanged(self): - pass - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="o") - def JoinedActivity(self, object_path): - pass - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="o") - def LeftActivity(self, object_path): - pass - - @dbus.service.signal(BUDDY_DBUS_INTERFACE, - signature="as") - def PropertyChanged(self, prop_list): - pass - - @dbus.service.method(BUDDY_DBUS_INTERFACE, - in_signature="", out_signature="ay") - def getIcon(self): - icon = self._parent.get_icon() - if not icon: - return "" - return icon - - @dbus.service.method(BUDDY_DBUS_INTERFACE, - in_signature="so", out_signature="o") - def getServiceOfType(self, stype, activity_op): - activity = None - # "/" is the placeholder for None - if activity_op != "/": - for act in self._parent.get_joined_activities(): - if act.object_path() == activity_op: - activity = act - if not activity: - raise NotFoundError("Not found") - - service = self._parent.get_service_of_type(stype, activity) - if not service: - raise NotFoundError("Not found") - return service.object_path() - - @dbus.service.method(BUDDY_DBUS_INTERFACE, - in_signature="", out_signature="ao") - def getJoinedActivities(self): - acts = [] - for act in self._parent.get_joined_activities(): - acts.append(act.object_path()) - return acts - - @dbus.service.method(BUDDY_DBUS_INTERFACE, - in_signature="", out_signature="a{sv}") - def getProperties(self): - props = {} - props['name'] = self._parent.get_name() - addr = self._parent.get_address() - if addr: - props['ip4_address'] = addr - props['owner'] = self._parent.is_owner() - color = self._parent.get_color() - if color: - props[_BUDDY_KEY_COLOR] = self._parent.get_color() - return props - - @dbus.service.method(BUDDY_DBUS_INTERFACE, - in_signature="", out_signature="o") - def getCurrentActivity(self): - activity = self._parent.get_current_activity() - if not activity: - raise NotFoundError() - return activity.object_path() + def __init__(self, parent, bus_name, object_path): + self._parent = parent + self._bus_name = bus_name + self._object_path = object_path + dbus.service.Object.__init__(self, bus_name, self._object_path) + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="o") + def ServiceAppeared(self, object_path): + pass + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="o") + def ServiceDisappeared(self, object_path): + pass + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="") + def Disappeared(self): + pass + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="ao") + def CurrentActivityChanged(self, activities): + pass + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="") + def IconChanged(self): + pass + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="o") + def JoinedActivity(self, object_path): + pass + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="o") + def LeftActivity(self, object_path): + pass + + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="as") + def PropertyChanged(self, prop_list): + pass + + @dbus.service.method(BUDDY_DBUS_INTERFACE, + in_signature="", out_signature="ay") + def getIcon(self): + icon = self._parent.get_icon() + if not icon: + return "" + return icon + + @dbus.service.method(BUDDY_DBUS_INTERFACE, + in_signature="so", out_signature="o") + def getServiceOfType(self, stype, activity_op): + activity = None + # "/" is the placeholder for None + if activity_op != "/": + for act in self._parent.get_joined_activities(): + if act.object_path() == activity_op: + activity = act + if not activity: + raise NotFoundError("Not found") + + service = self._parent.get_service_of_type(stype, activity) + if not service: + raise NotFoundError("Not found") + return service.object_path() + + @dbus.service.method(BUDDY_DBUS_INTERFACE, + in_signature="", out_signature="ao") + def getJoinedActivities(self): + acts = [] + for act in self._parent.get_joined_activities(): + acts.append(act.object_path()) + return acts + + @dbus.service.method(BUDDY_DBUS_INTERFACE, + in_signature="", out_signature="a{sv}") + def getProperties(self): + props = {} + props['name'] = self._parent.get_name() + addr = self._parent.get_address() + if addr: + props['ip4_address'] = addr + props['owner'] = self._parent.is_owner() + color = self._parent.get_color() + if color: + props[_BUDDY_KEY_COLOR] = self._parent.get_color() + return props + + @dbus.service.method(BUDDY_DBUS_INTERFACE, + in_signature="", out_signature="o") + def getCurrentActivity(self): + activity = self._parent.get_current_activity() + if not activity: + raise NotFoundError() + return activity.object_path() class Buddy(object): - """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, service, icon_cache): - 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") - # Normal Buddy objects must be created with a valid service, - # owner objects do not - if not isinstance(self, Owner): - if not isinstance(service, Service.Service): - raise ValueError("service must be a valid service object") - - self._services = {} - self._activities = {} - - self._icon_cache = icon_cache - - self._nick_name = None - self._address = None - if service is not None: - self._nick_name = service.get_name() - self._address = service.get_source_address() - self._color = None - self._current_activity = None - self._valid = False - self._icon = None - self._icon_tries = 0 - - self._object_id = object_id - self._object_path = BUDDY_DBUS_OBJECT_PATH + str(self._object_id) - self._dbus_helper = BuddyDBusHelper(self, bus_name, self._object_path) - - self._buddy_presence_service = None - if service is not None: - self.add_service(service) - - def object_path(self): - return dbus.ObjectPath(self._object_path) - - def _request_buddy_icon_cb(self, result_status, response, user_data): - """Callback when icon request has completed.""" - from sugar.p2p import network - icon = response - service = user_data - if result_status == network.RESULT_SUCCESS: - if icon and len(icon): - icon = base64.b64decode(icon) - self._set_icon(icon) - self._icon_cache.add_icon(icon) - - if (result_status == network.RESULT_FAILED or not icon) and self._icon_tries < 3: - self._icon_tries = self._icon_tries + 1 - if self._icon_tries >= 3: - logging.debug("Failed to retrieve buddy icon for '%s'." % self._nick_name) - gobject.timeout_add(1000, self._get_buddy_icon, service, True) - return False - - def _get_buddy_icon(self, service, retry=False): - """Get the buddy's icon. Check the cache first, if its - not there get the icon from the buddy over the network.""" - if retry != True: - # Only hit the cache once - icon_hash = service.get_one_property('icon-hash') - if icon_hash is not None: - icon = self._icon_cache.get_icon(icon_hash) - if icon: - logging.debug("%s: icon cache hit for %s." % (self._nick_name, icon_hash)) - self._set_icon(icon) - return False - logging.debug("%s: icon cache miss, fetching icon from buddy..." % self._nick_name) - - from sugar.p2p import Stream - buddy_stream = Stream.Stream.new_from_service(service, start_reader=False) - writer = buddy_stream.new_writer(service) - success = writer.custom_request("get_buddy_icon", self._request_buddy_icon_cb, service) - if not success: - del writer, buddy_stream - gobject.timeout_add(1000, self._get_buddy_icon, service, True) - return False - - def _get_service_key(self, service): - return (service.get_type(), service.get_activity_id()) - - def add_service(self, service): - """Adds a new service to this buddy's service list, returning - True if the service was successfully added, and False if it was not.""" - if service.get_name() != self._nick_name: - logging.error("Service and buddy nick names doesn't match: " \ - "%s %s" % (service.get_name(), self._nick_name)) - return False - - source_addr = service.get_source_address() - if source_addr != self._address: - logging.error("Service source and buddy address doesn't " \ - "match: %s %s" % (source_addr, self._address)) - return False - return self._internal_add_service(service) - - def _internal_add_service(self, service): - service_key = self._get_service_key(service) - if service_key in self._services.keys(): - logging.error("Service already known: %s %s" % (service_key[0], - service_key[1])) - return False - - if service.get_type() == PRESENCE_SERVICE_TYPE and self._buddy_presence_service: - # already have a presence service for this buddy - logging.debug("!!! Tried to add a buddy presence service when " \ - "one already existed.") - return False - - logging.debug("Buddy %s added service type %s id %s" % (self._nick_name, - service.get_type(), service.get_activity_id())) - self._services[service_key] = service - service.set_owner(self) - - if service.get_type() == PRESENCE_SERVICE_TYPE: - self._buddy_presence_service = service - # A buddy isn't valid until its official presence - # service has been found and resolved - self._valid = True - self._get_buddy_icon(service) - self._color = service.get_one_property(_BUDDY_KEY_COLOR) - self._current_activity = service.get_one_property(_BUDDY_KEY_CURACT) - # Monitor further buddy property changes, like current activity - # and color - service.connect('property-changed', - self.__buddy_presence_service_property_changed_cb) - - if self._valid: - self._dbus_helper.ServiceAppeared(service.object_path()) - return True - - def __buddy_presence_service_property_changed_cb(self, service, keys): - if _BUDDY_KEY_COLOR in keys: - new_color = service.get_one_property(_BUDDY_KEY_COLOR) - if new_color and self._color != new_color: - self._color = new_color - self._dbus_helper.PropertyChanged([_BUDDY_KEY_COLOR]) - if _BUDDY_KEY_CURACT in keys: - # Three cases here: - # 1) Buddy didn't publish a 'curact' key at all; we do nothing - # 2) Buddy published a blank/zero-length 'curact' key; we send - # a current-activity-changed signal for no activity - # 3) Buddy published a non-zero-length 'curact' key; we send - # a current-activity-changed signal if we know about the - # activity already, if not we postpone until the activity - # is found on the network and added to the buddy - new_curact = service.get_one_property(_BUDDY_KEY_CURACT) - if new_curact and self._current_activity != new_curact: - if not len(new_curact): - new_curact = None - self._current_activity = new_curact - if self._activities.has_key(self._current_activity): - # Case (3) above, valid activity id - activity = self._activities[self._current_activity] - if activity.is_valid(): - self._dbus_helper.CurrentActivityChanged([activity.object_path()]) - elif not self._current_activity: - # Case (2) above, no current activity - self._dbus_helper.CurrentActivityChanged([]) - - def __find_service_by_activity_id(self, actid): - for serv in self._services.values(): - if serv.get_activity_id() == actid: - return serv - return None - - def add_activity(self, activity): - if activity in self._activities.values(): - return - actid = activity.get_id() - if not self.__find_service_by_activity_id(actid): - raise RuntimeError("Tried to add activity for which we had no service") - self._activities[actid] = activity - if activity.is_valid(): - self._dbus_helper.JoinedActivity(activity.object_path()) - - # If when we received a current activity update from the buddy, - # but didn't know about that activity yet, and now we do know about - # it, we need to send out the changed activity signal - if actid == self._current_activity: - self._dbus_helper.CurrentActivityChanged([activity.object_path()]) - - def remove_service(self, service): - """Remove a service from a buddy; ie, the activity was closed - or the buddy went away.""" - if service.get_source_address() != self._address: - return - if service.get_name() != self._nick_name: - return - - if service.get_type() == PRESENCE_SERVICE_TYPE \ - and self._buddy_presence_service \ - and service != self._buddy_presence_service: - logging.debug("!!! Tried to remove a spurious buddy presence service.") - return - - service_key = self._get_service_key(service) - if self._services.has_key(service_key): - if self._valid: - self._dbus_helper.ServiceDisappeared(service.object_path()) - del self._services[service_key] - - if service.get_type() == PRESENCE_SERVICE_TYPE: - self._valid = False - self._dbus_helper.Disappeared() - - def remove_activity(self, activity): - actid = activity.get_id() - if not self._activities.has_key(actid): - return - del self._activities[actid] - if activity.is_valid(): - self._dbus_helper.LeftActivity(activity.object_path()) - - # If we just removed the buddy's current activity, - # send out a signal - if actid == self._current_activity: - self._current_activity = None - self._dbus_helper.CurrentActivityChanged([]) - - def get_joined_activities(self): - acts = [] - for act in self._activities.values(): - if act.is_valid(): - acts.append(act) - return acts - - def get_service_of_type(self, stype, activity=None): - """Return a service of a certain type, or None if the buddy - doesn't provide that service.""" - if not stype: - raise RuntimeError("Need to specify a service type.") - - if activity and not activity.is_valid(): - raise RuntimeError("Activity is not yet valid.") - - if activity: - key = (stype, activity.get_id()) - else: - key = (stype, None) - if self._services.has_key(key): - return self._services[key] - return None - - def is_valid(self): - """Return whether the buddy is valid or not. A buddy is - not valid until its official presence service has been found - and successfully resolved.""" - return self._valid - - def get_icon(self): - """Return the buddies icon, if any.""" - return self._icon - - def get_address(self): - return self._address - - def get_name(self): - return self._nick_name - - 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._dbus_helper.IconChanged() - - def is_owner(self): - return False + """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, service, icon_cache): + 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") + # Normal Buddy objects must be created with a valid service, + # owner objects do not + if not isinstance(self, Owner): + if not isinstance(service, Service.Service): + raise ValueError("service must be a valid service object") + + self._services = {} + self._activities = {} + + self._icon_cache = icon_cache + + self._nick_name = None + self._address = None + if service is not None: + self._nick_name = service.get_name() + self._address = service.get_source_address() + self._color = None + self._current_activity = None + self._valid = False + self._icon = None + self._icon_tries = 0 + + self._object_id = object_id + self._object_path = BUDDY_DBUS_OBJECT_PATH + str(self._object_id) + self._dbus_helper = BuddyDBusHelper(self, bus_name, self._object_path) + + self._buddy_presence_service = None + if service is not None: + self.add_service(service) + + def object_path(self): + return dbus.ObjectPath(self._object_path) + + def _request_buddy_icon_cb(self, result_status, response, user_data): + """Callback when icon request has completed.""" + from sugar.p2p import network + icon = response + service = user_data + if result_status == network.RESULT_SUCCESS: + if icon and len(icon): + icon = base64.b64decode(icon) + self._set_icon(icon) + self._icon_cache.add_icon(icon) + + if (result_status == network.RESULT_FAILED or not icon) and self._icon_tries < 3: + self._icon_tries = self._icon_tries + 1 + if self._icon_tries >= 3: + logging.debug("Failed to retrieve buddy icon for '%s'." % self._nick_name) + gobject.timeout_add(1000, self._get_buddy_icon, service, True) + return False + + def _get_buddy_icon(self, service, retry=False): + """Get the buddy's icon. Check the cache first, if its + not there get the icon from the buddy over the network.""" + if retry != True: + # Only hit the cache once + icon_hash = service.get_one_property('icon-hash') + if icon_hash is not None: + icon = self._icon_cache.get_icon(icon_hash) + if icon: + logging.debug("%s: icon cache hit for %s." % (self._nick_name, icon_hash)) + self._set_icon(icon) + return False + logging.debug("%s: icon cache miss, fetching icon from buddy..." % self._nick_name) + + from sugar.p2p import Stream + buddy_stream = Stream.Stream.new_from_service(service, start_reader=False) + writer = buddy_stream.new_writer(service) + success = writer.custom_request("get_buddy_icon", self._request_buddy_icon_cb, service) + if not success: + del writer, buddy_stream + gobject.timeout_add(1000, self._get_buddy_icon, service, True) + return False + + def _get_service_key(self, service): + return (service.get_type(), service.get_activity_id()) + + def add_service(self, service): + """Adds a new service to this buddy's service list, returning + True if the service was successfully added, and False if it was not.""" + if service.get_name() != self._nick_name: + logging.error("Service and buddy nick names doesn't match: " \ + "%s %s" % (service.get_name(), self._nick_name)) + return False + + source_addr = service.get_source_address() + if source_addr != self._address: + logging.error("Service source and buddy address doesn't " \ + "match: %s %s" % (source_addr, self._address)) + return False + return self._internal_add_service(service) + + def _internal_add_service(self, service): + service_key = self._get_service_key(service) + if service_key in self._services.keys(): + logging.error("Service already known: %s %s" % (service_key[0], + service_key[1])) + return False + + if service.get_type() == PRESENCE_SERVICE_TYPE and self._buddy_presence_service: + # already have a presence service for this buddy + logging.debug("!!! Tried to add a buddy presence service when " \ + "one already existed.") + return False + + logging.debug("Buddy %s added service type %s id %s" % (self._nick_name, + service.get_type(), service.get_activity_id())) + self._services[service_key] = service + service.set_owner(self) + + if service.get_type() == PRESENCE_SERVICE_TYPE: + self._buddy_presence_service = service + # A buddy isn't valid until its official presence + # service has been found and resolved + self._valid = True + self._get_buddy_icon(service) + self._color = service.get_one_property(_BUDDY_KEY_COLOR) + self._current_activity = service.get_one_property(_BUDDY_KEY_CURACT) + # Monitor further buddy property changes, like current activity + # and color + service.connect('property-changed', + self.__buddy_presence_service_property_changed_cb) + + if self._valid: + self._dbus_helper.ServiceAppeared(service.object_path()) + return True + + def __buddy_presence_service_property_changed_cb(self, service, keys): + if _BUDDY_KEY_COLOR in keys: + new_color = service.get_one_property(_BUDDY_KEY_COLOR) + if new_color and self._color != new_color: + self._color = new_color + self._dbus_helper.PropertyChanged([_BUDDY_KEY_COLOR]) + if _BUDDY_KEY_CURACT in keys: + # Three cases here: + # 1) Buddy didn't publish a 'curact' key at all; we do nothing + # 2) Buddy published a blank/zero-length 'curact' key; we send + # a current-activity-changed signal for no activity + # 3) Buddy published a non-zero-length 'curact' key; we send + # a current-activity-changed signal if we know about the + # activity already, if not we postpone until the activity + # is found on the network and added to the buddy + new_curact = service.get_one_property(_BUDDY_KEY_CURACT) + if new_curact and self._current_activity != new_curact: + if not len(new_curact): + new_curact = None + self._current_activity = new_curact + if self._activities.has_key(self._current_activity): + # Case (3) above, valid activity id + activity = self._activities[self._current_activity] + if activity.is_valid(): + self._dbus_helper.CurrentActivityChanged([activity.object_path()]) + elif not self._current_activity: + # Case (2) above, no current activity + self._dbus_helper.CurrentActivityChanged([]) + + def __find_service_by_activity_id(self, actid): + for serv in self._services.values(): + if serv.get_activity_id() == actid: + return serv + return None + + def add_activity(self, activity): + if activity in self._activities.values(): + return + actid = activity.get_id() + if not self.__find_service_by_activity_id(actid): + raise RuntimeError("Tried to add activity for which we had no service") + self._activities[actid] = activity + if activity.is_valid(): + self._dbus_helper.JoinedActivity(activity.object_path()) + + # If when we received a current activity update from the buddy, + # but didn't know about that activity yet, and now we do know about + # it, we need to send out the changed activity signal + if actid == self._current_activity: + self._dbus_helper.CurrentActivityChanged([activity.object_path()]) + + def remove_service(self, service): + """Remove a service from a buddy; ie, the activity was closed + or the buddy went away.""" + if service.get_source_address() != self._address: + return + if service.get_name() != self._nick_name: + return + + if service.get_type() == PRESENCE_SERVICE_TYPE \ + and self._buddy_presence_service \ + and service != self._buddy_presence_service: + logging.debug("!!! Tried to remove a spurious buddy presence service.") + return + + service_key = self._get_service_key(service) + if self._services.has_key(service_key): + if self._valid: + self._dbus_helper.ServiceDisappeared(service.object_path()) + del self._services[service_key] + + if service.get_type() == PRESENCE_SERVICE_TYPE: + self._valid = False + self._dbus_helper.Disappeared() + + def remove_activity(self, activity): + actid = activity.get_id() + if not self._activities.has_key(actid): + return + del self._activities[actid] + if activity.is_valid(): + self._dbus_helper.LeftActivity(activity.object_path()) + + # If we just removed the buddy's current activity, + # send out a signal + if actid == self._current_activity: + self._current_activity = None + self._dbus_helper.CurrentActivityChanged([]) + + def get_joined_activities(self): + acts = [] + for act in self._activities.values(): + if act.is_valid(): + acts.append(act) + return acts + + def get_service_of_type(self, stype, activity=None): + """Return a service of a certain type, or None if the buddy + doesn't provide that service.""" + if not stype: + raise RuntimeError("Need to specify a service type.") + + if activity and not activity.is_valid(): + raise RuntimeError("Activity is not yet valid.") + + if activity: + key = (stype, activity.get_id()) + else: + key = (stype, None) + if self._services.has_key(key): + return self._services[key] + return None + + def is_valid(self): + """Return whether the buddy is valid or not. A buddy is + not valid until its official presence service has been found + and successfully resolved.""" + return self._valid + + def get_icon(self): + """Return the buddies icon, if any.""" + return self._icon + + def get_address(self): + return self._address + + def get_name(self): + return self._nick_name + + 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._dbus_helper.IconChanged() + + def is_owner(self): + return False 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, icon_cache): - Buddy.__init__(self, bus_name, object_id, None, icon_cache) - self._nick_name = profile.get_nick_name() - self._color = profile.get_color() - self._ps = ps - - def add_service(self, service): - """Adds a new service to this buddy's service list, returning - True if the service was successfully added, and False if it was not.""" - if service.get_name() != self._nick_name: - logging.error("Service and buddy nick names doesn't match: " \ - "%s %s" % (service.get_name(), self._nick_name)) - return False - - # The Owner initially doesn't have an address, so the first - # service added to the Owner determines the owner's address - source_addr = service.get_source_address() - if self._address is None and service.is_local(): - self._address = source_addr - self._dbus_helper.PropertyChanged(['ip4_address']) - - # The owner bypasses address checks and only cares if - # avahi says the service is a local service - if not service.is_local(): - logging.error("Cannot add remote service to owner object.") - return False - - logging.debug("Adding owner service %s.%s at %s:%d." % (service.get_name(), - service.get_type(), service.get_source_address(), - service.get_port())) - return self._internal_add_service(service) - - def is_owner(self): - return True + """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, icon_cache): + Buddy.__init__(self, bus_name, object_id, None, icon_cache) + self._nick_name = profile.get_nick_name() + self._color = profile.get_color() + self._ps = ps + + def add_service(self, service): + """Adds a new service to this buddy's service list, returning + True if the service was successfully added, and False if it was not.""" + if service.get_name() != self._nick_name: + logging.error("Service and buddy nick names doesn't match: " \ + "%s %s" % (service.get_name(), self._nick_name)) + return False + + # The Owner initially doesn't have an address, so the first + # service added to the Owner determines the owner's address + source_addr = service.get_source_address() + if self._address is None and service.is_local(): + self._address = source_addr + self._dbus_helper.PropertyChanged(['ip4_address']) + + # The owner bypasses address checks and only cares if + # avahi says the service is a local service + if not service.is_local(): + logging.error("Cannot add remote service to owner object.") + return False + + logging.debug("Adding owner service %s.%s at %s:%d." % (service.get_name(), + service.get_type(), service.get_source_address(), + service.get_port())) + return self._internal_add_service(service) + + def is_owner(self): + return True ################################################################# @@ -468,62 +468,62 @@ import Service __objid_seq = 0 def _next_objid(): - global __objid_seq - __objid_seq = __objid_seq + 1 - return __objid_seq + global __objid_seq + __objid_seq = __objid_seq + 1 + return __objid_seq class BuddyTestCase(unittest.TestCase): - _DEF_NAME = u"Tommy" - _DEF_STYPE = unicode(PRESENCE_SERVICE_TYPE) - _DEF_DOMAIN = u"local" - _DEF_ADDRESS = u"1.1.1.1" - _DEF_PORT = 1234 - - def __init__(self, name): - self._bus = dbus.SessionBus() - self._bus_name = dbus.service.BusName('org.laptop.Presence', bus=self._bus) - unittest.TestCase.__init__(self, name) - - def __del__(self): - del self._bus_name - del self._bus - - def _test_init_fail(self, service, fail_msg): - """Test something we expect to fail.""" - try: - objid = _next_objid() - buddy = Buddy(self._bus_name, objid, service, owner=False) - except ValueError, exc: - pass - else: - self.fail("expected a ValueError for %s." % fail_msg) - - def testService(self): - service = None - self._test_init_fail(service, "invalid service") - - def testGoodInit(self): - objid = _next_objid() - service = Service.Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, - self._DEF_ADDRESS, self._DEF_PORT) - objid = _next_objid() - buddy = Buddy(self._bus_name, objid, service) - assert buddy.get_name() == self._DEF_NAME, "buddy name wasn't correct after init." - assert buddy.get_address() == self._DEF_ADDRESS, "buddy address wasn't correct after init." - assert buddy.object_path() == BUDDY_DBUS_OBJECT_PATH + str(objid) - - def addToSuite(suite): - suite.addTest(BuddyTestCase("testService")) - suite.addTest(BuddyTestCase("testGoodInit")) - addToSuite = staticmethod(addToSuite) + _DEF_NAME = u"Tommy" + _DEF_STYPE = unicode(PRESENCE_SERVICE_TYPE) + _DEF_DOMAIN = u"local" + _DEF_ADDRESS = u"1.1.1.1" + _DEF_PORT = 1234 + + def __init__(self, name): + self._bus = dbus.SessionBus() + self._bus_name = dbus.service.BusName('org.laptop.Presence', bus=self._bus) + unittest.TestCase.__init__(self, name) + + def __del__(self): + del self._bus_name + del self._bus + + def _test_init_fail(self, service, fail_msg): + """Test something we expect to fail.""" + try: + objid = _next_objid() + buddy = Buddy(self._bus_name, objid, service, owner=False) + except ValueError, exc: + pass + else: + self.fail("expected a ValueError for %s." % fail_msg) + + def testService(self): + service = None + self._test_init_fail(service, "invalid service") + + def testGoodInit(self): + objid = _next_objid() + service = Service.Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, + self._DEF_ADDRESS, self._DEF_PORT) + objid = _next_objid() + buddy = Buddy(self._bus_name, objid, service) + assert buddy.get_name() == self._DEF_NAME, "buddy name wasn't correct after init." + assert buddy.get_address() == self._DEF_ADDRESS, "buddy address wasn't correct after init." + assert buddy.object_path() == BUDDY_DBUS_OBJECT_PATH + str(objid) + + def addToSuite(suite): + suite.addTest(BuddyTestCase("testService")) + suite.addTest(BuddyTestCase("testGoodInit")) + addToSuite = staticmethod(addToSuite) def main(): - suite = unittest.TestSuite() - BuddyTestCase.addToSuite(suite) - runner = unittest.TextTestRunner() - runner.run(suite) + suite = unittest.TestSuite() + BuddyTestCase.addToSuite(suite) + runner = unittest.TextTestRunner() + runner.run(suite) if __name__ == "__main__": - main() + main() diff --git a/services/presence/BuddyIconCache.py b/services/presence/BuddyIconCache.py index 643116f..7f9952a 100644 --- a/services/presence/BuddyIconCache.py +++ b/services/presence/BuddyIconCache.py @@ -19,59 +19,59 @@ from sugar import env from sugar import util class BuddyIconCache(object): - """Caches icons on disk and finds them based on md5 hash.""" - def __init__(self): - ppath = env.get_profile_path() - self._cachepath = os.path.join(ppath, "cache", "buddy-icons") - if not os.path.exists(self._cachepath): - os.makedirs(self._cachepath) + """Caches icons on disk and finds them based on md5 hash.""" + def __init__(self): + ppath = env.get_profile_path() + self._cachepath = os.path.join(ppath, "cache", "buddy-icons") + if not os.path.exists(self._cachepath): + os.makedirs(self._cachepath) - self._cache = {} + self._cache = {} - # Read all cached icons and their sums - for fname in os.listdir(self._cachepath): - m = md5.new() - data = self._get_icon_data(fname) - if len(data) == 0: - continue - m.update(data) - printable_hash = util.printable_hash(m.digest()) - self._cache[printable_hash] = fname - del m + # Read all cached icons and their sums + for fname in os.listdir(self._cachepath): + m = md5.new() + data = self._get_icon_data(fname) + if len(data) == 0: + continue + m.update(data) + printable_hash = util.printable_hash(m.digest()) + self._cache[printable_hash] = fname + del m - def _get_icon_data(self, fname): - fd = open(os.path.join(self._cachepath, fname), "r") - data = fd.read() - fd.close() - del fd - return data + def _get_icon_data(self, fname): + fd = open(os.path.join(self._cachepath, fname), "r") + data = fd.read() + fd.close() + del fd + return data - def get_icon(self, printable_hash): - if not isinstance(printable_hash, unicode): - raise RuntimeError("printable_hash must be a unicode string.") - try: - fname = self._cache[printable_hash] - return self._get_icon_data(fname) - except KeyError: - pass - return None + def get_icon(self, printable_hash): + if not isinstance(printable_hash, unicode): + raise RuntimeError("printable_hash must be a unicode string.") + try: + fname = self._cache[printable_hash] + return self._get_icon_data(fname) + except KeyError: + pass + return None - def add_icon(self, icon_data): - if len(icon_data) == 0: - return + def add_icon(self, icon_data): + if len(icon_data) == 0: + return - m = md5.new() - m.update(icon_data) - printable_hash = util.printable_hash(m.digest()) - if self._cache.has_key(printable_hash): - del m - return + m = md5.new() + m.update(icon_data) + printable_hash = util.printable_hash(m.digest()) + if self._cache.has_key(printable_hash): + del m + return - # Write the icon to disk and add an entry to our cache for it - m.update(time.asctime()) - fname = util.printable_hash(m.digest()) - fd = open(os.path.join(self._cachepath, fname), "w") - fd.write(icon_data) - fd.close() - self._cache[printable_hash] = fname - del m + # Write the icon to disk and add an entry to our cache for it + m.update(time.asctime()) + fname = util.printable_hash(m.digest()) + fd = open(os.path.join(self._cachepath, fname), "w") + fd.write(icon_data) + fd.close() + self._cache[printable_hash] = fname + del m diff --git a/services/presence/PresenceService.py b/services/presence/PresenceService.py index 6399a7b..5f8c7a4 100644 --- a/services/presence/PresenceService.py +++ b/services/presence/PresenceService.py @@ -30,103 +30,103 @@ _SA_UNRESOLVED = 0 _SA_RESOLVE_PENDING = 1 _SA_RESOLVED = 2 class ServiceAdv(object): - """Wrapper class to track services from Avahi.""" - def __init__(self, interface, protocol, name, stype, domain, local): - self._interface = interface - self._protocol = protocol - if not isinstance(name, unicode): - raise ValueError("service advertisement name must be unicode.") - self._name = name - if not isinstance(stype, unicode): - raise ValueError("service advertisement type must be unicode.") - self._stype = stype - if not isinstance(domain, unicode): - raise ValueError("service advertisement domain must be unicode.") - self._domain = domain - self._service = None - if not isinstance(local, bool): - raise ValueError("local must be a bool.") - self._local = local - self._state = _SA_UNRESOLVED - self._resolver = None - self._resolv_tries = 0 - - def __del__(self): - if self._resolver: - del self._resolver - - def interface(self): - return self._interface - def protocol(self): - return self._protocol - def name(self): - return self._name - def stype(self): - return self._stype - def domain(self): - return self._domain - def is_local(self): - return self._local - def resolv_tries(self): - return self._resolv_tries - def inc_resolv_tries(self): - self._resolv_tries += 1 - def service(self): - return self._service - def set_service(self, service): - if not isinstance(service, Service.Service): - raise ValueError("must be a valid service.") - if service != self._service: - self._service = service - def resolver(self): - return self._resolver - def set_resolver(self, resolver): - if resolver and not isinstance(resolver, dbus.Interface): - raise ValueError("'resolver' must be a valid dbus object") - if not resolver and self._resolver: - del self._resolver - self._resolver = resolver - def state(self): - return self._state - def set_state(self, state): - if state == _SA_RESOLVE_PENDING: - if self._state == _SA_RESOLVED: - raise ValueError("Can't reset to resolve pending from resolved.") - if state == _SA_UNRESOLVED: - self._resolv_tries = 0 - self._state = state + """Wrapper class to track services from Avahi.""" + def __init__(self, interface, protocol, name, stype, domain, local): + self._interface = interface + self._protocol = protocol + if not isinstance(name, unicode): + raise ValueError("service advertisement name must be unicode.") + self._name = name + if not isinstance(stype, unicode): + raise ValueError("service advertisement type must be unicode.") + self._stype = stype + if not isinstance(domain, unicode): + raise ValueError("service advertisement domain must be unicode.") + self._domain = domain + self._service = None + if not isinstance(local, bool): + raise ValueError("local must be a bool.") + self._local = local + self._state = _SA_UNRESOLVED + self._resolver = None + self._resolv_tries = 0 + + def __del__(self): + if self._resolver: + del self._resolver + + def interface(self): + return self._interface + def protocol(self): + return self._protocol + def name(self): + return self._name + def stype(self): + return self._stype + def domain(self): + return self._domain + def is_local(self): + return self._local + def resolv_tries(self): + return self._resolv_tries + def inc_resolv_tries(self): + self._resolv_tries += 1 + def service(self): + return self._service + def set_service(self, service): + if not isinstance(service, Service.Service): + raise ValueError("must be a valid service.") + if service != self._service: + self._service = service + def resolver(self): + return self._resolver + def set_resolver(self, resolver): + if resolver and not isinstance(resolver, dbus.Interface): + raise ValueError("'resolver' must be a valid dbus object") + if not resolver and self._resolver: + del self._resolver + self._resolver = resolver + def state(self): + return self._state + def set_state(self, state): + if state == _SA_RESOLVE_PENDING: + if self._state == _SA_RESOLVED: + raise ValueError("Can't reset to resolve pending from resolved.") + if state == _SA_UNRESOLVED: + self._resolv_tries = 0 + self._state = state class RegisteredServiceType(object): - def __init__(self, stype): - self._stype = stype - self._refcount = 1 + def __init__(self, stype): + self._stype = stype + self._refcount = 1 - def get_type(self): - return self._stype + def get_type(self): + return self._stype - def ref(self): - self._refcount += 1 + def ref(self): + self._refcount += 1 - def unref(self): - self._refcount -= 1 - return self._refcount + def unref(self): + self._refcount -= 1 + return self._refcount def _txt_to_dict(txt): - """Convert an avahi-returned TXT record formatted - as nested arrays of integers (from dbus) into a dict - of key/value string pairs.""" - prop_dict = {} - props = avahi.txt_array_to_string_array(txt) - for item in props: - key = value = None - if '=' not in item: - # No = means a boolean value of true - key = item - value = True - else: - (key, value) = item.split('=', 1) - prop_dict[key] = value - return prop_dict + """Convert an avahi-returned TXT record formatted + as nested arrays of integers (from dbus) into a dict + of key/value string pairs.""" + prop_dict = {} + props = avahi.txt_array_to_string_array(txt) + for item in props: + key = value = None + if '=' not in item: + # No = means a boolean value of true + key = item + value = True + else: + (key, value) = item.split('=', 1) + prop_dict[key] = value + return prop_dict _PRESENCE_SERVICE = "org.laptop.Presence" @@ -134,731 +134,731 @@ _PRESENCE_DBUS_INTERFACE = "org.laptop.Presence" _PRESENCE_OBJECT_PATH = "/org/laptop/Presence" class NotFoundError(Exception): - pass + pass class PresenceServiceDBusHelper(dbus.service.Object): - def __init__(self, parent, bus_name): - self._parent = parent - self._bus_name = bus_name - dbus.service.Object.__init__(self, bus_name, _PRESENCE_OBJECT_PATH) - - @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, - signature="o") - def BuddyAppeared(self, object_path): - pass - - @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, - signature="o") - def BuddyDisappeared(self, object_path): - pass - - @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, - signature="o") - def ServiceAppeared(self, object_path): - pass - - @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, - signature="o") - def ServiceDisappeared(self, object_path): - pass - - @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, - signature="o") - def ActivityAppeared(self, object_path): - pass - - @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, - signature="o") - def ActivityDisappeared(self, object_path): - pass - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="", out_signature="ao") - def getServices(self): - ret = [] - for serv in self._parent.get_services(): - ret.append(serv.object_path()) - return ret - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="s", out_signature="ao") - def getServicesOfType(self, stype): - ret = [] - for serv in self._parent.get_services_of_type(stype): - ret.append(serv.object_path()) - return ret - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="", out_signature="ao") - def getActivities(self): - ret = [] - for act in self._parent.get_activities(): - ret.append(act.object_path()) - return ret - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="s", out_signature="o") - def getActivity(self, actid): - act = self._parent.get_activity(actid) - if not act: - raise NotFoundError("Not found") - return act.object_path() - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="", out_signature="ao") - def getBuddies(self): - ret = [] - for buddy in self._parent.get_buddies(): - ret.append(buddy.object_path()) - return ret - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="s", out_signature="o") - def getBuddyByName(self, name): - buddy = self._parent.get_buddy_by_name(name) - if not buddy: - raise NotFoundError("Not found") - return buddy.object_path() - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="s", out_signature="o") - def getBuddyByAddress(self, addr): - buddy = self._parent.get_buddy_by_address(addr) - if not buddy: - raise NotFoundError("Not found") - return buddy.object_path() - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="", out_signature="o") - def getOwner(self): - owner = self._parent.get_owner() - if not owner: - raise NotFoundError("Not found") - return owner.object_path() - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="os", out_signature="o", - sender_keyword="sender") - def joinActivity(self, activity_op, stype, sender): - found_activity = None - acts = self._parent.get_activities() - for act in acts: - if act.object_path() == activity_op: - found_activity = act - break - if not found_activity: - raise NotFoundError("The activity %s was not found." % activity_op) - return self._parent.join_activity(found_activity, stype, sender) - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="ssa{ss}sis", out_signature="o", - sender_keyword="sender") - def shareActivity(self, activity_id, stype, properties, address, port, - domain, sender=None): - if not len(address): - address = None - service = self._parent.share_activity(activity_id, stype, properties, address, - port, domain, sender) - return service.object_path() - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="ssa{ss}sis", out_signature="o", - sender_keyword="sender") - def registerService(self, name, stype, properties, address, port, domain, - sender=None): - if not len(address): - address = None - service = self._parent.register_service(name, stype, properties, address, - port, domain, sender) - return service.object_path() - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="o", out_signature="", - sender_keyword="sender") - def unregisterService(self, service_op, sender): - found_serv = None - services = self._parent.get_services() - for serv in services: - if serv.object_path() == service_op: - found_serv = serv - break - if not found_serv: - raise NotFoundError("The service %s was not found." % service_op) - return self._parent.unregister_service(found_serv, sender) - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="s", out_signature="") - def registerServiceType(self, stype): - self._parent.register_service_type(stype) - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE, - in_signature="s", out_signature="") - def unregisterServiceType(self, stype): - self._parent.unregister_service_type(stype) - - @dbus.service.method(_PRESENCE_DBUS_INTERFACE) - def start(self): - self._parent.start() + def __init__(self, parent, bus_name): + self._parent = parent + self._bus_name = bus_name + dbus.service.Object.__init__(self, bus_name, _PRESENCE_OBJECT_PATH) + + @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, + signature="o") + def BuddyAppeared(self, object_path): + pass + + @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, + signature="o") + def BuddyDisappeared(self, object_path): + pass + + @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, + signature="o") + def ServiceAppeared(self, object_path): + pass + + @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, + signature="o") + def ServiceDisappeared(self, object_path): + pass + + @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, + signature="o") + def ActivityAppeared(self, object_path): + pass + + @dbus.service.signal(_PRESENCE_DBUS_INTERFACE, + signature="o") + def ActivityDisappeared(self, object_path): + pass + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="", out_signature="ao") + def getServices(self): + ret = [] + for serv in self._parent.get_services(): + ret.append(serv.object_path()) + return ret + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="s", out_signature="ao") + def getServicesOfType(self, stype): + ret = [] + for serv in self._parent.get_services_of_type(stype): + ret.append(serv.object_path()) + return ret + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="", out_signature="ao") + def getActivities(self): + ret = [] + for act in self._parent.get_activities(): + ret.append(act.object_path()) + return ret + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="s", out_signature="o") + def getActivity(self, actid): + act = self._parent.get_activity(actid) + if not act: + raise NotFoundError("Not found") + return act.object_path() + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="", out_signature="ao") + def getBuddies(self): + ret = [] + for buddy in self._parent.get_buddies(): + ret.append(buddy.object_path()) + return ret + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="s", out_signature="o") + def getBuddyByName(self, name): + buddy = self._parent.get_buddy_by_name(name) + if not buddy: + raise NotFoundError("Not found") + return buddy.object_path() + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="s", out_signature="o") + def getBuddyByAddress(self, addr): + buddy = self._parent.get_buddy_by_address(addr) + if not buddy: + raise NotFoundError("Not found") + return buddy.object_path() + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="", out_signature="o") + def getOwner(self): + owner = self._parent.get_owner() + if not owner: + raise NotFoundError("Not found") + return owner.object_path() + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="os", out_signature="o", + sender_keyword="sender") + def joinActivity(self, activity_op, stype, sender): + found_activity = None + acts = self._parent.get_activities() + for act in acts: + if act.object_path() == activity_op: + found_activity = act + break + if not found_activity: + raise NotFoundError("The activity %s was not found." % activity_op) + return self._parent.join_activity(found_activity, stype, sender) + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="ssa{ss}sis", out_signature="o", + sender_keyword="sender") + def shareActivity(self, activity_id, stype, properties, address, port, + domain, sender=None): + if not len(address): + address = None + service = self._parent.share_activity(activity_id, stype, properties, address, + port, domain, sender) + return service.object_path() + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="ssa{ss}sis", out_signature="o", + sender_keyword="sender") + def registerService(self, name, stype, properties, address, port, domain, + sender=None): + if not len(address): + address = None + service = self._parent.register_service(name, stype, properties, address, + port, domain, sender) + return service.object_path() + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="o", out_signature="", + sender_keyword="sender") + def unregisterService(self, service_op, sender): + found_serv = None + services = self._parent.get_services() + for serv in services: + if serv.object_path() == service_op: + found_serv = serv + break + if not found_serv: + raise NotFoundError("The service %s was not found." % service_op) + return self._parent.unregister_service(found_serv, sender) + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="s", out_signature="") + def registerServiceType(self, stype): + self._parent.register_service_type(stype) + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE, + in_signature="s", out_signature="") + def unregisterServiceType(self, stype): + self._parent.unregister_service_type(stype) + + @dbus.service.method(_PRESENCE_DBUS_INTERFACE) + def start(self): + self._parent.start() class PresenceService(object): - def __init__(self): - # interface -> IP address: interfaces we've gotten events on so far - self._started = False - self._local_addrs = {} - - self._next_object_id = 0 - - self._buddies = {} # nick -> Buddy - self._services = {} # (name, type) -> Service - self._activities = {} # activity id -> Activity - - # Keep track of stuff we're already browsing - self._service_type_browsers = {} - self._service_browsers = {} - - # Resolved service list - self._service_advs = [] - - # Service types we care about resolving - self._registered_service_types = [] - - # Set up the dbus service we provide - self._session_bus = dbus.SessionBus() - self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=self._session_bus) - self._dbus_helper = PresenceServiceDBusHelper(self, self._bus_name) - - self._icon_cache = BuddyIconCache.BuddyIconCache() - - # Our owner object - if profile.get_nick_name(): - objid = self._get_next_object_id() - self._owner = Buddy.Owner(self, self._bus_name, - objid, self._icon_cache) - self._buddies[self._owner.get_name()] = self._owner - else: - self._owner = None - - def start(self): - if self._started: - return - self._started = True - - # Connect to Avahi for mDNS stuff - self._system_bus = dbus.SystemBus() - self._mdns_service = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, - avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) - - # Always browse .local - self._new_domain_cb(avahi.IF_UNSPEC, avahi.PROTO_INET, "local") - - # Connect to Avahi and start looking for stuff - domain_browser = self._mdns_service.DomainBrowserNew(avahi.IF_UNSPEC, avahi.PROTO_INET, - "", avahi.DOMAIN_BROWSER_BROWSE, dbus.UInt32(0)) - db = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, domain_browser), avahi.DBUS_INTERFACE_DOMAIN_BROWSER) - db.connect_to_signal('ItemNew', self._new_domain_cb_glue) - - def _get_next_object_id(self): - """Increment and return the object ID counter.""" - self._next_object_id = self._next_object_id + 1 - return self._next_object_id - - def get_services(self): - return self._services.values() - - def get_services_of_type(self, stype): - ret = [] - for serv in self._services.values(): - if serv.get_type() == stype: - ret.append(serv) - return ret - - def get_activities(self): - # Only return valid activities - ret = [] - for act in self._activities.values(): - if act.is_valid(): - ret.append(act) - return ret - - def get_activity(self, actid): - if self._activities.has_key(actid): - act = self._activities[actid] - if act.is_valid(): - return act - return None - - def get_buddies(self): - buddies = [] - for buddy in self._buddies.values(): - if buddy.is_valid(): - buddies.append(buddy) - return buddies - - def get_buddy_by_name(self, name): - if self._buddies.has_key(name): - if self._buddies[name].is_valid(): - return self._buddies[name] - return None - - def get_buddy_by_address(self, address): - for buddy in self._buddies.values(): - if buddy.get_address() == address and buddy.is_valid(): - return buddy - return None - - def get_owner(self): - return self._owner - - def _find_service_adv(self, interface=None, protocol=None, name=None, - stype=None, domain=None, local=None): - """Search a list of service advertisements for ones matching - certain criteria.""" - adv_list = [] - for adv in self._service_advs: - if interface and adv.interface() != interface: - continue - if protocol and adv.protocol() != protocol: - continue - if name and adv.name() != name: - continue - if stype and adv.stype() != stype: - continue - if domain and adv.domain() != domain: - continue - if local is not None and adv.is_local() != local: - continue - adv_list.append(adv) - return adv_list - - def _find_registered_service_type(self, stype): - for item in self._registered_service_types: - if item.get_type() == stype: - return item - return None - - def _handle_new_service_for_buddy(self, service, local): - """Deal with a new discovered service object.""" - # Once a service is resolved, we match it up to an existing buddy, - # or create a new Buddy if this is the first service known about the buddy - buddy_was_valid = False - name = service.get_name() - buddy = None - try: - buddy = self._buddies[name] - buddy_was_valid = buddy.is_valid() - service_added = buddy.add_service(service) - if service_added: - self._dbus_helper.ServiceAppeared(service.object_path()) - except KeyError: - source_addr = service.get_source_address() - objid = self._get_next_object_id() - buddy = Buddy.Buddy(self._bus_name, objid, service, self._icon_cache) - self._buddies[name] = buddy - self._dbus_helper.ServiceAppeared(service.object_path()) - if not buddy_was_valid and buddy.is_valid(): - self._dbus_helper.BuddyAppeared(buddy.object_path()) - return buddy - - def _handle_new_activity_service(self, service): - # If the serivce is an activity service, merge it into our activities list - actid = service.get_activity_id() - if not actid: - return - activity = None - was_valid = False - if not self._activities.has_key(actid): - objid = self._get_next_object_id() - activity = Activity.Activity(self._bus_name, objid, service) - self._activities[actid] = activity - else: - activity = self._activities[actid] - was_valid = activity.is_valid() - - if activity: - activity.add_service(service) - - # Add the activity to its buddy - # FIXME: use something other than name to attribute to buddy - try: - buddy = self._buddies[service.get_name()] - buddy.add_activity(activity) - except KeyError: - pass - - if not was_valid and activity.is_valid(): - self._dbus_helper.ActivityAppeared(activity.object_path()) - - def _handle_remove_activity_service(self, service): - actid = service.get_activity_id() - if not actid: - return - if not self._activities.has_key(actid): - return - - activity = self._activities[actid] - - activity.remove_service(service) - if len(activity.get_services()) == 0: - # Remove the activity from its buddy - # FIXME: use something other than name to attribute to buddy - try: - buddy = self._buddies[service.get_name()] - buddy.remove_activity(activity) - except KeyError: - pass - - # Kill the activity - self._dbus_helper.ActivityDisappeared(activity.object_path()) - del self._activities[actid] - - def _service_resolved_cb(self, adv, interface, protocol, full_name, - stype, domain, host, aprotocol, address, port, txt, flags, - updated): - """When the service discovery finally gets here, we've got enough - information about the service to assign it to a buddy.""" - if updated == False: - logging.debug("Resolved service '%s' type '%s' domain '%s' to " \ - " %s:%s" % (full_name, stype, domain, address, port)) - - if not adv in self._service_advs: - return False - if adv.state() != _SA_RESOLVED: - return False - - # See if we know about this service already - service = None - key = (full_name, stype) - props = _txt_to_dict(txt) - if not self._services.has_key(key): - objid = self._get_next_object_id() - service = Service.Service(self._bus_name, objid, name=full_name, - stype=stype, domain=domain, address=address, port=port, - properties=props, source_address=address) - self._services[key] = service - else: - # Already tracking this service; either: - # a) we were the one that shared it in the first place, - # and therefore the source address would not have - # been set yet - # b) the service has been updated - service = self._services[key] - if not service.get_source_address(): - service.set_source_address(address) - if not service.get_address(): - service.set_address(address) - - adv.set_service(service) - - if service and updated: - service.set_properties(props, from_network=True) - return False - - # Merge the service into our buddy and activity lists, if needed - buddy = self._handle_new_service_for_buddy(service, adv.is_local()) - if buddy and service.get_activity_id(): - self._handle_new_activity_service(service) - - return False - - def _service_resolved_cb_glue(self, adv, interface, protocol, name, - stype, domain, host, aprotocol, address, port, txt, flags): - # Avahi doesn't flag updates to existing services, so we have - # to determine that here - updated = False - if adv.state() == _SA_RESOLVED: - updated = True - - adv.set_state(_SA_RESOLVED) - gobject.idle_add(self._service_resolved_cb, adv, interface, - protocol, name, stype, domain, host, aprotocol, address, - port, txt, flags, updated) - - def _service_resolved_failure_cb(self, adv, err): - retried = False - adv.set_resolver(None) - if adv.stype() == Buddy.PRESENCE_SERVICE_TYPE: - # Retry the presence service type a few times - if adv.resolv_tries() < 4: - adv.set_state(_SA_RESOLVE_PENDING) - gobject.timeout_add(250, self._resolve_service, adv) - retried = True - logging.error("Retrying resolution of service %s.%s: %s" % (adv.name(), - adv.stype(), err)) - - if not retried: - logging.error("Error resolving service %s.%s: %s" % (adv.name(), - adv.stype(), err)) - adv.set_state(_SA_UNRESOLVED) - - def _resolve_service(self, adv): - """Resolve and lookup a ZeroConf service to obtain its address and TXT records.""" - # Ask avahi to resolve this particular service - path = self._mdns_service.ServiceResolverNew(dbus.Int32(adv.interface()), - dbus.Int32(adv.protocol()), adv.name(), adv.stype(), adv.domain(), - avahi.PROTO_INET, dbus.UInt32(0)) - resolver = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, path), - avahi.DBUS_INTERFACE_SERVICE_RESOLVER) - resolver.connect_to_signal('Found', lambda *args: self._service_resolved_cb_glue(adv, *args)) - resolver.connect_to_signal('Failure', lambda *args: self._service_resolved_failure_cb(adv, *args)) - adv.inc_resolv_tries() - adv.set_resolver(resolver) - return False - - def _service_appeared_cb(self, interface, protocol, full_name, stype, domain, flags): - local = flags & avahi.LOOKUP_RESULT_OUR_OWN > 0 - adv_list = self._find_service_adv(interface=interface, protocol=protocol, - name=full_name, stype=stype, domain=domain, local=local) - adv = None - if not adv_list: - adv = ServiceAdv(interface=interface, protocol=protocol, name=full_name, - stype=stype, domain=domain, local=local) - self._service_advs.append(adv) - else: - adv = adv_list[0] - - # Decompose service name if we can - (actid, buddy_name) = Service.decompose_service_name(full_name) - - # If we care about the service right now, resolve it - resolve = False - item = self._find_registered_service_type(stype) - if actid is not None or item is not None: - resolve = True - if resolve and adv.state() == _SA_UNRESOLVED: - logging.debug("Found '%s' (%d) of type '%s' in domain" \ - " '%s' on %i.%i; will resolve." % (full_name, flags, stype, - domain, interface, protocol)) - adv.set_state(_SA_RESOLVE_PENDING) - gobject.idle_add(self._resolve_service, adv) - - return False - - def _service_appeared_cb_glue(self, interface, protocol, name, stype, domain, flags): - gobject.idle_add(self._service_appeared_cb, interface, protocol, name, stype, domain, flags) - - def _service_disappeared_cb(self, interface, protocol, full_name, stype, domain, flags): - local = flags & avahi.LOOKUP_RESULT_OUR_OWN > 0 - # If it's an unresolved service, remove it from our unresolved list - adv_list = self._find_service_adv(interface=interface, protocol=protocol, - name=full_name, stype=stype, domain=domain, local=local) - if not adv_list: - return False - - # Get the service object; if none, we have nothing left to do - adv = adv_list[0] - service = adv.service() - self._service_advs.remove(adv) - del adv - if not service: - return False - - logging.debug("Service %s.%s in domain %s on %i.%i disappeared." % (full_name, - stype, domain, interface, protocol)) - - self._dbus_helper.ServiceDisappeared(service.object_path()) - self._handle_remove_activity_service(service) - - # Decompose service name if we can - (actid, buddy_name) = Service.decompose_service_name(full_name) - - # Remove the service from the buddy - try: - buddy = self._buddies[buddy_name] - except KeyError: - pass - else: - buddy.remove_service(service) - if not buddy.is_valid(): - self._dbus_helper.BuddyDisappeared(buddy.object_path()) - del self._buddies[buddy_name] - key = (service.get_full_name(), service.get_type()) - del self._services[key] - return False - - def _service_disappeared_cb_glue(self, interface, protocol, name, stype, domain, flags): - gobject.idle_add(self._service_disappeared_cb, interface, protocol, name, stype, domain, flags) - - def _new_service_type_cb(self, interface, protocol, stype, domain, flags): - # Are we already browsing this domain for this type? - if self._service_browsers.has_key((interface, protocol, stype, domain)): - return - - # Start browsing for all services of this type in this domain - try: - s_browser = self._mdns_service.ServiceBrowserNew(interface, - protocol, stype, domain, dbus.UInt32(0)) - browser_obj = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, s_browser), - avahi.DBUS_INTERFACE_SERVICE_BROWSER) - browser_obj.connect_to_signal('ItemNew', self._service_appeared_cb_glue) - browser_obj.connect_to_signal('ItemRemove', self._service_disappeared_cb_glue) - - self._service_browsers[(interface, protocol, stype, domain)] = browser_obj - except dbus.DBusException: - logging.debug("Error browsing service type '%s'" % stype) - return False - - def _new_service_type_cb_glue(self, interface, protocol, stype, domain, flags): - if len(stype) > 0: - gobject.idle_add(self._new_service_type_cb, interface, protocol, - stype, domain, flags) - - def _new_domain_cb(self, interface, protocol, domain, flags=0): - """Callback from Avahi when a new domain has been found. Start - browsing the new domain.""" - # Only use .local for now... - if domain != "local": - return - - # Are we already browsing this domain? - if self._service_type_browsers.has_key((interface, protocol, domain)): - return - - # Start browsing this domain for the services its members offer - try: - st_browser = self._mdns_service.ServiceTypeBrowserNew(interface, protocol, domain, dbus.UInt32(0)) - browser_obj = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, st_browser), - avahi.DBUS_INTERFACE_SERVICE_TYPE_BROWSER) - except dbus.DBusException, exc: - str_exc = str(exc) - logging.error("got exception %s while attempting to browse domain %s on %i.%i" % (str_exc, domain, interface, protocol)) - if str_exc.find("The name org.freedesktop.Avahi was not provided by any .service files") >= 0: - raise Exception("Avahi does not appear to be running. '%s'" % str_exc) - else: - raise exc - logging.debug("Browsing domain '%s' on %i.%i ..." % (domain, interface, protocol)) - browser_obj.connect_to_signal('ItemNew', self._new_service_type_cb_glue) - self._service_type_browsers[(interface, protocol, domain)] = browser_obj - return False - - def _new_domain_cb_glue(self, interface, protocol, domain, flags=0): - gobject.idle_add(self._new_domain_cb, interface, protocol, domain, flags) - - def join_activity(self, activity, stype, sender): - services = activity.get_services_of_type(stype) - if not len(services): - raise NotFoundError("The service type %s was not present within " \ - "the activity %s" % (stype, activity.object_path())) - act_service = services[0] - props = act_service.get_properties() - color = activity.get_color() - if color: - props['color'] = color - return self._share_activity(activity.get_id(), stype, properties, - act_service.get_address(), act_service.get_port(), - act_service.get_domain(), sender) - - def share_activity(self, activity_id, stype, properties=None, address=None, - port=-1, domain=u"local", sender=None): - """Convenience function to share an activity with other buddies.""" - if not util.validate_activity_id(activity_id): - raise ValueError("invalid activity id") - owner_nick = self._owner.get_name() - real_name = Service.compose_service_name(owner_nick, activity_id) - if address and not isinstance(address, unicode): - raise ValueError("address must be a unicode string.") - if address == None and stype.endswith('_udp'): - # Use random currently unassigned multicast address - address = u"232.%d.%d.%d" % (random.randint(0, 254), random.randint(1, 254), - random.randint(1, 254)) - properties['address'] = address - properties['port'] = port - if port and port != -1 and (not isinstance(port, int) or port <= 1024 or port >= 65535): - raise ValueError("port must be a number between 1024 and 65535") - - color = self._owner.get_color() - if color: - properties['color'] = color - - logging.debug('Share activity %s, type %s, address %s, port %d, " \ - "properties %s' % (activity_id, stype, address, port, - properties)) - return self.register_service(real_name, stype, properties, address, - port, domain, sender) - - def register_service(self, name, stype, properties={}, address=None, - port=-1, domain=u"local", sender=None): - """Register a new service, advertising it to other Buddies on the network.""" - # Refuse to register if we can't get the dbus connection this request - # came from for some reason - if not sender: - raise RuntimeError("Service registration request must have a sender.") - - (actid, person_name) = Service.decompose_service_name(name) - if self.get_owner() and person_name != self.get_owner().get_name(): - raise RuntimeError("Tried to register a service that didn't have" \ - " Owner nick as the service name!") - if not domain or not len(domain): - domain = u"local" - if not port or port == -1: - port = random.randint(4000, 65000) - - objid = self._get_next_object_id() - service = Service.Service(self._bus_name, objid, name=name, - stype=stype, domain=domain, address=address, port=port, - properties=properties, source_address=None, - local_publisher=sender) - self._services[(name, stype)] = service - self.register_service_type(stype) - service.register(self._system_bus, self._mdns_service) - return service - - def unregister_service(self, service, sender=None): - service.unregister(sender) - - def register_service_type(self, stype): - """Requests that the Presence service look for and recognize - a certain mDNS service types.""" - if not isinstance(stype, unicode): - raise ValueError("service type must be a unicode string.") - - # If we've already registered it as a service type, ref it and return - item = self._find_registered_service_type(stype) - if item is not None: - item.ref() - return - - # Otherwise track this type now - obj = RegisteredServiceType(stype) - self._registered_service_types.append(obj) - - # Find unresolved services that match the service type - # we're now interested in, and resolve them - resolv_list = [] - - # Find services of this type - resolv_list = self._find_service_adv(stype=stype) - # Request resolution for them if they aren't in-process already - for adv in resolv_list: - if adv.state() == _SA_UNRESOLVED: - adv.set_state(_SA_RESOLVE_PENDING) - gobject.idle_add(self._resolve_service, adv) - - def unregister_service_type(self, stype): - """Stop tracking a certain mDNS service.""" - if not isinstance(stype, unicode): - raise ValueError("service type must be a unicode string.") - - # if it was found, unref it and possibly remove it - item = self._find_registered_service_type(stype) - if not item: - return - if item.unref() <= 0: - self._registered_service_types.remove(item) - del item + def __init__(self): + # interface -> IP address: interfaces we've gotten events on so far + self._started = False + self._local_addrs = {} + + self._next_object_id = 0 + + self._buddies = {} # nick -> Buddy + self._services = {} # (name, type) -> Service + self._activities = {} # activity id -> Activity + + # Keep track of stuff we're already browsing + self._service_type_browsers = {} + self._service_browsers = {} + + # Resolved service list + self._service_advs = [] + + # Service types we care about resolving + self._registered_service_types = [] + + # Set up the dbus service we provide + self._session_bus = dbus.SessionBus() + self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=self._session_bus) + self._dbus_helper = PresenceServiceDBusHelper(self, self._bus_name) + + self._icon_cache = BuddyIconCache.BuddyIconCache() + + # Our owner object + if profile.get_nick_name(): + objid = self._get_next_object_id() + self._owner = Buddy.Owner(self, self._bus_name, + objid, self._icon_cache) + self._buddies[self._owner.get_name()] = self._owner + else: + self._owner = None + + def start(self): + if self._started: + return + self._started = True + + # Connect to Avahi for mDNS stuff + self._system_bus = dbus.SystemBus() + self._mdns_service = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, + avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) + + # Always browse .local + self._new_domain_cb(avahi.IF_UNSPEC, avahi.PROTO_INET, "local") + + # Connect to Avahi and start looking for stuff + domain_browser = self._mdns_service.DomainBrowserNew(avahi.IF_UNSPEC, avahi.PROTO_INET, + "", avahi.DOMAIN_BROWSER_BROWSE, dbus.UInt32(0)) + db = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, domain_browser), avahi.DBUS_INTERFACE_DOMAIN_BROWSER) + db.connect_to_signal('ItemNew', self._new_domain_cb_glue) + + def _get_next_object_id(self): + """Increment and return the object ID counter.""" + self._next_object_id = self._next_object_id + 1 + return self._next_object_id + + def get_services(self): + return self._services.values() + + def get_services_of_type(self, stype): + ret = [] + for serv in self._services.values(): + if serv.get_type() == stype: + ret.append(serv) + return ret + + def get_activities(self): + # Only return valid activities + ret = [] + for act in self._activities.values(): + if act.is_valid(): + ret.append(act) + return ret + + def get_activity(self, actid): + if self._activities.has_key(actid): + act = self._activities[actid] + if act.is_valid(): + return act + return None + + def get_buddies(self): + buddies = [] + for buddy in self._buddies.values(): + if buddy.is_valid(): + buddies.append(buddy) + return buddies + + def get_buddy_by_name(self, name): + if self._buddies.has_key(name): + if self._buddies[name].is_valid(): + return self._buddies[name] + return None + + def get_buddy_by_address(self, address): + for buddy in self._buddies.values(): + if buddy.get_address() == address and buddy.is_valid(): + return buddy + return None + + def get_owner(self): + return self._owner + + def _find_service_adv(self, interface=None, protocol=None, name=None, + stype=None, domain=None, local=None): + """Search a list of service advertisements for ones matching + certain criteria.""" + adv_list = [] + for adv in self._service_advs: + if interface and adv.interface() != interface: + continue + if protocol and adv.protocol() != protocol: + continue + if name and adv.name() != name: + continue + if stype and adv.stype() != stype: + continue + if domain and adv.domain() != domain: + continue + if local is not None and adv.is_local() != local: + continue + adv_list.append(adv) + return adv_list + + def _find_registered_service_type(self, stype): + for item in self._registered_service_types: + if item.get_type() == stype: + return item + return None + + def _handle_new_service_for_buddy(self, service, local): + """Deal with a new discovered service object.""" + # Once a service is resolved, we match it up to an existing buddy, + # or create a new Buddy if this is the first service known about the buddy + buddy_was_valid = False + name = service.get_name() + buddy = None + try: + buddy = self._buddies[name] + buddy_was_valid = buddy.is_valid() + service_added = buddy.add_service(service) + if service_added: + self._dbus_helper.ServiceAppeared(service.object_path()) + except KeyError: + source_addr = service.get_source_address() + objid = self._get_next_object_id() + buddy = Buddy.Buddy(self._bus_name, objid, service, self._icon_cache) + self._buddies[name] = buddy + self._dbus_helper.ServiceAppeared(service.object_path()) + if not buddy_was_valid and buddy.is_valid(): + self._dbus_helper.BuddyAppeared(buddy.object_path()) + return buddy + + def _handle_new_activity_service(self, service): + # If the serivce is an activity service, merge it into our activities list + actid = service.get_activity_id() + if not actid: + return + activity = None + was_valid = False + if not self._activities.has_key(actid): + objid = self._get_next_object_id() + activity = Activity.Activity(self._bus_name, objid, service) + self._activities[actid] = activity + else: + activity = self._activities[actid] + was_valid = activity.is_valid() + + if activity: + activity.add_service(service) + + # Add the activity to its buddy + # FIXME: use something other than name to attribute to buddy + try: + buddy = self._buddies[service.get_name()] + buddy.add_activity(activity) + except KeyError: + pass + + if not was_valid and activity.is_valid(): + self._dbus_helper.ActivityAppeared(activity.object_path()) + + def _handle_remove_activity_service(self, service): + actid = service.get_activity_id() + if not actid: + return + if not self._activities.has_key(actid): + return + + activity = self._activities[actid] + + activity.remove_service(service) + if len(activity.get_services()) == 0: + # Remove the activity from its buddy + # FIXME: use something other than name to attribute to buddy + try: + buddy = self._buddies[service.get_name()] + buddy.remove_activity(activity) + except KeyError: + pass + + # Kill the activity + self._dbus_helper.ActivityDisappeared(activity.object_path()) + del self._activities[actid] + + def _service_resolved_cb(self, adv, interface, protocol, full_name, + stype, domain, host, aprotocol, address, port, txt, flags, + updated): + """When the service discovery finally gets here, we've got enough + information about the service to assign it to a buddy.""" + if updated == False: + logging.debug("Resolved service '%s' type '%s' domain '%s' to " \ + " %s:%s" % (full_name, stype, domain, address, port)) + + if not adv in self._service_advs: + return False + if adv.state() != _SA_RESOLVED: + return False + + # See if we know about this service already + service = None + key = (full_name, stype) + props = _txt_to_dict(txt) + if not self._services.has_key(key): + objid = self._get_next_object_id() + service = Service.Service(self._bus_name, objid, name=full_name, + stype=stype, domain=domain, address=address, port=port, + properties=props, source_address=address) + self._services[key] = service + else: + # Already tracking this service; either: + # a) we were the one that shared it in the first place, + # and therefore the source address would not have + # been set yet + # b) the service has been updated + service = self._services[key] + if not service.get_source_address(): + service.set_source_address(address) + if not service.get_address(): + service.set_address(address) + + adv.set_service(service) + + if service and updated: + service.set_properties(props, from_network=True) + return False + + # Merge the service into our buddy and activity lists, if needed + buddy = self._handle_new_service_for_buddy(service, adv.is_local()) + if buddy and service.get_activity_id(): + self._handle_new_activity_service(service) + + return False + + def _service_resolved_cb_glue(self, adv, interface, protocol, name, + stype, domain, host, aprotocol, address, port, txt, flags): + # Avahi doesn't flag updates to existing services, so we have + # to determine that here + updated = False + if adv.state() == _SA_RESOLVED: + updated = True + + adv.set_state(_SA_RESOLVED) + gobject.idle_add(self._service_resolved_cb, adv, interface, + protocol, name, stype, domain, host, aprotocol, address, + port, txt, flags, updated) + + def _service_resolved_failure_cb(self, adv, err): + retried = False + adv.set_resolver(None) + if adv.stype() == Buddy.PRESENCE_SERVICE_TYPE: + # Retry the presence service type a few times + if adv.resolv_tries() < 4: + adv.set_state(_SA_RESOLVE_PENDING) + gobject.timeout_add(250, self._resolve_service, adv) + retried = True + logging.error("Retrying resolution of service %s.%s: %s" % (adv.name(), + adv.stype(), err)) + + if not retried: + logging.error("Error resolving service %s.%s: %s" % (adv.name(), + adv.stype(), err)) + adv.set_state(_SA_UNRESOLVED) + + def _resolve_service(self, adv): + """Resolve and lookup a ZeroConf service to obtain its address and TXT records.""" + # Ask avahi to resolve this particular service + path = self._mdns_service.ServiceResolverNew(dbus.Int32(adv.interface()), + dbus.Int32(adv.protocol()), adv.name(), adv.stype(), adv.domain(), + avahi.PROTO_INET, dbus.UInt32(0)) + resolver = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, path), + avahi.DBUS_INTERFACE_SERVICE_RESOLVER) + resolver.connect_to_signal('Found', lambda *args: self._service_resolved_cb_glue(adv, *args)) + resolver.connect_to_signal('Failure', lambda *args: self._service_resolved_failure_cb(adv, *args)) + adv.inc_resolv_tries() + adv.set_resolver(resolver) + return False + + def _service_appeared_cb(self, interface, protocol, full_name, stype, domain, flags): + local = flags & avahi.LOOKUP_RESULT_OUR_OWN > 0 + adv_list = self._find_service_adv(interface=interface, protocol=protocol, + name=full_name, stype=stype, domain=domain, local=local) + adv = None + if not adv_list: + adv = ServiceAdv(interface=interface, protocol=protocol, name=full_name, + stype=stype, domain=domain, local=local) + self._service_advs.append(adv) + else: + adv = adv_list[0] + + # Decompose service name if we can + (actid, buddy_name) = Service.decompose_service_name(full_name) + + # If we care about the service right now, resolve it + resolve = False + item = self._find_registered_service_type(stype) + if actid is not None or item is not None: + resolve = True + if resolve and adv.state() == _SA_UNRESOLVED: + logging.debug("Found '%s' (%d) of type '%s' in domain" \ + " '%s' on %i.%i; will resolve." % (full_name, flags, stype, + domain, interface, protocol)) + adv.set_state(_SA_RESOLVE_PENDING) + gobject.idle_add(self._resolve_service, adv) + + return False + + def _service_appeared_cb_glue(self, interface, protocol, name, stype, domain, flags): + gobject.idle_add(self._service_appeared_cb, interface, protocol, name, stype, domain, flags) + + def _service_disappeared_cb(self, interface, protocol, full_name, stype, domain, flags): + local = flags & avahi.LOOKUP_RESULT_OUR_OWN > 0 + # If it's an unresolved service, remove it from our unresolved list + adv_list = self._find_service_adv(interface=interface, protocol=protocol, + name=full_name, stype=stype, domain=domain, local=local) + if not adv_list: + return False + + # Get the service object; if none, we have nothing left to do + adv = adv_list[0] + service = adv.service() + self._service_advs.remove(adv) + del adv + if not service: + return False + + logging.debug("Service %s.%s in domain %s on %i.%i disappeared." % (full_name, + stype, domain, interface, protocol)) + + self._dbus_helper.ServiceDisappeared(service.object_path()) + self._handle_remove_activity_service(service) + + # Decompose service name if we can + (actid, buddy_name) = Service.decompose_service_name(full_name) + + # Remove the service from the buddy + try: + buddy = self._buddies[buddy_name] + except KeyError: + pass + else: + buddy.remove_service(service) + if not buddy.is_valid(): + self._dbus_helper.BuddyDisappeared(buddy.object_path()) + del self._buddies[buddy_name] + key = (service.get_full_name(), service.get_type()) + del self._services[key] + return False + + def _service_disappeared_cb_glue(self, interface, protocol, name, stype, domain, flags): + gobject.idle_add(self._service_disappeared_cb, interface, protocol, name, stype, domain, flags) + + def _new_service_type_cb(self, interface, protocol, stype, domain, flags): + # Are we already browsing this domain for this type? + if self._service_browsers.has_key((interface, protocol, stype, domain)): + return + + # Start browsing for all services of this type in this domain + try: + s_browser = self._mdns_service.ServiceBrowserNew(interface, + protocol, stype, domain, dbus.UInt32(0)) + browser_obj = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, s_browser), + avahi.DBUS_INTERFACE_SERVICE_BROWSER) + browser_obj.connect_to_signal('ItemNew', self._service_appeared_cb_glue) + browser_obj.connect_to_signal('ItemRemove', self._service_disappeared_cb_glue) + + self._service_browsers[(interface, protocol, stype, domain)] = browser_obj + except dbus.DBusException: + logging.debug("Error browsing service type '%s'" % stype) + return False + + def _new_service_type_cb_glue(self, interface, protocol, stype, domain, flags): + if len(stype) > 0: + gobject.idle_add(self._new_service_type_cb, interface, protocol, + stype, domain, flags) + + def _new_domain_cb(self, interface, protocol, domain, flags=0): + """Callback from Avahi when a new domain has been found. Start + browsing the new domain.""" + # Only use .local for now... + if domain != "local": + return + + # Are we already browsing this domain? + if self._service_type_browsers.has_key((interface, protocol, domain)): + return + + # Start browsing this domain for the services its members offer + try: + st_browser = self._mdns_service.ServiceTypeBrowserNew(interface, protocol, domain, dbus.UInt32(0)) + browser_obj = dbus.Interface(self._system_bus.get_object(avahi.DBUS_NAME, st_browser), + avahi.DBUS_INTERFACE_SERVICE_TYPE_BROWSER) + except dbus.DBusException, exc: + str_exc = str(exc) + logging.error("got exception %s while attempting to browse domain %s on %i.%i" % (str_exc, domain, interface, protocol)) + if str_exc.find("The name org.freedesktop.Avahi was not provided by any .service files") >= 0: + raise Exception("Avahi does not appear to be running. '%s'" % str_exc) + else: + raise exc + logging.debug("Browsing domain '%s' on %i.%i ..." % (domain, interface, protocol)) + browser_obj.connect_to_signal('ItemNew', self._new_service_type_cb_glue) + self._service_type_browsers[(interface, protocol, domain)] = browser_obj + return False + + def _new_domain_cb_glue(self, interface, protocol, domain, flags=0): + gobject.idle_add(self._new_domain_cb, interface, protocol, domain, flags) + + def join_activity(self, activity, stype, sender): + services = activity.get_services_of_type(stype) + if not len(services): + raise NotFoundError("The service type %s was not present within " \ + "the activity %s" % (stype, activity.object_path())) + act_service = services[0] + props = act_service.get_properties() + color = activity.get_color() + if color: + props['color'] = color + return self._share_activity(activity.get_id(), stype, properties, + act_service.get_address(), act_service.get_port(), + act_service.get_domain(), sender) + + def share_activity(self, activity_id, stype, properties=None, address=None, + port=-1, domain=u"local", sender=None): + """Convenience function to share an activity with other buddies.""" + if not util.validate_activity_id(activity_id): + raise ValueError("invalid activity id") + owner_nick = self._owner.get_name() + real_name = Service.compose_service_name(owner_nick, activity_id) + if address and not isinstance(address, unicode): + raise ValueError("address must be a unicode string.") + if address == None and stype.endswith('_udp'): + # Use random currently unassigned multicast address + address = u"232.%d.%d.%d" % (random.randint(0, 254), random.randint(1, 254), + random.randint(1, 254)) + properties['address'] = address + properties['port'] = port + if port and port != -1 and (not isinstance(port, int) or port <= 1024 or port >= 65535): + raise ValueError("port must be a number between 1024 and 65535") + + color = self._owner.get_color() + if color: + properties['color'] = color + + logging.debug('Share activity %s, type %s, address %s, port %d, " \ + "properties %s' % (activity_id, stype, address, port, + properties)) + return self.register_service(real_name, stype, properties, address, + port, domain, sender) + + def register_service(self, name, stype, properties={}, address=None, + port=-1, domain=u"local", sender=None): + """Register a new service, advertising it to other Buddies on the network.""" + # Refuse to register if we can't get the dbus connection this request + # came from for some reason + if not sender: + raise RuntimeError("Service registration request must have a sender.") + + (actid, person_name) = Service.decompose_service_name(name) + if self.get_owner() and person_name != self.get_owner().get_name(): + raise RuntimeError("Tried to register a service that didn't have" \ + " Owner nick as the service name!") + if not domain or not len(domain): + domain = u"local" + if not port or port == -1: + port = random.randint(4000, 65000) + + objid = self._get_next_object_id() + service = Service.Service(self._bus_name, objid, name=name, + stype=stype, domain=domain, address=address, port=port, + properties=properties, source_address=None, + local_publisher=sender) + self._services[(name, stype)] = service + self.register_service_type(stype) + service.register(self._system_bus, self._mdns_service) + return service + + def unregister_service(self, service, sender=None): + service.unregister(sender) + + def register_service_type(self, stype): + """Requests that the Presence service look for and recognize + a certain mDNS service types.""" + if not isinstance(stype, unicode): + raise ValueError("service type must be a unicode string.") + + # If we've already registered it as a service type, ref it and return + item = self._find_registered_service_type(stype) + if item is not None: + item.ref() + return + + # Otherwise track this type now + obj = RegisteredServiceType(stype) + self._registered_service_types.append(obj) + + # Find unresolved services that match the service type + # we're now interested in, and resolve them + resolv_list = [] + + # Find services of this type + resolv_list = self._find_service_adv(stype=stype) + # Request resolution for them if they aren't in-process already + for adv in resolv_list: + if adv.state() == _SA_UNRESOLVED: + adv.set_state(_SA_RESOLVE_PENDING) + gobject.idle_add(self._resolve_service, adv) + + def unregister_service_type(self, stype): + """Stop tracking a certain mDNS service.""" + if not isinstance(stype, unicode): + raise ValueError("service type must be a unicode string.") + + # if it was found, unref it and possibly remove it + item = self._find_registered_service_type(stype) + if not item: + return + if item.unref() <= 0: + self._registered_service_types.remove(item) + del item def main(): - from sugar import TracebackUtils - loop = gobject.MainLoop() - ps = PresenceService() - tbh = TracebackUtils.TracebackHelper() - try: - loop.run() - except KeyboardInterrupt: - print 'Ctrl+C pressed, exiting...' + from sugar import TracebackUtils + loop = gobject.MainLoop() + ps = PresenceService() + tbh = TracebackUtils.TracebackHelper() + try: + loop.run() + except KeyboardInterrupt: + print 'Ctrl+C pressed, exiting...' - del tbh + del tbh if __name__ == "__main__": - main() + main() diff --git a/services/presence/Service.py b/services/presence/Service.py index bb41f88..5072ed5 100644 --- a/services/presence/Service.py +++ b/services/presence/Service.py @@ -24,61 +24,61 @@ import logging import gobject def compose_service_name(name, activity_id): - if isinstance(name, str): - name = unicode(name) - if not name: - raise ValueError("name must be a valid string.") - if not activity_id: - return name - if not isinstance(name, unicode): - raise ValueError("name must be in unicode.") - composed = "%s [%s]" % (name, activity_id) - return composed + if isinstance(name, str): + name = unicode(name) + if not name: + raise ValueError("name must be a valid string.") + if not activity_id: + return name + if not isinstance(name, unicode): + raise ValueError("name must be in unicode.") + composed = "%s [%s]" % (name, activity_id) + return composed def decompose_service_name(name): - """Break a service name into the name and activity ID, if we can.""" - if not isinstance(name, unicode): - raise ValueError("name must be a valid unicode string.") - name_len = len(name) - if name_len < util.ACTIVITY_ID_LEN + 5: - return (None, name) - # check for activity id end marker - if name[name_len - 1] != "]": - return (None, name) - start = name_len - 1 - util.ACTIVITY_ID_LEN - end = name_len - 1 - # check for activity id start marker - if name[start - 1] != "[" or name[start - 2] != " ": - return (None, name) - activity_id = name[start:end] - if not util.validate_activity_id(activity_id): - return (None, name) - return (activity_id, name[:start - 2]) + """Break a service name into the name and activity ID, if we can.""" + if not isinstance(name, unicode): + raise ValueError("name must be a valid unicode string.") + name_len = len(name) + if name_len < util.ACTIVITY_ID_LEN + 5: + return (None, name) + # check for activity id end marker + if name[name_len - 1] != "]": + return (None, name) + start = name_len - 1 - util.ACTIVITY_ID_LEN + end = name_len - 1 + # check for activity id start marker + if name[start - 1] != "[" or name[start - 2] != " ": + return (None, name) + activity_id = name[start:end] + if not util.validate_activity_id(activity_id): + return (None, name) + return (activity_id, name[:start - 2]) 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 def _convert_properties_to_dbus_byte_array(props): - # Ensure properties are converted to ByteArray types - # because python sometimes can't figure that out - info = dbus.Array([], signature="aay") - for k, v in props.items(): - info.append(dbus.types.ByteArray("%s=%s" % (k, v))) - return info + # Ensure properties are converted to ByteArray types + # because python sometimes can't figure that out + info = dbus.Array([], signature="aay") + for k, v in props.items(): + info.append(dbus.types.ByteArray("%s=%s" % (k, v))) + return info _ACTIVITY_ID_TAG = "ActivityID" @@ -86,360 +86,360 @@ SERVICE_DBUS_INTERFACE = "org.laptop.Presence.Service" SERVICE_DBUS_OBJECT_PATH = "/org/laptop/Presence/Services/" class ServiceDBusHelper(dbus.service.Object): - """Handle dbus requests and signals for Service objects""" - def __init__(self, parent, bus_name, object_path): - self._parent = parent - self._bus_name = bus_name - self._object_path = object_path - dbus.service.Object.__init__(self, bus_name, self._object_path) - - @dbus.service.signal(SERVICE_DBUS_INTERFACE, - signature="as") - def PublishedValueChanged(self, keylist): - pass - - @dbus.service.method(SERVICE_DBUS_INTERFACE, - in_signature="", out_signature="a{sv}") - def getProperties(self): - """Return service properties.""" - pary = {} - pary['name'] = self._parent.get_name() - pary['type'] = self._parent.get_type() - pary['domain'] = self._parent.get_domain() - actid = self._parent.get_activity_id() - if actid: - pary['activityId'] = actid - port = self._parent.get_port() - if port: - pary['port'] = self._parent.get_port() - addr = self._parent.get_address() - if addr: - pary['address'] = addr - source_addr = self._parent.get_source_address() - if source_addr: - pary['sourceAddress'] = source_addr - return pary - - @dbus.service.method(SERVICE_DBUS_INTERFACE, - in_signature="s") - def getPublishedValue(self, key): - """Return the value belonging to the requested key from the - service's TXT records.""" - val = self._parent.get_one_property(key) - if not val: - raise KeyError("Value was not found.") - return val - - @dbus.service.method(SERVICE_DBUS_INTERFACE, - in_signature="", out_signature="a{sv}") - def getPublishedValues(self): - pary = {} - props = self._parent.get_properties() - for key, value in props.items(): - pary[key] = str(value) - return dbus.Dictionary(pary) - - @dbus.service.method(SERVICE_DBUS_INTERFACE, - sender_keyword="sender") - def setPublishedValue(self, key, value, sender): - self._parent.set_property(key, value, sender) - - @dbus.service.method(SERVICE_DBUS_INTERFACE, - in_signature="a{sv}", sender_keyword="sender") - def setPublishedValues(self, values, sender): - if not self._parent.is_local(): - raise ValueError("Service was not not registered by requesting process!") - self._parent.set_properties(values, sender) + """Handle dbus requests and signals for Service objects""" + def __init__(self, parent, bus_name, object_path): + self._parent = parent + self._bus_name = bus_name + self._object_path = object_path + dbus.service.Object.__init__(self, bus_name, self._object_path) + + @dbus.service.signal(SERVICE_DBUS_INTERFACE, + signature="as") + def PublishedValueChanged(self, keylist): + pass + + @dbus.service.method(SERVICE_DBUS_INTERFACE, + in_signature="", out_signature="a{sv}") + def getProperties(self): + """Return service properties.""" + pary = {} + pary['name'] = self._parent.get_name() + pary['type'] = self._parent.get_type() + pary['domain'] = self._parent.get_domain() + actid = self._parent.get_activity_id() + if actid: + pary['activityId'] = actid + port = self._parent.get_port() + if port: + pary['port'] = self._parent.get_port() + addr = self._parent.get_address() + if addr: + pary['address'] = addr + source_addr = self._parent.get_source_address() + if source_addr: + pary['sourceAddress'] = source_addr + return pary + + @dbus.service.method(SERVICE_DBUS_INTERFACE, + in_signature="s") + def getPublishedValue(self, key): + """Return the value belonging to the requested key from the + service's TXT records.""" + val = self._parent.get_one_property(key) + if not val: + raise KeyError("Value was not found.") + return val + + @dbus.service.method(SERVICE_DBUS_INTERFACE, + in_signature="", out_signature="a{sv}") + def getPublishedValues(self): + pary = {} + props = self._parent.get_properties() + for key, value in props.items(): + pary[key] = str(value) + return dbus.Dictionary(pary) + + @dbus.service.method(SERVICE_DBUS_INTERFACE, + sender_keyword="sender") + def setPublishedValue(self, key, value, sender): + self._parent.set_property(key, value, sender) + + @dbus.service.method(SERVICE_DBUS_INTERFACE, + in_signature="a{sv}", sender_keyword="sender") + def setPublishedValues(self, values, sender): + if not self._parent.is_local(): + raise ValueError("Service was not not registered by requesting process!") + self._parent.set_properties(values, sender) class Service(gobject.GObject): - """Encapsulates information about a specific ZeroConf/mDNS - service as advertised on the network.""" - - __gsignals__ = { - 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - - def __init__(self, bus_name, object_id, name, stype, domain=u"local", - address=None, port=-1, properties=None, source_address=None, - local_publisher=None): - gobject.GObject.__init__(self) - 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") - - # Validate immutable options - if name and not isinstance(name, unicode): - raise ValueError("name must be unicode.") - if not name or not len(name): - raise ValueError("must specify a valid service name.") - - if stype and not isinstance(stype, unicode): - raise ValueError("service type must be in unicode.") - if not stype or not len(stype): - raise ValueError("must specify a valid service type.") - if not stype.endswith("._tcp") and not stype.endswith("._udp"): - raise ValueError("must specify a TCP or UDP service type.") - - if not isinstance(domain, unicode): - raise ValueError("domain must be in unicode.") - if domain and domain != "local": - raise ValueError("must use the 'local' domain (for now).") - - # ID of the D-Bus connection that published this service, if any. - # We only let the local publisher modify the service. - self._local_publisher = local_publisher - - self._avahi_entry_group = None - - (actid, real_name) = decompose_service_name(name) - self._name = real_name - self._full_name = name - self._stype = stype - self._domain = domain - self._port = -1 - self.set_port(port) - self._properties = {} - self._dbus_helper = None - self._internal_set_properties(properties) - - # Source address is the unicast source IP - self._source_address = None - if source_address is not None: - self.set_source_address(source_address) - - # Address is the published address, could be multicast or unicast - self._address = None - if self._properties.has_key('address'): - self.set_address(self._properties['address']) - elif address is not None: - self.set_address(address) - self._properties['address'] = address - - # Ensure that an ActivityID tag, if given, matches - # what we expect from the service type - if self._properties.has_key(_ACTIVITY_ID_TAG): - prop_actid = self._properties[_ACTIVITY_ID_TAG] - if (prop_actid and not actid) or (prop_actid != actid): - raise ValueError("ActivityID property specified, but the " \ - "service names's activity ID didn't match it: %s," \ - " %s" % (prop_actid, actid)) - self._activity_id = actid - if actid and not self._properties.has_key(_ACTIVITY_ID_TAG): - self._properties[_ACTIVITY_ID_TAG] = actid - - self._owner = None - - # register ourselves with dbus - self._object_id = object_id - self._object_path = SERVICE_DBUS_OBJECT_PATH + str(self._object_id) - self._dbus_helper = ServiceDBusHelper(self, bus_name, self._object_path) - - def object_path(self): - return dbus.ObjectPath(self._object_path) - - def get_owner(self): - return self._owner - - def set_owner(self, owner): - if self._owner is not None: - raise RuntimeError("Can only set a service's owner once") - self._owner = owner - - def is_local(self): - if self._local_publisher is not None: - return True - return False - - def get_name(self): - """Return the service's name, usually that of the - buddy who provides it.""" - return self._name - - def get_full_name(self): - return self._full_name - - def get_one_property(self, key): - """Return one property of the service, or None - if the property was not found. Cannot distinguish - between lack of a property, and a property value that - actually is None.""" - if key in self._properties.keys(): - return self._properties[key] - return None - - def get_properties(self): - """Return a python dictionary of all the service's - properties.""" - return self._properties - - def __emit_properties_changed_signal(self, keys): - if self._dbus_helper: - self._dbus_helper.PublishedValueChanged(keys) - self.emit('property-changed', keys) - - def set_property(self, key, value, sender=None): - """Set one service property""" - if not self._local_publisher: - raise ValueError("Service was not not registered by requesting process!") - if sender is not None and self._local_publisher != sender: - raise ValueError("Service was not not registered by requesting process!") - - if not isinstance(key, unicode): - raise ValueError("Key must be a unicode string.") - if not isinstance(value, unicode) and not isinstance(value, bool): - raise ValueError("Key must be a unicode string or a boolean.") - - # Ignore setting the key to it's current value - if self._properties.has_key(key): - if self._properties[key] == value: - return - - # Blank value means remove key - remove = False - if isinstance(value, unicode) and len(value) == 0: - remove = True - if isinstance(value, bool) and value == False: - remove = True - - if remove: - # If the key wasn't present, return without error - if self._properties.has_key(key): - del self._properties[key] - else: - # Otherwise set it - if isinstance(value, bool): - value = "" - self._properties[key] = value - - # if the service is locally published already, update the TXT records - if self._local_publisher and self._avahi_entry_group: - self.__internal_update_avahi_properties() - - self.__emit_properties_changed_signal([key]) - - def set_properties(self, properties, sender=None, from_network=False): - """Set all service properties in one call""" - if sender is not None and self._local_publisher != sender: - raise ValueError("Service was not not registered by requesting process!") - - self._internal_set_properties(properties, from_network) - - def _internal_set_properties(self, properties, from_network=False): - """Set the service's properties from either an Avahi - TXT record (a list of lists of integers), or a - python dictionary.""" - if not isinstance (properties, dict): - raise ValueError("Properties must be a dictionary.") - - # Make sure the properties are actually different - diff_keys = _dicts_differ(self._properties, properties) - if len(diff_keys) == 0: - return - - self._properties = {} - # Set key/value pairs on internal property list - for key, value in properties.items(): - if len(key) == 0: - continue - tmp_key = key - tmp_val = value - if not isinstance(tmp_key, unicode): - tmp_key = unicode(tmp_key) - if not isinstance(tmp_val, unicode): - tmp_val = unicode(tmp_val) - self._properties[tmp_key] = tmp_val - - # if the service is locally published already, update the TXT records - if self._local_publisher and self._avahi_entry_group and not from_network: - self.__internal_update_avahi_properties() - - self.__emit_properties_changed_signal(diff_keys) - - def __internal_update_avahi_properties(self): - info = _convert_properties_to_dbus_byte_array(self._properties) - self._avahi_entry_group.UpdateServiceTxt(avahi.IF_UNSPEC, - avahi.PROTO_UNSPEC, 0, - dbus.String(self._full_name), dbus.String(self._stype), - dbus.String(self._domain), info) - - def get_type(self): - """Return the service's service type.""" - return self._stype - - def get_activity_id(self): - """Return the activity ID this service is associated with, if any.""" - return self._activity_id - - def get_port(self): - return self._port - - def set_port(self, port): - if not isinstance(port, int) or (port <= 1024 and port > 65536): - raise ValueError("must specify a valid port number between 1024 and 65536.") - self._port = port - - def get_source_address(self): - return self._source_address - - def set_source_address(self, address): - if not address or not isinstance(address, unicode): - raise ValueError("address must be unicode") - self._source_address = address - - def get_address(self): - return self._address - - def set_address(self, address): - if not address or not isinstance(address, unicode): - raise ValueError("address must be a unicode string") - self._address = address - self._properties['address'] = address - - def get_domain(self): - """Return the ZeroConf/mDNS domain the service was found in.""" - return self._domain - - def register(self, system_bus, avahi_service): - if self._avahi_entry_group is not None: - raise RuntimeError("Service already registered!") - - obj = system_bus.get_object(avahi.DBUS_NAME, avahi_service.EntryGroupNew()) - self._avahi_entry_group = dbus.Interface(obj, avahi.DBUS_INTERFACE_ENTRY_GROUP) - - info = _convert_properties_to_dbus_byte_array(self._properties) - logging.debug("Will register service with name='%s', stype='%s'," \ - " domain='%s', address='%s', port=%d, info='%s'" % (self._full_name, - self._stype, self._domain, self._address, self._port, info)) - - self._avahi_entry_group.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, 0, - dbus.String(self._full_name), dbus.String(self._stype), - dbus.String(self._domain), dbus.String(""), # let Avahi figure the 'host' out - dbus.UInt16(self._port), info) - - self._avahi_entry_group.connect_to_signal('StateChanged', self.__entry_group_changed_cb) - self._avahi_entry_group.Commit() - - def __entry_group_changed_cb(self, state, error): - pass -# logging.debug("** %s.%s Entry group changed: state %s, error %s" % (self._full_name, self._stype, state, error)) - - def unregister(self, sender): - # Refuse to unregister if we can't get the dbus connection this request - # came from for some reason - if not sender: - raise RuntimeError("Service registration request must have a sender.") - if not self._local_publisher: - raise ValueError("Service was not a local service provided by this laptop!") - if sender is not None and self._local_publisher != sender: - raise ValueError("Service was not registered by requesting process!") - if not self._avahi_entry_group: - raise ValueError("Service was not registered by requesting process!") - self._avahi_entry_group.Free() - del self._avahi_entry_group - self._avahi_entry_group = None + """Encapsulates information about a specific ZeroConf/mDNS + service as advertised on the network.""" + + __gsignals__ = { + 'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, bus_name, object_id, name, stype, domain=u"local", + address=None, port=-1, properties=None, source_address=None, + local_publisher=None): + gobject.GObject.__init__(self) + 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") + + # Validate immutable options + if name and not isinstance(name, unicode): + raise ValueError("name must be unicode.") + if not name or not len(name): + raise ValueError("must specify a valid service name.") + + if stype and not isinstance(stype, unicode): + raise ValueError("service type must be in unicode.") + if not stype or not len(stype): + raise ValueError("must specify a valid service type.") + if not stype.endswith("._tcp") and not stype.endswith("._udp"): + raise ValueError("must specify a TCP or UDP service type.") + + if not isinstance(domain, unicode): + raise ValueError("domain must be in unicode.") + if domain and domain != "local": + raise ValueError("must use the 'local' domain (for now).") + + # ID of the D-Bus connection that published this service, if any. + # We only let the local publisher modify the service. + self._local_publisher = local_publisher + + self._avahi_entry_group = None + + (actid, real_name) = decompose_service_name(name) + self._name = real_name + self._full_name = name + self._stype = stype + self._domain = domain + self._port = -1 + self.set_port(port) + self._properties = {} + self._dbus_helper = None + self._internal_set_properties(properties) + + # Source address is the unicast source IP + self._source_address = None + if source_address is not None: + self.set_source_address(source_address) + + # Address is the published address, could be multicast or unicast + self._address = None + if self._properties.has_key('address'): + self.set_address(self._properties['address']) + elif address is not None: + self.set_address(address) + self._properties['address'] = address + + # Ensure that an ActivityID tag, if given, matches + # what we expect from the service type + if self._properties.has_key(_ACTIVITY_ID_TAG): + prop_actid = self._properties[_ACTIVITY_ID_TAG] + if (prop_actid and not actid) or (prop_actid != actid): + raise ValueError("ActivityID property specified, but the " \ + "service names's activity ID didn't match it: %s," \ + " %s" % (prop_actid, actid)) + self._activity_id = actid + if actid and not self._properties.has_key(_ACTIVITY_ID_TAG): + self._properties[_ACTIVITY_ID_TAG] = actid + + self._owner = None + + # register ourselves with dbus + self._object_id = object_id + self._object_path = SERVICE_DBUS_OBJECT_PATH + str(self._object_id) + self._dbus_helper = ServiceDBusHelper(self, bus_name, self._object_path) + + def object_path(self): + return dbus.ObjectPath(self._object_path) + + def get_owner(self): + return self._owner + + def set_owner(self, owner): + if self._owner is not None: + raise RuntimeError("Can only set a service's owner once") + self._owner = owner + + def is_local(self): + if self._local_publisher is not None: + return True + return False + + def get_name(self): + """Return the service's name, usually that of the + buddy who provides it.""" + return self._name + + def get_full_name(self): + return self._full_name + + def get_one_property(self, key): + """Return one property of the service, or None + if the property was not found. Cannot distinguish + between lack of a property, and a property value that + actually is None.""" + if key in self._properties.keys(): + return self._properties[key] + return None + + def get_properties(self): + """Return a python dictionary of all the service's + properties.""" + return self._properties + + def __emit_properties_changed_signal(self, keys): + if self._dbus_helper: + self._dbus_helper.PublishedValueChanged(keys) + self.emit('property-changed', keys) + + def set_property(self, key, value, sender=None): + """Set one service property""" + if not self._local_publisher: + raise ValueError("Service was not not registered by requesting process!") + if sender is not None and self._local_publisher != sender: + raise ValueError("Service was not not registered by requesting process!") + + if not isinstance(key, unicode): + raise ValueError("Key must be a unicode string.") + if not isinstance(value, unicode) and not isinstance(value, bool): + raise ValueError("Key must be a unicode string or a boolean.") + + # Ignore setting the key to it's current value + if self._properties.has_key(key): + if self._properties[key] == value: + return + + # Blank value means remove key + remove = False + if isinstance(value, unicode) and len(value) == 0: + remove = True + if isinstance(value, bool) and value == False: + remove = True + + if remove: + # If the key wasn't present, return without error + if self._properties.has_key(key): + del self._properties[key] + else: + # Otherwise set it + if isinstance(value, bool): + value = "" + self._properties[key] = value + + # if the service is locally published already, update the TXT records + if self._local_publisher and self._avahi_entry_group: + self.__internal_update_avahi_properties() + + self.__emit_properties_changed_signal([key]) + + def set_properties(self, properties, sender=None, from_network=False): + """Set all service properties in one call""" + if sender is not None and self._local_publisher != sender: + raise ValueError("Service was not not registered by requesting process!") + + self._internal_set_properties(properties, from_network) + + def _internal_set_properties(self, properties, from_network=False): + """Set the service's properties from either an Avahi + TXT record (a list of lists of integers), or a + python dictionary.""" + if not isinstance (properties, dict): + raise ValueError("Properties must be a dictionary.") + + # Make sure the properties are actually different + diff_keys = _dicts_differ(self._properties, properties) + if len(diff_keys) == 0: + return + + self._properties = {} + # Set key/value pairs on internal property list + for key, value in properties.items(): + if len(key) == 0: + continue + tmp_key = key + tmp_val = value + if not isinstance(tmp_key, unicode): + tmp_key = unicode(tmp_key) + if not isinstance(tmp_val, unicode): + tmp_val = unicode(tmp_val) + self._properties[tmp_key] = tmp_val + + # if the service is locally published already, update the TXT records + if self._local_publisher and self._avahi_entry_group and not from_network: + self.__internal_update_avahi_properties() + + self.__emit_properties_changed_signal(diff_keys) + + def __internal_update_avahi_properties(self): + info = _convert_properties_to_dbus_byte_array(self._properties) + self._avahi_entry_group.UpdateServiceTxt(avahi.IF_UNSPEC, + avahi.PROTO_UNSPEC, 0, + dbus.String(self._full_name), dbus.String(self._stype), + dbus.String(self._domain), info) + + def get_type(self): + """Return the service's service type.""" + return self._stype + + def get_activity_id(self): + """Return the activity ID this service is associated with, if any.""" + return self._activity_id + + def get_port(self): + return self._port + + def set_port(self, port): + if not isinstance(port, int) or (port <= 1024 and port > 65536): + raise ValueError("must specify a valid port number between 1024 and 65536.") + self._port = port + + def get_source_address(self): + return self._source_address + + def set_source_address(self, address): + if not address or not isinstance(address, unicode): + raise ValueError("address must be unicode") + self._source_address = address + + def get_address(self): + return self._address + + def set_address(self, address): + if not address or not isinstance(address, unicode): + raise ValueError("address must be a unicode string") + self._address = address + self._properties['address'] = address + + def get_domain(self): + """Return the ZeroConf/mDNS domain the service was found in.""" + return self._domain + + def register(self, system_bus, avahi_service): + if self._avahi_entry_group is not None: + raise RuntimeError("Service already registered!") + + obj = system_bus.get_object(avahi.DBUS_NAME, avahi_service.EntryGroupNew()) + self._avahi_entry_group = dbus.Interface(obj, avahi.DBUS_INTERFACE_ENTRY_GROUP) + + info = _convert_properties_to_dbus_byte_array(self._properties) + logging.debug("Will register service with name='%s', stype='%s'," \ + " domain='%s', address='%s', port=%d, info='%s'" % (self._full_name, + self._stype, self._domain, self._address, self._port, info)) + + self._avahi_entry_group.AddService(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, 0, + dbus.String(self._full_name), dbus.String(self._stype), + dbus.String(self._domain), dbus.String(""), # let Avahi figure the 'host' out + dbus.UInt16(self._port), info) + + self._avahi_entry_group.connect_to_signal('StateChanged', self.__entry_group_changed_cb) + self._avahi_entry_group.Commit() + + def __entry_group_changed_cb(self, state, error): + pass +# logging.debug("** %s.%s Entry group changed: state %s, error %s" % (self._full_name, self._stype, state, error)) + + def unregister(self, sender): + # Refuse to unregister if we can't get the dbus connection this request + # came from for some reason + if not sender: + raise RuntimeError("Service registration request must have a sender.") + if not self._local_publisher: + raise ValueError("Service was not a local service provided by this laptop!") + if sender is not None and self._local_publisher != sender: + raise ValueError("Service was not registered by requesting process!") + if not self._avahi_entry_group: + raise ValueError("Service was not registered by requesting process!") + self._avahi_entry_group.Free() + del self._avahi_entry_group + self._avahi_entry_group = None ################################################################# @@ -450,153 +450,153 @@ import unittest __objid_seq = 0 def _next_objid(): - global __objid_seq - __objid_seq = __objid_seq + 1 - return __objid_seq + global __objid_seq + __objid_seq = __objid_seq + 1 + return __objid_seq class ServiceTestCase(unittest.TestCase): - _DEF_NAME = u"foobar" - _DEF_STYPE = u"_foo._bar._tcp" - _DEF_DOMAIN = u"local" - _DEF_ADDRESS = u"1.1.1.1" - _DEF_PORT = 1234 - _DEF_PROPS = {'foobar': 'baz'} - _STR_TEST_ARGS = [None, 0, [], {}] - - def __init__(self, name): - self._bus = dbus.SessionBus() - self._bus_name = dbus.service.BusName('org.laptop.Presence', bus=self._bus) - unittest.TestCase.__init__(self, name) - - def __del__(self): - del self._bus_name - del self._bus - - def _test_init_fail(self, name, stype, domain, address, port, properties, fail_msg): - """Test something we expect to fail.""" - try: - objid = _next_objid() - service = Service(self._bus_name, objid, name, stype, domain, address, - port, properties) - except ValueError, exc: - pass - else: - self.fail("expected a ValueError for %s." % fail_msg) - - def testName(self): - for item in self._STR_TEST_ARGS: - self._test_init_fail(item, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, - self._DEF_PORT, self._DEF_PROPS, "invalid name") - - def testType(self): - for item in self._STR_TEST_ARGS: - self._test_init_fail(self._DEF_NAME, item, self._DEF_DOMAIN, self._DEF_ADDRESS, - self._DEF_PORT, self._DEF_PROPS, "invalid service type") - self._test_init_fail(self._DEF_NAME, u"_bork._foobar", self._DEF_DOMAIN, self._DEF_ADDRESS, - self._DEF_PORT, self._DEF_PROPS, "invalid service type") - - def testDomain(self): - for item in self._STR_TEST_ARGS: - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, item, self._DEF_ADDRESS, - self._DEF_PORT, self._DEF_PROPS, "invalid domain") - # Only accept local for now - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, u"foobar", self._DEF_ADDRESS, - self._DEF_PORT, self._DEF_PROPS, "invalid domain") - # Make sure "" works - objid = _next_objid() - service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, u"", - self._DEF_ADDRESS, self._DEF_PORT, self._DEF_PROPS) - assert service, "Empty domain was not accepted!" - - def testAddress(self): - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, [], - self._DEF_PORT, self._DEF_PROPS, "invalid address") - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, {}, - self._DEF_PORT, self._DEF_PROPS, "invalid address") - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, 1234, - self._DEF_PORT, self._DEF_PROPS, "invalid address") - - def testPort(self): - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, - [], self._DEF_PROPS, "invalid port") - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, - {}, self._DEF_PROPS, "invalid port") - self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, - "adf", self._DEF_PROPS, "invalid port") - - def testGoodInit(self): - objid = _next_objid() - service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, - self._DEF_ADDRESS, self._DEF_PORT, self._DEF_PROPS) - assert service.get_name() == self._DEF_NAME, "service name wasn't correct after init." - assert service.get_type() == self._DEF_STYPE, "service type wasn't correct after init." - assert service.get_domain() == "local", "service domain wasn't correct after init." - assert service.get_address() == self._DEF_ADDRESS, "service address wasn't correct after init." - assert service.get_port() == self._DEF_PORT, "service port wasn't correct after init." - assert service.object_path() == SERVICE_DBUS_OBJECT_PATH + str(objid) - value = service.get_one_property('foobar') - assert value and value == 'baz', "service property wasn't correct after init." - - def testAvahiProperties(self): - props = [[111, 114, 103, 46, 102, 114, 101, 101, 100, 101, 115, 107, 116, 111, 112, 46, 65, 118, 97, 104, 105, 46, 99, 111, 111, 107, 105, 101, 61, 50, 54, 48, 49, 53, 52, 51, 57, 53, 50]] - key = "org.freedesktop.Avahi.cookie" - expected_value = "2601543952" - objid = _next_objid() - service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, - self._DEF_ADDRESS, self._DEF_PORT, props) - value = service.get_one_property(key) - assert value and value == expected_value, "service properties weren't correct after init." - value = service.get_one_property('bork') - assert not value, "service properties weren't correct after init." - - def testBoolProperty(self): - props = [[111, 114, 103, 46, 102, 114, 101, 101, 100, 101, 115, 107, 116, 111, 112, 46, 65, 118, 97, 104, 105, 46, 99, 111, 111, 107, 105, 101]] - key = "org.freedesktop.Avahi.cookie" - expected_value = True - objid = _next_objid() - service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, - self._DEF_PORT, props) - value = service.get_one_property(key) - assert value is not None and value == expected_value, "service properties weren't correct after init." - - def testActivityService(self): - # Valid group service type, non-multicast address - actid = "4569a71b80805aa96a847f7ac1c407327b3ec2b4" - name = compose_service_name("Tommy", actid) - - # Valid activity service name, None address - objid = _next_objid() - service = Service(self._bus_name, objid, name, self._DEF_STYPE, self._DEF_DOMAIN, None, - self._DEF_PORT, self._DEF_PROPS) - assert service.get_address() == None, "address was not None as expected!" - assert service.get_activity_id() == actid, "activity id was different than expected!" - - # Valid activity service name and multicast address, ensure it works - mc_addr = u"224.0.0.34" - objid = _next_objid() - service = Service(self._bus_name, objid, name, self._DEF_STYPE, self._DEF_DOMAIN, mc_addr, - self._DEF_PORT, self._DEF_PROPS) - assert service.get_address() == mc_addr, "address was not expected address!" - - def addToSuite(suite): - suite.addTest(ServiceTestCase("testName")) - suite.addTest(ServiceTestCase("testType")) - suite.addTest(ServiceTestCase("testDomain")) - suite.addTest(ServiceTestCase("testAddress")) - suite.addTest(ServiceTestCase("testPort")) - suite.addTest(ServiceTestCase("testGoodInit")) - suite.addTest(ServiceTestCase("testAvahiProperties")) - suite.addTest(ServiceTestCase("testBoolProperty")) - suite.addTest(ServiceTestCase("testActivityService")) - addToSuite = staticmethod(addToSuite) + _DEF_NAME = u"foobar" + _DEF_STYPE = u"_foo._bar._tcp" + _DEF_DOMAIN = u"local" + _DEF_ADDRESS = u"1.1.1.1" + _DEF_PORT = 1234 + _DEF_PROPS = {'foobar': 'baz'} + _STR_TEST_ARGS = [None, 0, [], {}] + + def __init__(self, name): + self._bus = dbus.SessionBus() + self._bus_name = dbus.service.BusName('org.laptop.Presence', bus=self._bus) + unittest.TestCase.__init__(self, name) + + def __del__(self): + del self._bus_name + del self._bus + + def _test_init_fail(self, name, stype, domain, address, port, properties, fail_msg): + """Test something we expect to fail.""" + try: + objid = _next_objid() + service = Service(self._bus_name, objid, name, stype, domain, address, + port, properties) + except ValueError, exc: + pass + else: + self.fail("expected a ValueError for %s." % fail_msg) + + def testName(self): + for item in self._STR_TEST_ARGS: + self._test_init_fail(item, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, + self._DEF_PORT, self._DEF_PROPS, "invalid name") + + def testType(self): + for item in self._STR_TEST_ARGS: + self._test_init_fail(self._DEF_NAME, item, self._DEF_DOMAIN, self._DEF_ADDRESS, + self._DEF_PORT, self._DEF_PROPS, "invalid service type") + self._test_init_fail(self._DEF_NAME, u"_bork._foobar", self._DEF_DOMAIN, self._DEF_ADDRESS, + self._DEF_PORT, self._DEF_PROPS, "invalid service type") + + def testDomain(self): + for item in self._STR_TEST_ARGS: + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, item, self._DEF_ADDRESS, + self._DEF_PORT, self._DEF_PROPS, "invalid domain") + # Only accept local for now + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, u"foobar", self._DEF_ADDRESS, + self._DEF_PORT, self._DEF_PROPS, "invalid domain") + # Make sure "" works + objid = _next_objid() + service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, u"", + self._DEF_ADDRESS, self._DEF_PORT, self._DEF_PROPS) + assert service, "Empty domain was not accepted!" + + def testAddress(self): + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, [], + self._DEF_PORT, self._DEF_PROPS, "invalid address") + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, {}, + self._DEF_PORT, self._DEF_PROPS, "invalid address") + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, 1234, + self._DEF_PORT, self._DEF_PROPS, "invalid address") + + def testPort(self): + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, + [], self._DEF_PROPS, "invalid port") + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, + {}, self._DEF_PROPS, "invalid port") + self._test_init_fail(self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, + "adf", self._DEF_PROPS, "invalid port") + + def testGoodInit(self): + objid = _next_objid() + service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, + self._DEF_ADDRESS, self._DEF_PORT, self._DEF_PROPS) + assert service.get_name() == self._DEF_NAME, "service name wasn't correct after init." + assert service.get_type() == self._DEF_STYPE, "service type wasn't correct after init." + assert service.get_domain() == "local", "service domain wasn't correct after init." + assert service.get_address() == self._DEF_ADDRESS, "service address wasn't correct after init." + assert service.get_port() == self._DEF_PORT, "service port wasn't correct after init." + assert service.object_path() == SERVICE_DBUS_OBJECT_PATH + str(objid) + value = service.get_one_property('foobar') + assert value and value == 'baz', "service property wasn't correct after init." + + def testAvahiProperties(self): + props = [[111, 114, 103, 46, 102, 114, 101, 101, 100, 101, 115, 107, 116, 111, 112, 46, 65, 118, 97, 104, 105, 46, 99, 111, 111, 107, 105, 101, 61, 50, 54, 48, 49, 53, 52, 51, 57, 53, 50]] + key = "org.freedesktop.Avahi.cookie" + expected_value = "2601543952" + objid = _next_objid() + service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, + self._DEF_ADDRESS, self._DEF_PORT, props) + value = service.get_one_property(key) + assert value and value == expected_value, "service properties weren't correct after init." + value = service.get_one_property('bork') + assert not value, "service properties weren't correct after init." + + def testBoolProperty(self): + props = [[111, 114, 103, 46, 102, 114, 101, 101, 100, 101, 115, 107, 116, 111, 112, 46, 65, 118, 97, 104, 105, 46, 99, 111, 111, 107, 105, 101]] + key = "org.freedesktop.Avahi.cookie" + expected_value = True + objid = _next_objid() + service = Service(self._bus_name, objid, self._DEF_NAME, self._DEF_STYPE, self._DEF_DOMAIN, self._DEF_ADDRESS, + self._DEF_PORT, props) + value = service.get_one_property(key) + assert value is not None and value == expected_value, "service properties weren't correct after init." + + def testActivityService(self): + # Valid group service type, non-multicast address + actid = "4569a71b80805aa96a847f7ac1c407327b3ec2b4" + name = compose_service_name("Tommy", actid) + + # Valid activity service name, None address + objid = _next_objid() + service = Service(self._bus_name, objid, name, self._DEF_STYPE, self._DEF_DOMAIN, None, + self._DEF_PORT, self._DEF_PROPS) + assert service.get_address() == None, "address was not None as expected!" + assert service.get_activity_id() == actid, "activity id was different than expected!" + + # Valid activity service name and multicast address, ensure it works + mc_addr = u"224.0.0.34" + objid = _next_objid() + service = Service(self._bus_name, objid, name, self._DEF_STYPE, self._DEF_DOMAIN, mc_addr, + self._DEF_PORT, self._DEF_PROPS) + assert service.get_address() == mc_addr, "address was not expected address!" + + def addToSuite(suite): + suite.addTest(ServiceTestCase("testName")) + suite.addTest(ServiceTestCase("testType")) + suite.addTest(ServiceTestCase("testDomain")) + suite.addTest(ServiceTestCase("testAddress")) + suite.addTest(ServiceTestCase("testPort")) + suite.addTest(ServiceTestCase("testGoodInit")) + suite.addTest(ServiceTestCase("testAvahiProperties")) + suite.addTest(ServiceTestCase("testBoolProperty")) + suite.addTest(ServiceTestCase("testActivityService")) + addToSuite = staticmethod(addToSuite) def main(): - suite = unittest.TestSuite() - ServiceTestCase.addToSuite(suite) - runner = unittest.TextTestRunner() - runner.run(suite) + suite = unittest.TestSuite() + ServiceTestCase.addToSuite(suite) + runner = unittest.TextTestRunner() + runner.run(suite) if __name__ == "__main__": - main() + main() diff --git a/shell/console/console.py b/shell/console/console.py index 565b973..c105e85 100755 --- a/shell/console/console.py +++ b/shell/console/console.py @@ -27,7 +27,7 @@ logviewer_widget = logviewer.Interface().widget logviewer_widget.show() # Terminal interface -terminal_widget = terminal.Interface().widget +terminal_widget = terminal.Interface().widget terminal_widget.show() # Notebook diff --git a/shell/console/logviewer.py b/shell/console/logviewer.py index fb589a2..b93f05e 100644 --- a/shell/console/logviewer.py +++ b/shell/console/logviewer.py @@ -26,76 +26,76 @@ import gobject from sugar import env class LogBuffer(gtk.TextBuffer): - def __init__(self, logfile): - gtk.TextBuffer.__init__(self) + def __init__(self, logfile): + gtk.TextBuffer.__init__(self) - self._logfile = logfile - self._pos = 0 + self._logfile = logfile + self._pos = 0 - self.update() + self.update() - def update(self): - f = open(self._logfile, 'r') + def update(self): + f = open(self._logfile, 'r') - f.seek(self._pos) - self.insert(self.get_end_iter(), f.read()) - self._pos = f.tell() + f.seek(self._pos) + self.insert(self.get_end_iter(), f.read()) + self._pos = f.tell() - f.close() + f.close() - return True + return True class LogView(gtk.ScrolledWindow): - def __init__(self, model): - gtk.ScrolledWindow.__init__(self) + def __init__(self, model): + gtk.ScrolledWindow.__init__(self) - self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - textview = gtk.TextView(model) - textview.set_wrap_mode(gtk.WRAP_WORD) - textview.set_editable(False) + textview = gtk.TextView(model) + textview.set_wrap_mode(gtk.WRAP_WORD) + textview.set_editable(False) - self.add(textview) - textview.show() + self.add(textview) + textview.show() class MultiLogView(gtk.Notebook): - def __init__(self, path): - gtk.Notebook.__init__(self) + def __init__(self, path): + gtk.Notebook.__init__(self) - self._logs_path = path - self._pages = {} + self._logs_path = path + self._pages = {} - self._update() + self._update() - gobject.timeout_add(1000, self._update) + gobject.timeout_add(1000, self._update) - def _add_page(self, logfile): - full_log_path = os.path.join(self._logs_path, logfile) - model = LogBuffer(full_log_path) + def _add_page(self, logfile): + full_log_path = os.path.join(self._logs_path, logfile) + model = LogBuffer(full_log_path) - view = LogView(model) - self.append_page(view, gtk.Label(logfile)) - view.show() + view = LogView(model) + self.append_page(view, gtk.Label(logfile)) + view.show() - self._pages[logfile] = model + self._pages[logfile] = model - def _update(self): - if not os.path.isdir(self._logs_path): - return True + def _update(self): + if not os.path.isdir(self._logs_path): + return True - for logfile in os.listdir(self._logs_path): - if self._pages.has_key(logfile): - self._pages[logfile].update() - else: - self._add_page(logfile) + for logfile in os.listdir(self._logs_path): + if self._pages.has_key(logfile): + self._pages[logfile].update() + else: + self._add_page(logfile) - return True + return True class Interface: - def __init__(self): - path = os.path.join(env.get_profile_path(), 'logs') - viewer = MultiLogView(path) - viewer.show() - self.widget = viewer - \ No newline at end of file + def __init__(self): + path = os.path.join(env.get_profile_path(), 'logs') + viewer = MultiLogView(path) + viewer.show() + self.widget = viewer + \ No newline at end of file diff --git a/shell/console/memphis.py b/shell/console/memphis.py index 4513987..5e4e8a2 100644 --- a/shell/console/memphis.py +++ b/shell/console/memphis.py @@ -8,175 +8,175 @@ import plugin from procmem import proc try: - import gtk - import gtk.gdk - import gobject + import gtk + import gtk.gdk + import gobject except: - sys.exit(1) + sys.exit(1) class Interface: - - store_data_types = [] - store_data_types_details = [] - - def __init__(self): - - # Our GtkTree (Treeview) - self.treeview = gtk.TreeView() - self.widget = self.treeview - - # Loading plugins - self.plg = plugin.Plugin() - - # TOP data types (columns) - self.store_data_types = [] - - for plg in self.plg.list: - plg_data = plg.INTERNALS - - # Give plugin object to plugin - plg.INTERNALS['Plg'] = self.plg - - # Creating a store model and loading process data to Treeview - # self.store_data_types, ex [int, str, str, str, int,...] - #self.store = gtk.TreeStore(*self.store_data_types) - self.data = Data(self.treeview, self.plg.list) - + + store_data_types = [] + store_data_types_details = [] + + def __init__(self): + + # Our GtkTree (Treeview) + self.treeview = gtk.TreeView() + self.widget = self.treeview + + # Loading plugins + self.plg = plugin.Plugin() + + # TOP data types (columns) + self.store_data_types = [] + + for plg in self.plg.list: + plg_data = plg.INTERNALS + + # Give plugin object to plugin + plg.INTERNALS['Plg'] = self.plg + + # Creating a store model and loading process data to Treeview + # self.store_data_types, ex [int, str, str, str, int,...] + #self.store = gtk.TreeStore(*self.store_data_types) + self.data = Data(self.treeview, self.plg.list) + class Data: - treeview = None - last_col_index = 0 - - store_data_cols = [] - store_data_types = [] - store_data_types_details = [] - - def __init__(self, treeview, plg_list): - - # Top data types - self.plg_list = plg_list - - for plg in self.plg_list: - - if plg.INTERNALS['top_data'] != None: - last_dt = len(self.store_data_types) - - if last_dt > 0: - last_dt -= 1 - - len_dt = len(plg.INTERNALS['top_data']) - - self.store_data_types_details.append({"plugin": plg, "init": last_dt, "end": last_dt + len_dt}) - - for dt in plg.INTERNALS['top_data']: - self.store_data_types.append(dt) - - for col in plg.INTERNALS['top_cols']: - self.store_data_cols.append(col) - - # Set global treeview - self.treeview = treeview - - # Basic columns - index = 0 - for column_name in self.store_data_cols: - self.add_column(column_name, index) - index += 1 - - self.store = gtk.TreeStore(*self.store_data_types) - treeview.set_model(self.store) - - # Update information every 1 second - gobject.timeout_add(500, self.load_data, treeview) - - # Add a new column to the main treeview - def add_column(self, column_name, index): - cell = gtk.CellRendererText() - col_tv = gtk.TreeViewColumn(column_name, cell, text=index) - col_tv.set_resizable(True) - col_tv.connect('clicked', self.sort_column_clicked) - col_tv.set_property('clickable', True) - - self.treeview.append_column(col_tv) - - # Set the last column index added - self.last_col_index = index - - # Sorting - def sort_column_clicked(self, TreeViewColumn): - cols = self.treeview.get_columns() - - # Searching column index - index = 0 - for col in cols: - if col == TreeViewColumn: - break - - index += 1 - - self.store.set_sort_column_id(index, gtk.SORT_DESCENDING) - - def load_data(self, treeview): - self.store.clear() - - # Getting procfs data - self.procdata = proc.ProcInfo() - self.process_list = [] - - pids = [] - screen = wnck.screen_get_default() - windows = screen.get_windows() - - current_pid = os.getpid() - - for win in windows: - pid = int(win.get_pid()) - if current_pid != pid: - pids.append(pid) - - self.process_list = set(pids) - - # Sort rows using pid - #self.process_list.sort(key=operator.itemgetter('pid')) - self.process_iter = [] - - for pid in self.process_list: - pi = self.build_row(self.store, None, self.procdata, pid) - self.process_iter.append(pi) - - treeview.set_rules_hint(True) - treeview.expand_all() - - return True - - def build_row(self, store, parent_iter, proc_data, pid): - data = [] - - pinfo = proc_data.MemoryInfo(pid) - - # Look for plugins that need to update the top data treeview - for plg in self.plg_list: - plg_data = [] - - if plg.INTERNALS['top_data'] != None: - # data = [xxx, yyy,zzz,...] - plg_data = plg.info.plg_on_top_data_refresh(plg, pinfo) - - for field in plg_data: - data.append(field) - - pi = self.insert_row(store, parent_iter, data) - - return pi - - # Insert a Row in our TreeView - def insert_row(self, store, parent, row_data): - iter = store.insert_after(parent, None) - - index = 0 - - for data in row_data: - store.set_value(iter, index , data) - index += 1 - - return iter + treeview = None + last_col_index = 0 + + store_data_cols = [] + store_data_types = [] + store_data_types_details = [] + + def __init__(self, treeview, plg_list): + + # Top data types + self.plg_list = plg_list + + for plg in self.plg_list: + + if plg.INTERNALS['top_data'] != None: + last_dt = len(self.store_data_types) + + if last_dt > 0: + last_dt -= 1 + + len_dt = len(plg.INTERNALS['top_data']) + + self.store_data_types_details.append({"plugin": plg, "init": last_dt, "end": last_dt + len_dt}) + + for dt in plg.INTERNALS['top_data']: + self.store_data_types.append(dt) + + for col in plg.INTERNALS['top_cols']: + self.store_data_cols.append(col) + + # Set global treeview + self.treeview = treeview + + # Basic columns + index = 0 + for column_name in self.store_data_cols: + self.add_column(column_name, index) + index += 1 + + self.store = gtk.TreeStore(*self.store_data_types) + treeview.set_model(self.store) + + # Update information every 1 second + gobject.timeout_add(500, self.load_data, treeview) + + # Add a new column to the main treeview + def add_column(self, column_name, index): + cell = gtk.CellRendererText() + col_tv = gtk.TreeViewColumn(column_name, cell, text=index) + col_tv.set_resizable(True) + col_tv.connect('clicked', self.sort_column_clicked) + col_tv.set_property('clickable', True) + + self.treeview.append_column(col_tv) + + # Set the last column index added + self.last_col_index = index + + # Sorting + def sort_column_clicked(self, TreeViewColumn): + cols = self.treeview.get_columns() + + # Searching column index + index = 0 + for col in cols: + if col == TreeViewColumn: + break + + index += 1 + + self.store.set_sort_column_id(index, gtk.SORT_DESCENDING) + + def load_data(self, treeview): + self.store.clear() + + # Getting procfs data + self.procdata = proc.ProcInfo() + self.process_list = [] + + pids = [] + screen = wnck.screen_get_default() + windows = screen.get_windows() + + current_pid = os.getpid() + + for win in windows: + pid = int(win.get_pid()) + if current_pid != pid: + pids.append(pid) + + self.process_list = set(pids) + + # Sort rows using pid + #self.process_list.sort(key=operator.itemgetter('pid')) + self.process_iter = [] + + for pid in self.process_list: + pi = self.build_row(self.store, None, self.procdata, pid) + self.process_iter.append(pi) + + treeview.set_rules_hint(True) + treeview.expand_all() + + return True + + def build_row(self, store, parent_iter, proc_data, pid): + data = [] + + pinfo = proc_data.MemoryInfo(pid) + + # Look for plugins that need to update the top data treeview + for plg in self.plg_list: + plg_data = [] + + if plg.INTERNALS['top_data'] != None: + # data = [xxx, yyy,zzz,...] + plg_data = plg.info.plg_on_top_data_refresh(plg, pinfo) + + for field in plg_data: + data.append(field) + + pi = self.insert_row(store, parent_iter, data) + + return pi + + # Insert a Row in our TreeView + def insert_row(self, store, parent, row_data): + iter = store.insert_after(parent, None) + + index = 0 + + for data in row_data: + store.set_value(iter, index , data) + index += 1 + + return iter diff --git a/shell/console/plugin.py b/shell/console/plugin.py index 5ad5ba2..62ed947 100755 --- a/shell/console/plugin.py +++ b/shell/console/plugin.py @@ -9,42 +9,42 @@ from procmem import proc, proc_smaps, analysis class Plugin: - # Plugin list - list = [] - proc = proc.ProcInfo() - - internal_plugin = "memphis_init" - plg_path = os.path.dirname(os.path.abspath(__file__)) + "/plugins" - - # Frequency timer, managed by main program - freq_timer = 0 - - def __init__(self): - - sys.path.insert(0, self.plg_path) - - # Including memphis plugin - self.list.append(__import__(self.internal_plugin)) - - if os.path.isdir(self.plg_path): - # around dir entries - for plg in os.listdir(self.plg_path): - - if plg == self.internal_plugin: - continue - - if os.path.isdir(self.plg_path + "/" + plg): - p = __import__(plg) - self.list.append(__import__(plg)) - - # Parse /proc/PID/smaps information - def proc_get_smaps(self, pid): - return proc_smaps.ProcSmaps(pid) - - # Parse /proc/PID/maps information - def proc_get_maps(self, pid): - return proc_smaps.ProcMaps(pid) + # Plugin list + list = [] + proc = proc.ProcInfo() + + internal_plugin = "memphis_init" + plg_path = os.path.dirname(os.path.abspath(__file__)) + "/plugins" + + # Frequency timer, managed by main program + freq_timer = 0 + + def __init__(self): + + sys.path.insert(0, self.plg_path) + + # Including memphis plugin + self.list.append(__import__(self.internal_plugin)) + + if os.path.isdir(self.plg_path): + # around dir entries + for plg in os.listdir(self.plg_path): + + if plg == self.internal_plugin: + continue + + if os.path.isdir(self.plg_path + "/" + plg): + p = __import__(plg) + self.list.append(__import__(plg)) + + # Parse /proc/PID/smaps information + def proc_get_smaps(self, pid): + return proc_smaps.ProcSmaps(pid) + + # Parse /proc/PID/maps information + def proc_get_maps(self, pid): + return proc_smaps.ProcMaps(pid) - def proc_analysis(self, pid): - return analysis.Analysis(pid) - \ No newline at end of file + def proc_analysis(self, pid): + return analysis.Analysis(pid) + \ No newline at end of file diff --git a/shell/console/plugins/clean_size/__init__.py b/shell/console/plugins/clean_size/__init__.py index fed740c..75ce1d1 100644 --- a/shell/console/plugins/clean_size/__init__.py +++ b/shell/console/plugins/clean_size/__init__.py @@ -2,15 +2,15 @@ import info INTERNALS = { - # Basic information - 'PLGNAME': "Clean Size", - 'TABNAME': None, - 'AUTHOR': "Eduardo Silva", - 'DESC': "Print the approx real memory usage", + # Basic information + 'PLGNAME': "Clean Size", + 'TABNAME': None, + 'AUTHOR': "Eduardo Silva", + 'DESC': "Print the approx real memory usage", - # Plugin API - 'Plg': None, # Plugin object + # Plugin API + 'Plg': None, # Plugin object - 'top_data': [int], # Top data types needed by memphis core plugin - 'top_cols': ["Approx Real Usage (kb)"] - } + 'top_data': [int], # Top data types needed by memphis core plugin + 'top_cols': ["Approx Real Usage (kb)"] + } diff --git a/shell/console/plugins/clean_size/info.py b/shell/console/plugins/clean_size/info.py index e223ea5..25ed044 100644 --- a/shell/console/plugins/clean_size/info.py +++ b/shell/console/plugins/clean_size/info.py @@ -7,9 +7,9 @@ ############################################################ def plg_on_top_data_refresh(self, pinfo): - - # Get clean size - maps = self.INTERNALS['Plg'].proc_get_maps(pinfo['pid']) + + # Get clean size + maps = self.INTERNALS['Plg'].proc_get_maps(pinfo['pid']) - size = (maps.clean_size/1024) - return [size] + size = (maps.clean_size/1024) + return [size] diff --git a/shell/console/plugins/cpu/__init__.py b/shell/console/plugins/cpu/__init__.py index 3ec6135..e22a413 100644 --- a/shell/console/plugins/cpu/__init__.py +++ b/shell/console/plugins/cpu/__init__.py @@ -2,20 +2,20 @@ import os import info INTERNALS = { - 'PLGNAME': "cpu", - 'TABNAME': None, - 'AUTHOR': "Eduardo Silva", - 'DESC': "Print CPU usage", + 'PLGNAME': "cpu", + 'TABNAME': None, + 'AUTHOR': "Eduardo Silva", + 'DESC': "Print CPU usage", - # Plugin API - 'Plg': None, # Plugin object - 'current_plg': None, # Current plugin object - 'current_page': None, # Current page number + # Plugin API + 'Plg': None, # Plugin object + 'current_plg': None, # Current plugin object + 'current_page': None, # Current page number - # Top process view requirements - 'top_data': [int], # Top data types needed by memphis core plugin - 'top_cols': ["%CPU "] # Column names - } + # Top process view requirements + 'top_data': [int], # Top data types needed by memphis core plugin + 'top_cols': ["%CPU "] # Column names + } # Get CPU frequency cpu_hz = os.sysconf(2) diff --git a/shell/console/plugins/cpu/info.py b/shell/console/plugins/cpu/info.py index b8b715e..9cb1ad4 100644 --- a/shell/console/plugins/cpu/info.py +++ b/shell/console/plugins/cpu/info.py @@ -7,42 +7,42 @@ ############################################################ def plg_on_top_data_refresh(self, pinfo): - PI = self.INTERNALS['Plg'].proc - - pid = pinfo['pid'] - - # Get JIFFIES CPU usage - used_jiffies = pinfo['utime'] + pinfo['stime'] - last_ujiffies = get_pid_ujiffies(self, pid) - - cpu_usage = PI.get_CPU_usage(self.cpu_hz, used_jiffies, pinfo['start_time']) + PI = self.INTERNALS['Plg'].proc + + pid = pinfo['pid'] + + # Get JIFFIES CPU usage + used_jiffies = pinfo['utime'] + pinfo['stime'] + last_ujiffies = get_pid_ujiffies(self, pid) + + cpu_usage = PI.get_CPU_usage(self.cpu_hz, used_jiffies, pinfo['start_time']) - # Get PERCENT CPU usage - if last_ujiffies == 0.0: - pcpu = 0.0 - set_pid_ujiffies(self, pid, cpu_usage['used_jiffies']) - data = [int(pcpu)] - return data - - used_jiffies = cpu_usage['used_jiffies'] - last_ujiffies + # Get PERCENT CPU usage + if last_ujiffies == 0.0: + pcpu = 0.0 + set_pid_ujiffies(self, pid, cpu_usage['used_jiffies']) + data = [int(pcpu)] + return data + + used_jiffies = cpu_usage['used_jiffies'] - last_ujiffies - # Available jiffies are - avail_jiffies = (500/1000.0)*self.cpu_hz # 500 = 0.5 second - pcpu = ((used_jiffies*100)/avail_jiffies) - - set_pid_ujiffies(self, pid, cpu_usage['used_jiffies']) - - data = [int(pcpu)] - return data + # Available jiffies are + avail_jiffies = (500/1000.0)*self.cpu_hz # 500 = 0.5 second + pcpu = ((used_jiffies*100)/avail_jiffies) + + set_pid_ujiffies(self, pid, cpu_usage['used_jiffies']) + + data = [int(pcpu)] + return data def get_pid_ujiffies(self, pid): - - if pid in self.pids_ujiffies: - return self.pids_ujiffies[pid] - else: - set_pid_ujiffies(self, pid, 0) - return self.pids_ujiffies[pid] + + if pid in self.pids_ujiffies: + return self.pids_ujiffies[pid] + else: + set_pid_ujiffies(self, pid, 0) + return self.pids_ujiffies[pid] def set_pid_ujiffies(self, pid, ujiffies): - self.pids_ujiffies[pid] = ujiffies + self.pids_ujiffies[pid] = ujiffies diff --git a/shell/console/plugins/dirty_size/__init__.py b/shell/console/plugins/dirty_size/__init__.py index 2661db8..f8e9e0a 100644 --- a/shell/console/plugins/dirty_size/__init__.py +++ b/shell/console/plugins/dirty_size/__init__.py @@ -3,15 +3,15 @@ import info INTERNALS = { - # Basic information - 'PLGNAME': "Dirty Size", - 'TABNAME': None, # No tabbed plugin - 'AUTHOR': "Eduardo Silva", - 'DESC': "Get dirty size memory usage", + # Basic information + 'PLGNAME': "Dirty Size", + 'TABNAME': None, # No tabbed plugin + 'AUTHOR': "Eduardo Silva", + 'DESC': "Get dirty size memory usage", - # Plugin API - 'Plg': None, # Plugin object + # Plugin API + 'Plg': None, # Plugin object - 'top_data': [int], # Top data types needed by memphis core plugin - 'top_cols': ["PDRSS (kb)"] - } + 'top_data': [int], # Top data types needed by memphis core plugin + 'top_cols': ["PDRSS (kb)"] + } diff --git a/shell/console/plugins/dirty_size/info.py b/shell/console/plugins/dirty_size/info.py index 631d86a..54a2e7e 100644 --- a/shell/console/plugins/dirty_size/info.py +++ b/shell/console/plugins/dirty_size/info.py @@ -9,12 +9,12 @@ def plg_on_top_data_refresh(self, ppinfo): - dirty_sizes = get_dirty(self, ppinfo['pid']) - - # memhis need an array - return [dirty_sizes['private']] + dirty_sizes = get_dirty(self, ppinfo['pid']) + + # memhis need an array + return [dirty_sizes['private']] def get_dirty(pself, pid): - ProcAnalysis = pself.INTERNALS['Plg'].proc_analysis(pid) + ProcAnalysis = pself.INTERNALS['Plg'].proc_analysis(pid) - return ProcAnalysis.DirtyRSS() + return ProcAnalysis.DirtyRSS() diff --git a/shell/console/plugins/memphis_init/__init__.py b/shell/console/plugins/memphis_init/__init__.py index c13ce2e..f5ada7e 100644 --- a/shell/console/plugins/memphis_init/__init__.py +++ b/shell/console/plugins/memphis_init/__init__.py @@ -1,15 +1,15 @@ import info INTERNALS = { - 'PLGNAME': "memphis", - 'TABNAME': None, - 'AUTHOR': "Eduardo Silva", - 'DESC': "Print basic process information", + 'PLGNAME': "memphis", + 'TABNAME': None, + 'AUTHOR': "Eduardo Silva", + 'DESC': "Print basic process information", - # Plugin API - 'Plg': None, # Plugin object + # Plugin API + 'Plg': None, # Plugin object - # Top process view requirements - 'top_data': [int, str, str], # Top data types needed by memphis core plugin - 'top_cols': ["PID", "Process Name", "Status"] # Column names - } + # Top process view requirements + 'top_data': [int, str, str], # Top data types needed by memphis core plugin + 'top_cols': ["PID", "Process Name", "Status"] # Column names + } diff --git a/shell/console/plugins/memphis_init/info.py b/shell/console/plugins/memphis_init/info.py index 94d2d43..6e524c7 100644 --- a/shell/console/plugins/memphis_init/info.py +++ b/shell/console/plugins/memphis_init/info.py @@ -8,6 +8,6 @@ def plg_on_top_data_refresh(self, ppinfo): - data = [ppinfo['pid'], ppinfo['name'], ppinfo['state_name']] - - return data + data = [ppinfo['pid'], ppinfo['name'], ppinfo['state_name']] + + return data diff --git a/shell/console/procmem/analysis.py b/shell/console/procmem/analysis.py index a468acd..d2a247a 100644 --- a/shell/console/procmem/analysis.py +++ b/shell/console/procmem/analysis.py @@ -1,30 +1,30 @@ import proc, proc_smaps class Analysis: - - pid = 0 - - def __init__(self, pid): - self.pid = pid - - def DirtyRSS(self): - smaps = proc_smaps.ProcSmaps(self.pid) - dirty = [] + + pid = 0 + + def __init__(self, pid): + self.pid = pid + + def DirtyRSS(self): + smaps = proc_smaps.ProcSmaps(self.pid) + dirty = [] - private = 0 - shared = 0 - - for map in smaps.mappings: - private += map.private_dirty - shared += map.shared_dirty + private = 0 + shared = 0 + + for map in smaps.mappings: + private += map.private_dirty + shared += map.shared_dirty - dirty = {"private": int(private), "shared": int(shared)} + dirty = {"private": int(private), "shared": int(shared)} - return dirty - - def ApproxRealMemoryUsage(self): - maps = proc_smaps.ProcMaps(self.pid) - size = (maps.clean_size/1024) + return dirty + + def ApproxRealMemoryUsage(self): + maps = proc_smaps.ProcMaps(self.pid) + size = (maps.clean_size/1024) - return size - \ No newline at end of file + return size + \ No newline at end of file diff --git a/shell/console/procmem/proc.py b/shell/console/procmem/proc.py index af0a656..729aa13 100644 --- a/shell/console/procmem/proc.py +++ b/shell/console/procmem/proc.py @@ -5,96 +5,96 @@ import string class ProcInfo: - dir_path = "/proc/" # Our cute Proc File System - status_file = "status" - stat_file = "stat" - - proc_list = [] # Our PID list :D - proc_info = [] # - - def __init__(self): - self.proc_list = self.Get_PID_List() - - # Returns Process List - def Get_PID_List(self): - list = [] - - # Exists our procfs ? - if os.path.isdir(self.dir_path): - # around dir entries - for f in os.listdir(self.dir_path): - if os.path.isdir(self.dir_path+f) & str.isdigit(f): - list.append(int(f)) + dir_path = "/proc/" # Our cute Proc File System + status_file = "status" + stat_file = "stat" + + proc_list = [] # Our PID list :D + proc_info = [] # + + def __init__(self): + self.proc_list = self.Get_PID_List() + + # Returns Process List + def Get_PID_List(self): + list = [] + + # Exists our procfs ? + if os.path.isdir(self.dir_path): + # around dir entries + for f in os.listdir(self.dir_path): + if os.path.isdir(self.dir_path+f) & str.isdigit(f): + list.append(int(f)) - return list - - def MemoryInfo(self, pid): - # Path - pidfile = self.dir_path + str(pid) + "/stat" - try: - infile = open(pidfile, "r") - except: - print "Error trying " + pidfile - return None + return list + + def MemoryInfo(self, pid): + # Path + pidfile = self.dir_path + str(pid) + "/stat" + try: + infile = open(pidfile, "r") + except: + print "Error trying " + pidfile + return None - # Parsing data , check 'man 5 proc' for details - data = infile.read().split() + # Parsing data , check 'man 5 proc' for details + data = infile.read().split() - infile.close() - - state_dic = { - 'R': 'Running', - 'S': 'Sleeping', - 'D': 'Disk sleep', - 'Z': 'Zombie', - 'T': 'Traced/Stopped', - 'W': 'Paging' - } + infile.close() + + state_dic = { + 'R': 'Running', + 'S': 'Sleeping', + 'D': 'Disk sleep', + 'Z': 'Zombie', + 'T': 'Traced/Stopped', + 'W': 'Paging' + } - # user and group owners - pidstat = os.stat(pidfile) - - info = { - 'pid': int(data[0]), # Process ID - 'name': data[1].strip('()'), # Process name - 'state': data[2], # Process State, ex: R|S|D|Z|T|W - 'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc - 'ppid': int(data[3]), # Parent process ID - 'utime': int(data[13]), # Used jiffies in user mode - 'stime': int(data[14]), # Used jiffies in kernel mode - 'start_time': int(data[21]), # Process time from system boot (jiffies) - 'vsize': int(data[22]), # Virtual memory size used (bytes) - 'rss': int(data[23])*4, # Resident Set Size (bytes) - 'user_id': pidstat.st_uid, # process owner - 'group_id': pidstat.st_gid # owner group - } - - return info - + # user and group owners + pidstat = os.stat(pidfile) + + info = { + 'pid': int(data[0]), # Process ID + 'name': data[1].strip('()'), # Process name + 'state': data[2], # Process State, ex: R|S|D|Z|T|W + 'state_name': state_dic[data[2]], # Process State name, ex: Running, sleeping, Zombie, etc + 'ppid': int(data[3]), # Parent process ID + 'utime': int(data[13]), # Used jiffies in user mode + 'stime': int(data[14]), # Used jiffies in kernel mode + 'start_time': int(data[21]), # Process time from system boot (jiffies) + 'vsize': int(data[22]), # Virtual memory size used (bytes) + 'rss': int(data[23])*4, # Resident Set Size (bytes) + 'user_id': pidstat.st_uid, # process owner + 'group_id': pidstat.st_gid # owner group + } + + return info + - # Returns the CPU usage expressed in Jiffies - def get_CPU_usage(self, cpu_hz, used_jiffies, start_time): - - # Uptime info - uptime_file = self.dir_path + "/uptime" - try: - infile = file(uptime_file, "r") - except: - print "Error trying uptime file" - return None - - uptime_line = infile.readline() - uptime = string.split(uptime_line, " ",2) - - infile.close() - - # System uptime, from /proc/uptime - uptime = float(uptime[0]) - - # Jiffies - avail_jiffies = (uptime * cpu_hz) - start_time - - cpu_usage = {'used_jiffies': used_jiffies, 'avail_jiffies': avail_jiffies} + # Returns the CPU usage expressed in Jiffies + def get_CPU_usage(self, cpu_hz, used_jiffies, start_time): + + # Uptime info + uptime_file = self.dir_path + "/uptime" + try: + infile = file(uptime_file, "r") + except: + print "Error trying uptime file" + return None + + uptime_line = infile.readline() + uptime = string.split(uptime_line, " ",2) + + infile.close() + + # System uptime, from /proc/uptime + uptime = float(uptime[0]) + + # Jiffies + avail_jiffies = (uptime * cpu_hz) - start_time + + cpu_usage = {'used_jiffies': used_jiffies, 'avail_jiffies': avail_jiffies} - return cpu_usage + return cpu_usage diff --git a/shell/console/procmem/proc_smaps.py b/shell/console/procmem/proc_smaps.py index 84bef43..9416c52 100644 --- a/shell/console/procmem/proc_smaps.py +++ b/shell/console/procmem/proc_smaps.py @@ -9,121 +9,121 @@ import os # Parse the /proc/PID/smaps file class ProcSmaps: - mappings = [] # Devices information - - def __init__(self, pid): - - smapfile = "/proc/%s/smaps" % pid - self.mappings = [] - - # Coded by Federico Mena (script) - try: - infile = open(smapfile, "r") - input = infile.read() - infile.close() - except: - print "Error trying " + smapfile - return - - lines = input.splitlines() + mappings = [] # Devices information + + def __init__(self, pid): + + smapfile = "/proc/%s/smaps" % pid + self.mappings = [] + + # Coded by Federico Mena (script) + try: + infile = open(smapfile, "r") + input = infile.read() + infile.close() + except: + print "Error trying " + smapfile + return + + lines = input.splitlines() - num_lines = len (lines) - line_idx = 0 - - # 08065000-08067000 rw-p 0001c000 03:01 147613 /opt/gnome/bin/evolution-2.6 - # Size: 8 kB - # Rss: 8 kB - # Shared_Clean: 0 kB - # Shared_Dirty: 0 kB - # Private_Clean: 8 kB - # Private_Dirty: 0 kB - - while num_lines > 0: - fields = lines[line_idx].split (" ", 5) - if len (fields) == 6: - (offsets, permissions, bin_permissions, device, inode, name) = fields - else: - (offsets, permissions, bin_permissions, device, inode) = fields - name = "" - - size = self.parse_smaps_size_line (lines[line_idx + 1]) - rss = self.parse_smaps_size_line (lines[line_idx + 2]) - shared_clean = self.parse_smaps_size_line (lines[line_idx + 3]) - shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4]) - private_clean = self.parse_smaps_size_line (lines[line_idx + 5]) - private_dirty = self.parse_smaps_size_line (lines[line_idx + 6]) - name = name.strip () + num_lines = len (lines) + line_idx = 0 + + # 08065000-08067000 rw-p 0001c000 03:01 147613 /opt/gnome/bin/evolution-2.6 + # Size: 8 kB + # Rss: 8 kB + # Shared_Clean: 0 kB + # Shared_Dirty: 0 kB + # Private_Clean: 8 kB + # Private_Dirty: 0 kB + + while num_lines > 0: + fields = lines[line_idx].split (" ", 5) + if len (fields) == 6: + (offsets, permissions, bin_permissions, device, inode, name) = fields + else: + (offsets, permissions, bin_permissions, device, inode) = fields + name = "" + + size = self.parse_smaps_size_line (lines[line_idx + 1]) + rss = self.parse_smaps_size_line (lines[line_idx + 2]) + shared_clean = self.parse_smaps_size_line (lines[line_idx + 3]) + shared_dirty = self.parse_smaps_size_line (lines[line_idx + 4]) + private_clean = self.parse_smaps_size_line (lines[line_idx + 5]) + private_dirty = self.parse_smaps_size_line (lines[line_idx + 6]) + name = name.strip () - mapping = Mapping (size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name) - self.mappings.append (mapping) + mapping = Mapping (size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name) + self.mappings.append (mapping) - num_lines -= 7 - line_idx += 7 + num_lines -= 7 + line_idx += 7 - # Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field - def parse_smaps_size_line (self, line): - # Rss: 8 kB - fields = line.split () - return int(fields[1]) + # Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field + def parse_smaps_size_line (self, line): + # Rss: 8 kB + fields = line.split () + return int(fields[1]) class Mapping: - def __init__ (self, size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name): - self.size = size - self.rss = rss - self.shared_clean = shared_clean - self.shared_dirty = shared_dirty - self.private_clean = private_clean - self.private_dirty = private_dirty - self.permissions = permissions - self.name = name + def __init__ (self, size, rss, shared_clean, shared_dirty, private_clean, private_dirty, permissions, name): + self.size = size + self.rss = rss + self.shared_clean = shared_clean + self.shared_dirty = shared_dirty + self.private_clean = private_clean + self.private_dirty = private_dirty + self.permissions = permissions + self.name = name # Parse /proc/PID/maps file to get the clean memory usage by process, # we avoid lines with backed-files class ProcMaps: - - clean_size = 0 - - def __init__(self, pid): - mapfile = "/proc/%s/maps" % pid + + clean_size = 0 + + def __init__(self, pid): + mapfile = "/proc/%s/maps" % pid - try: - infile = open(mapfile, "r") - except: - print "Error trying " + mapfile - return None - - sum = 0 - to_data_do = { - "[anon]": self.parse_size_line, - "[heap]": self.parse_size_line - } - - for line in infile: - arr = line.split() - - # Just parse writable mapped areas - if arr[1][1] != "w": - continue - - if len(arr) == 6: - # if we got a backed-file we skip this info - if os.path.isfile(arr[5]): - continue - else: - line_size = to_data_do.get(arr[5], self.skip)(line) - sum += line_size - else: - line_size = self.parse_size_line(line) - sum += line_size - - infile.close() - self.clean_size = sum - - def skip(self, line): - return 0 - - # Parse a maps line and return the mapped size - def parse_size_line(self, line): - start, end = line.split()[0].split('-') - size = int(end, 16) - int(start, 16) - return size + try: + infile = open(mapfile, "r") + except: + print "Error trying " + mapfile + return None + + sum = 0 + to_data_do = { + "[anon]": self.parse_size_line, + "[heap]": self.parse_size_line + } + + for line in infile: + arr = line.split() + + # Just parse writable mapped areas + if arr[1][1] != "w": + continue + + if len(arr) == 6: + # if we got a backed-file we skip this info + if os.path.isfile(arr[5]): + continue + else: + line_size = to_data_do.get(arr[5], self.skip)(line) + sum += line_size + else: + line_size = self.parse_size_line(line) + sum += line_size + + infile.close() + self.clean_size = sum + + def skip(self, line): + return 0 + + # Parse a maps line and return the mapped size + def parse_size_line(self, line): + start, end = line.split()[0].split('-') + size = int(end, 16) - int(start, 16) + return size diff --git a/shell/console/terminal.py b/shell/console/terminal.py index 6a92661..038c36d 100644 --- a/shell/console/terminal.py +++ b/shell/console/terminal.py @@ -3,141 +3,141 @@ import vte import pango class Terminal(gtk.HBox): - def __init__(self): - gtk.HBox.__init__(self, False, 4) - - self._vte = vte.Terminal() - self._configure_vte() - self._vte.set_size(30, 5) - self._vte.set_size_request(200, 450) - self._vte.show() - self.pack_start(self._vte) - - self._scrollbar = gtk.VScrollbar(self._vte.get_adjustment()) - self._scrollbar.show() - self.pack_start(self._scrollbar, False, False, 0) - - self._vte.connect("child-exited", lambda term: term.fork_command()) - - self._vte.fork_command() - - def _configure_vte(self): - self._vte.set_font(pango.FontDescription('Monospace 10')) - self._vte.set_colors(gtk.gdk.color_parse ('#AAAAAA'), - gtk.gdk.color_parse ('#000000'), - []) - self._vte.set_cursor_blinks(False) - self._vte.set_audible_bell(False) - self._vte.set_scrollback_lines(100) - self._vte.set_allow_bold(True) - self._vte.set_scroll_on_keystroke(False) - self._vte.set_scroll_on_output(False) - self._vte.set_emulation('xterm') - self._vte.set_visible_bell(False) - - def on_gconf_notification(self, client, cnxn_id, entry, what): - self.reconfigure_vte() - - def on_vte_button_press(self, term, event): - if event.button == 3: - self.do_popup(event) - return True - - def on_vte_popup_menu(self, term): - pass + def __init__(self): + gtk.HBox.__init__(self, False, 4) + + self._vte = vte.Terminal() + self._configure_vte() + self._vte.set_size(30, 5) + self._vte.set_size_request(200, 450) + self._vte.show() + self.pack_start(self._vte) + + self._scrollbar = gtk.VScrollbar(self._vte.get_adjustment()) + self._scrollbar.show() + self.pack_start(self._scrollbar, False, False, 0) + + self._vte.connect("child-exited", lambda term: term.fork_command()) + + self._vte.fork_command() + + def _configure_vte(self): + self._vte.set_font(pango.FontDescription('Monospace 10')) + self._vte.set_colors(gtk.gdk.color_parse ('#AAAAAA'), + gtk.gdk.color_parse ('#000000'), + []) + self._vte.set_cursor_blinks(False) + self._vte.set_audible_bell(False) + self._vte.set_scrollback_lines(100) + self._vte.set_allow_bold(True) + self._vte.set_scroll_on_keystroke(False) + self._vte.set_scroll_on_output(False) + self._vte.set_emulation('xterm') + self._vte.set_visible_bell(False) + + def on_gconf_notification(self, client, cnxn_id, entry, what): + self.reconfigure_vte() + + def on_vte_button_press(self, term, event): + if event.button == 3: + self.do_popup(event) + return True + + def on_vte_popup_menu(self, term): + pass class Multiple: - - page_number = 0 - - def __init__(self): - self.notebook = gtk.Notebook() - self.add_new_terminal() - - open_terminal = gtk.Button('Open a new terminal') - open_terminal.connect("clicked", self.add_new_terminal) - open_terminal.show() - - self.notebook.show() - - self.main_vbox = gtk.VBox(False, 3) - self.main_vbox.pack_start(open_terminal, True, True, 2) - self.main_vbox.pack_start(self.notebook, True, True, 2) - - self.main_vbox.show_all() - - # Remove a page from the notebook - def close_terminal(self, button, child): - page = self.notebook.page_num(child) - - if page != -1: - self.notebook.remove_page(page) - - - pages = self.notebook.get_n_pages() - if pages <= 0: - self.page_number = 0 - self.add_new_terminal() - - # Need to refresh the widget -- - # This forces the widget to redraw itself. - self.notebook.queue_draw_area(0, 0, -1, -1) - - def add_icon_to_button(self, button): - iconBox = gtk.HBox(False, 0) - image = gtk.Image() - image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) - gtk.Button.set_relief(button, gtk.RELIEF_NONE) - - settings = gtk.Widget.get_settings (button) - (w,h) = gtk.icon_size_lookup_for_settings (settings, gtk.ICON_SIZE_MENU) - gtk.Widget.set_size_request (button, w + 4, h + 4) - image.show() - iconBox.pack_start(image, True, False, 0) - button.add(iconBox) - iconBox.show() - - def add_new_terminal(self, *arguments, **keywords): - self.page_number += 1 - - terminal = Terminal() - terminal.show() - - eventBox = self.create_custom_tab("Term %d" % self.page_number, terminal) - self.notebook.append_page(terminal, eventBox) - - # Set the new page - pages = gtk.Notebook.get_n_pages(self.notebook) - self.notebook.set_current_page(pages - 1) - return True - - def create_custom_tab(self, text, child): - eventBox = gtk.EventBox() - tabBox = gtk.HBox(False, 2) - tabLabel = gtk.Label(text) - - tabButton = gtk.Button() - tabButton.connect('clicked', self.close_terminal, child) - - # Add a picture on a button - self.add_icon_to_button(tabButton) - iconBox = gtk.HBox(False, 0) - - eventBox.show() - tabButton.show() - tabLabel.show() - - tabBox.pack_start(tabLabel, False) - tabBox.pack_start(tabButton, False) - - tabBox.show_all() - eventBox.add(tabBox) - - return eventBox + + page_number = 0 + + def __init__(self): + self.notebook = gtk.Notebook() + self.add_new_terminal() + + open_terminal = gtk.Button('Open a new terminal') + open_terminal.connect("clicked", self.add_new_terminal) + open_terminal.show() + + self.notebook.show() + + self.main_vbox = gtk.VBox(False, 3) + self.main_vbox.pack_start(open_terminal, True, True, 2) + self.main_vbox.pack_start(self.notebook, True, True, 2) + + self.main_vbox.show_all() + + # Remove a page from the notebook + def close_terminal(self, button, child): + page = self.notebook.page_num(child) + + if page != -1: + self.notebook.remove_page(page) + + + pages = self.notebook.get_n_pages() + if pages <= 0: + self.page_number = 0 + self.add_new_terminal() + + # Need to refresh the widget -- + # This forces the widget to redraw itself. + self.notebook.queue_draw_area(0, 0, -1, -1) + + def add_icon_to_button(self, button): + iconBox = gtk.HBox(False, 0) + image = gtk.Image() + image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) + gtk.Button.set_relief(button, gtk.RELIEF_NONE) + + settings = gtk.Widget.get_settings (button) + (w,h) = gtk.icon_size_lookup_for_settings (settings, gtk.ICON_SIZE_MENU) + gtk.Widget.set_size_request (button, w + 4, h + 4) + image.show() + iconBox.pack_start(image, True, False, 0) + button.add(iconBox) + iconBox.show() + + def add_new_terminal(self, *arguments, **keywords): + self.page_number += 1 + + terminal = Terminal() + terminal.show() + + eventBox = self.create_custom_tab("Term %d" % self.page_number, terminal) + self.notebook.append_page(terminal, eventBox) + + # Set the new page + pages = gtk.Notebook.get_n_pages(self.notebook) + self.notebook.set_current_page(pages - 1) + return True + + def create_custom_tab(self, text, child): + eventBox = gtk.EventBox() + tabBox = gtk.HBox(False, 2) + tabLabel = gtk.Label(text) + + tabButton = gtk.Button() + tabButton.connect('clicked', self.close_terminal, child) + + # Add a picture on a button + self.add_icon_to_button(tabButton) + iconBox = gtk.HBox(False, 0) + + eventBox.show() + tabButton.show() + tabLabel.show() + + tabBox.pack_start(tabLabel, False) + tabBox.pack_start(tabButton, False) + + tabBox.show_all() + eventBox.add(tabBox) + + return eventBox class Interface: - def __init__(self): - multiple = Multiple() - self.widget = multiple.main_vbox - \ No newline at end of file + def __init__(self): + multiple = Multiple() + self.widget = multiple.main_vbox + \ No newline at end of file diff --git a/shell/model/BuddyModel.py b/shell/model/BuddyModel.py index 651e911..8e175e7 100644 --- a/shell/model/BuddyModel.py +++ b/shell/model/BuddyModel.py @@ -21,122 +21,122 @@ import gobject _NOT_PRESENT_COLOR = "#888888,#BBBBBB" class BuddyModel(gobject.GObject): - __gsignals__ = { - 'appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), - 'color-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])), - 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([])), - 'current-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT])) - } - - def __init__(self, name=None, buddy=None): - if name and buddy: - raise RuntimeError("Must specify only _one_ of name or buddy.") - - gobject.GObject.__init__(self) - - self._ba_handler = None - self._pc_handler = None - self._dis_handler = None - self._bic_handler = None - self._cac_handler = None - - self._pservice = PresenceService.get_instance() - - self._buddy = None - - # If given just a name, try to get the buddy from the PS first - if not buddy: - self._name = name - # FIXME: use public key, not name - buddy = self._pservice.get_buddy_by_name(self._name) - - # If successful, copy properties from the PS buddy object - if buddy: - self.__update_buddy(buddy) - else: - # Otherwise, connect to the PS's buddy-appeared signal and - # wait for the buddy to appear - self._ba_handler = self._pservice.connect('buddy-appeared', - self.__buddy_appeared_cb) - self._name = name - # Set color to 'inactive'/'disconnected' - self.__set_color_from_string(_NOT_PRESENT_COLOR) - - def __set_color_from_string(self, color_string): - self._color = IconColor(color_string) - - def get_name(self): - return self._name - - def get_color(self): - return self._color - - def get_buddy(self): - return self._buddy - - def is_present(self): - if self._buddy: - return True - return False - - def get_current_activity(self): - if self._buddy: - return self._buddy.get_current_activity() - return None - - def __update_buddy(self, buddy): - if not buddy: - raise ValueError("Buddy cannot be None.") - - self._buddy = buddy - self._name = self._buddy.get_name() - self.__set_color_from_string(self._buddy.get_color()) - - self._pc_handler = self._buddy.connect('property-changed', self.__buddy_property_changed_cb) - self._dis_handler = self._buddy.connect('disappeared', self.__buddy_disappeared_cb) - self._bic_handler = self._buddy.connect('icon-changed', self.__buddy_icon_changed_cb) - self._cac_handler = self._buddy.connect('current-activity-changed', self.__buddy_current_activity_changed_cb) - - def __buddy_appeared_cb(self, pservice, buddy): - # FIXME: use public key rather than buddy name - if self._buddy or buddy.get_name() != self._name: - return - - if self._ba_handler: - # Once we have the buddy, we no longer need to - # monitor buddy-appeared events - self._pservice.disconnect(self._ba_handler) - self._ba_handler = None - - self.__update_buddy(buddy) - self.emit('appeared') - - def __buddy_property_changed_cb(self, buddy, keys): - if not self._buddy: - return - if 'color' in keys: - self.__set_color_from_string(self._buddy.get_color()) - self.emit('color-changed', self.get_color()) - - def __buddy_disappeared_cb(self, buddy): - if buddy != self._buddy: - return - self._buddy.disconnect(self._pc_handler) - self._buddy.disconnect(self._dis_handler) - self._buddy.disconnect(self._bic_handler) - self._buddy.disconnect(self._cac_handler) - self.__set_color_from_string(_NOT_PRESENT_COLOR) - self.emit('disappeared') - self._buddy = None - - def __buddy_icon_changed_cb(self, buddy): - self.emit('icon-changed') - - def __buddy_current_activity_changed_cb(self, buddy, activity=None): - if not self._buddy: - return - self.emit('current-activity-changed', activity) + __gsignals__ = { + 'appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'color-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), + 'current-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, name=None, buddy=None): + if name and buddy: + raise RuntimeError("Must specify only _one_ of name or buddy.") + + gobject.GObject.__init__(self) + + self._ba_handler = None + self._pc_handler = None + self._dis_handler = None + self._bic_handler = None + self._cac_handler = None + + self._pservice = PresenceService.get_instance() + + self._buddy = None + + # If given just a name, try to get the buddy from the PS first + if not buddy: + self._name = name + # FIXME: use public key, not name + buddy = self._pservice.get_buddy_by_name(self._name) + + # If successful, copy properties from the PS buddy object + if buddy: + self.__update_buddy(buddy) + else: + # Otherwise, connect to the PS's buddy-appeared signal and + # wait for the buddy to appear + self._ba_handler = self._pservice.connect('buddy-appeared', + self.__buddy_appeared_cb) + self._name = name + # Set color to 'inactive'/'disconnected' + self.__set_color_from_string(_NOT_PRESENT_COLOR) + + def __set_color_from_string(self, color_string): + self._color = IconColor(color_string) + + def get_name(self): + return self._name + + def get_color(self): + return self._color + + def get_buddy(self): + return self._buddy + + def is_present(self): + if self._buddy: + return True + return False + + def get_current_activity(self): + if self._buddy: + return self._buddy.get_current_activity() + return None + + def __update_buddy(self, buddy): + if not buddy: + raise ValueError("Buddy cannot be None.") + + self._buddy = buddy + self._name = self._buddy.get_name() + self.__set_color_from_string(self._buddy.get_color()) + + self._pc_handler = self._buddy.connect('property-changed', self.__buddy_property_changed_cb) + self._dis_handler = self._buddy.connect('disappeared', self.__buddy_disappeared_cb) + self._bic_handler = self._buddy.connect('icon-changed', self.__buddy_icon_changed_cb) + self._cac_handler = self._buddy.connect('current-activity-changed', self.__buddy_current_activity_changed_cb) + + def __buddy_appeared_cb(self, pservice, buddy): + # FIXME: use public key rather than buddy name + if self._buddy or buddy.get_name() != self._name: + return + + if self._ba_handler: + # Once we have the buddy, we no longer need to + # monitor buddy-appeared events + self._pservice.disconnect(self._ba_handler) + self._ba_handler = None + + self.__update_buddy(buddy) + self.emit('appeared') + + def __buddy_property_changed_cb(self, buddy, keys): + if not self._buddy: + return + if 'color' in keys: + self.__set_color_from_string(self._buddy.get_color()) + self.emit('color-changed', self.get_color()) + + def __buddy_disappeared_cb(self, buddy): + if buddy != self._buddy: + return + self._buddy.disconnect(self._pc_handler) + self._buddy.disconnect(self._dis_handler) + self._buddy.disconnect(self._bic_handler) + self._buddy.disconnect(self._cac_handler) + self.__set_color_from_string(_NOT_PRESENT_COLOR) + self.emit('disappeared') + self._buddy = None + + def __buddy_icon_changed_cb(self, buddy): + self.emit('icon-changed') + + def __buddy_current_activity_changed_cb(self, buddy, activity=None): + if not self._buddy: + return + self.emit('current-activity-changed', activity) diff --git a/shell/model/Friends.py b/shell/model/Friends.py index ab1935b..36c443f 100644 --- a/shell/model/Friends.py +++ b/shell/model/Friends.py @@ -24,61 +24,61 @@ from sugar import env import logging class Friends(gobject.GObject): - __gsignals__ = { - 'friend-added': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([object])), - 'friend-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([str])), - } - - def __init__(self): - gobject.GObject.__init__(self) - - self._friends = {} - self._path = os.path.join(env.get_profile_path(), 'friends') - - self.load() - - def has_buddy(self, buddy): - return self._friends.has_key(buddy.get_name()) - - def add_friend(self, buddy_info): - self._friends[buddy_info.get_name()] = buddy_info - self.emit('friend-added', buddy_info) - - def make_friend(self, buddy): - if not self.has_buddy(buddy): - self.add_friend(BuddyModel(buddy=buddy)) - self.save() - - def remove(self, buddy_info): - del self._friends[buddy_info.get_name()] - self.save() - self.emit('friend-removed', buddy_info.get_name()) - - def __iter__(self): - return self._friends.values().__iter__() - - def load(self): - cp = ConfigParser() - - try: - success = cp.read([self._path]) - if success: - for name in cp.sections(): - buddy = BuddyModel(name) - self.add_friend(buddy) - except Exception, exc: - logging.error("Error parsing friends file: %s" % exc) - - def save(self): - cp = ConfigParser() - - for friend in self: - section = friend.get_name() - cp.add_section(section) - cp.set(section, 'color', friend.get_color().to_string()) - - fileobject = open(self._path, 'w') - cp.write(fileobject) - fileobject.close() + __gsignals__ = { + 'friend-added': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([object])), + 'friend-removed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([str])), + } + + def __init__(self): + gobject.GObject.__init__(self) + + self._friends = {} + self._path = os.path.join(env.get_profile_path(), 'friends') + + self.load() + + def has_buddy(self, buddy): + return self._friends.has_key(buddy.get_name()) + + def add_friend(self, buddy_info): + self._friends[buddy_info.get_name()] = buddy_info + self.emit('friend-added', buddy_info) + + def make_friend(self, buddy): + if not self.has_buddy(buddy): + self.add_friend(BuddyModel(buddy=buddy)) + self.save() + + def remove(self, buddy_info): + del self._friends[buddy_info.get_name()] + self.save() + self.emit('friend-removed', buddy_info.get_name()) + + def __iter__(self): + return self._friends.values().__iter__() + + def load(self): + cp = ConfigParser() + + try: + success = cp.read([self._path]) + if success: + for name in cp.sections(): + buddy = BuddyModel(name) + self.add_friend(buddy) + except Exception, exc: + logging.error("Error parsing friends file: %s" % exc) + + def save(self): + cp = ConfigParser() + + for friend in self: + section = friend.get_name() + cp.add_section(section) + cp.set(section, 'color', friend.get_color().to_string()) + + fileobject = open(self._path, 'w') + cp.write(fileobject) + fileobject.close() diff --git a/shell/model/Invites.py b/shell/model/Invites.py index bc947a9..7bc0af0 100644 --- a/shell/model/Invites.py +++ b/shell/model/Invites.py @@ -17,38 +17,38 @@ import gobject class Invite: - def __init__(self, issuer, bundle_id, activity_id): - self._issuer = issuer - self._activity_id = activity_id - self._bundle_id = bundle_id + def __init__(self, issuer, bundle_id, activity_id): + self._issuer = issuer + self._activity_id = activity_id + self._bundle_id = bundle_id - def get_activity_id(self): - return self._activity_id + def get_activity_id(self): + return self._activity_id - def get_bundle_id(self): - return self._bundle_id + def get_bundle_id(self): + return self._bundle_id class Invites(gobject.GObject): - __gsignals__ = { - 'invite-added': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([object])), - 'invite-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([object])), - } + __gsignals__ = { + 'invite-added': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([object])), + 'invite-removed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([object])), + } - def __init__(self): - gobject.GObject.__init__(self) + def __init__(self): + gobject.GObject.__init__(self) - self._list = [] + self._list = [] - def add_invite(self, issuer, bundle_id, activity_id): - invite = Invite(issuer, bundle_id, activity_id) - self._list.append(invite) - self.emit('invite-added', invite) + def add_invite(self, issuer, bundle_id, activity_id): + invite = Invite(issuer, bundle_id, activity_id) + self._list.append(invite) + self.emit('invite-added', invite) - def remove_invite(self, invite): - self._list.remove(invite) - self.emit('invite-removed', invite) + def remove_invite(self, invite): + self._list.remove(invite) + self.emit('invite-removed', invite) - def __iter__(self): - return self._list.__iter__() + def __iter__(self): + return self._list.__iter__() diff --git a/shell/model/MeshModel.py b/shell/model/MeshModel.py index 0ecf2b8..5ab36e9 100644 --- a/shell/model/MeshModel.py +++ b/shell/model/MeshModel.py @@ -21,137 +21,137 @@ from sugar.presence import PresenceService from model.BuddyModel import BuddyModel class ActivityModel: - def __init__(self, activity, bundle, service): - self._service = service - self._activity = activity - - def get_id(self): - return self._activity.get_id() - - def get_icon_name(self): - return bundle.get_icon() - - def get_color(self): - return IconColor(self._activity.get_color()) - - def get_service(self): - return self._service + def __init__(self, activity, bundle, service): + self._service = service + self._activity = activity + + def get_id(self): + return self._activity.get_id() + + def get_icon_name(self): + return bundle.get_icon() + + def get_color(self): + return IconColor(self._activity.get_color()) + + def get_service(self): + return self._service class MeshModel(gobject.GObject): - __gsignals__ = { - 'activity-added': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'activity-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'buddy-added': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'buddy-moved': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT, - gobject.TYPE_PYOBJECT])), - 'buddy-removed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) - } - - def __init__(self, bundle_registry): - gobject.GObject.__init__(self) - - self._activities = {} - self._buddies = {} - self._bundle_registry = bundle_registry - - self._pservice = PresenceService.get_instance() - self._pservice.connect("service-appeared", - self._service_appeared_cb) - self._pservice.connect('activity-disappeared', - self._activity_disappeared_cb) - self._pservice.connect("buddy-appeared", - self._buddy_appeared_cb) - self._pservice.connect("buddy-disappeared", - self._buddy_disappeared_cb) - - # Add any buddies the PS knows about already - for buddy in self._pservice.get_buddies(): - self._buddy_appeared_cb(self._pservice, buddy) - - for service in self._pservice.get_services(): - self._check_service(service) - - def get_activities(self): - return self._activities.values() - - def get_buddies(self): - return self._buddies.values() - - def _buddy_activity_changed_cb(self, buddy, cur_activity): - if not self._buddies.has_key(buddy.get_name()): - return - buddy_model = self._buddies[buddy.get_name()] - if cur_activity == None: - self.emit('buddy-moved', buddy_model, None) - else: - self._notify_buddy_change(buddy_model, cur_activity) - - def _notify_buddy_change(self, buddy_model, cur_activity): - if self._activities.has_key(cur_activity.get_id()): - activity_model = self._activities[cur_activity.get_id()] - self.emit('buddy-moved', buddy_model, activity_model) - - def _buddy_appeared_cb(self, pservice, buddy): - model = BuddyModel(buddy=buddy) - if self._buddies.has_key(model.get_name()): - del model - return - - model.connect('current-activity-changed', - self._buddy_activity_changed_cb) - self._buddies[model.get_name()] = model - self.emit('buddy-added', model) - - cur_activity = buddy.get_current_activity() - if cur_activity: - self._notify_buddy_change(model, cur_activity) - - def _buddy_disappeared_cb(self, pservice, buddy): - if not self._buddies.has_key(buddy.get_name()): - return - self.emit('buddy-removed', buddy) - del self._buddies[buddy.get_name()] - - def _service_appeared_cb(self, pservice, service): - self._check_service(service) - - def _check_service(self, service): - if self._bundle_registry.get_bundle(service.get_type()) != None: - activity_id = service.get_activity_id() - if not self.has_activity(activity_id): - activity = self._pservice.get_activity(activity_id) - if activity != None: - self.add_activity(activity, service) - - def has_activity(self, activity_id): - return self._activities.has_key(activity_id) - - def get_activity(self, activity_id): - if self.has_activity(activity_id): - return self._activities[activity_id] - else: - return None - - def add_activity(self, activity, service): - bundle = self._bundle_registry.get_bundle(service.get_type()) - model = ActivityModel(activity, bundle, service) - self._activities[model.get_id()] = model - self.emit('activity-added', model) - - for buddy in self._pservice.get_buddies(): - cur_activity = buddy.get_current_activity() - name = buddy.get_name() - if cur_activity == activity and self._buddies.has_key(name): - buddy_model = self._buddies[name] - self.emit('buddy-moved', buddy_model, model) - - def _activity_disappeared_cb(self, pservice, activity): - if self._activities.has_key(activity.get_id()): - activity_model = self._activities[activity.get_id()] - self.emit('activity-removed', activity_model) - del self._activities[activity.get_id()] + __gsignals__ = { + 'activity-added': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + 'activity-removed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + 'buddy-added': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + 'buddy-moved': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT, + gobject.TYPE_PYOBJECT])), + 'buddy-removed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, bundle_registry): + gobject.GObject.__init__(self) + + self._activities = {} + self._buddies = {} + self._bundle_registry = bundle_registry + + self._pservice = PresenceService.get_instance() + self._pservice.connect("service-appeared", + self._service_appeared_cb) + self._pservice.connect('activity-disappeared', + self._activity_disappeared_cb) + self._pservice.connect("buddy-appeared", + self._buddy_appeared_cb) + self._pservice.connect("buddy-disappeared", + self._buddy_disappeared_cb) + + # Add any buddies the PS knows about already + for buddy in self._pservice.get_buddies(): + self._buddy_appeared_cb(self._pservice, buddy) + + for service in self._pservice.get_services(): + self._check_service(service) + + def get_activities(self): + return self._activities.values() + + def get_buddies(self): + return self._buddies.values() + + def _buddy_activity_changed_cb(self, buddy, cur_activity): + if not self._buddies.has_key(buddy.get_name()): + return + buddy_model = self._buddies[buddy.get_name()] + if cur_activity == None: + self.emit('buddy-moved', buddy_model, None) + else: + self._notify_buddy_change(buddy_model, cur_activity) + + def _notify_buddy_change(self, buddy_model, cur_activity): + if self._activities.has_key(cur_activity.get_id()): + activity_model = self._activities[cur_activity.get_id()] + self.emit('buddy-moved', buddy_model, activity_model) + + def _buddy_appeared_cb(self, pservice, buddy): + model = BuddyModel(buddy=buddy) + if self._buddies.has_key(model.get_name()): + del model + return + + model.connect('current-activity-changed', + self._buddy_activity_changed_cb) + self._buddies[model.get_name()] = model + self.emit('buddy-added', model) + + cur_activity = buddy.get_current_activity() + if cur_activity: + self._notify_buddy_change(model, cur_activity) + + def _buddy_disappeared_cb(self, pservice, buddy): + if not self._buddies.has_key(buddy.get_name()): + return + self.emit('buddy-removed', buddy) + del self._buddies[buddy.get_name()] + + def _service_appeared_cb(self, pservice, service): + self._check_service(service) + + def _check_service(self, service): + if self._bundle_registry.get_bundle(service.get_type()) != None: + activity_id = service.get_activity_id() + if not self.has_activity(activity_id): + activity = self._pservice.get_activity(activity_id) + if activity != None: + self.add_activity(activity, service) + + def has_activity(self, activity_id): + return self._activities.has_key(activity_id) + + def get_activity(self, activity_id): + if self.has_activity(activity_id): + return self._activities[activity_id] + else: + return None + + def add_activity(self, activity, service): + bundle = self._bundle_registry.get_bundle(service.get_type()) + model = ActivityModel(activity, bundle, service) + self._activities[model.get_id()] = model + self.emit('activity-added', model) + + for buddy in self._pservice.get_buddies(): + cur_activity = buddy.get_current_activity() + name = buddy.get_name() + if cur_activity == activity and self._buddies.has_key(name): + buddy_model = self._buddies[name] + self.emit('buddy-moved', buddy_model, model) + + def _activity_disappeared_cb(self, pservice, activity): + if self._activities.has_key(activity.get_id()): + activity_model = self._activities[activity.get_id()] + self.emit('activity-removed', activity_model) + del self._activities[activity.get_id()] diff --git a/shell/model/Owner.py b/shell/model/Owner.py index 7d83ab2..49386a9 100644 --- a/shell/model/Owner.py +++ b/shell/model/Owner.py @@ -31,90 +31,90 @@ from model.Invites import Invites PRESENCE_SERVICE_TYPE = "_presence_olpc._tcp" class ShellOwner(object): - """Class representing the owner of this machine/instance. This class - runs in the shell and serves up the buddy icon and other stuff. It's the - server portion of the Owner, paired with the client portion in Buddy.py.""" - def __init__(self): - self._nick = profile.get_nick_name() - user_dir = env.get_profile_path() - - self._icon = None - self._icon_hash = "" - for fname in os.listdir(user_dir): - if not fname.startswith("buddy-icon."): - continue - fd = open(os.path.join(user_dir, fname), "r") - self._icon = fd.read() - if self._icon: - # Get the icon's hash - import md5, binascii - digest = md5.new(self._icon).digest() - self._icon_hash = util.printable_hash(digest) - fd.close() - break - - self._pservice = PresenceService.get_instance() - - self._invites = Invites() - - self._last_activity_update = time.time() - self._pending_activity_update_timer = None - self._pending_activity_update = None - - def get_invites(self): - return self._invites - - def get_name(self): - return self._nick - - def announce(self): - # Create and announce our presence - color = profile.get_color() - props = {'color': color.to_string(), 'icon-hash': self._icon_hash} - self._service = self._pservice.register_service(self._nick, - PRESENCE_SERVICE_TYPE, properties=props) - logging.debug("Owner '%s' using port %d" % (self._nick, self._service.get_port())) - self._icon_stream = Stream.Stream.new_from_service(self._service) - self._icon_stream.register_reader_handler(self._handle_buddy_icon_request, "get_buddy_icon") - self._icon_stream.register_reader_handler(self._handle_invite, "invite") - - def _handle_buddy_icon_request(self): - """XMLRPC method, return the owner's icon encoded with base64.""" - if self._icon: - return base64.b64encode(self._icon) - return "" - - def _handle_invite(self, issuer, bundle_id, activity_id): - """XMLRPC method, called when the owner is invited to an activity.""" - self._invites.add_invite(issuer, bundle_id, activity_id) - return '' - - def __update_advertised_current_activity_cb(self): - self._last_activity_update = time.time() - self._pending_activity_update_timer = None - if self._pending_activity_update: - logging.debug("*** Updating current activity to %s" % self._pending_activity_update) - self._service.set_published_value('curact', dbus.String(self._pending_activity_update)) - return False - - def set_current_activity(self, activity_id): - """Update our presence service with the latest activity, but no - more frequently than every 30 seconds""" - self._pending_activity_update = activity_id - # If there's no pending update, we must not have updated it in the - # last 30 seconds (except for the initial update, hence we also check - # for the last update) - if not self._pending_activity_update_timer or time.time() - self._last_activity_update > 30: - self.__update_advertised_current_activity_cb() - return - - # If we have a pending update already, we have nothing left to do - if self._pending_activity_update_timer: - return - - # Otherwise, we start a timer to update the activity at the next - # interval, which should be 30 seconds from the last update, or if that - # is in the past already, then now - next = 30 - max(30, time.time() - self._last_activity_update) - self._pending_activity_update_timer = gobject.timeout_add(next * 1000, - self.__update_advertised_current_activity_cb) + """Class representing the owner of this machine/instance. This class + runs in the shell and serves up the buddy icon and other stuff. It's the + server portion of the Owner, paired with the client portion in Buddy.py.""" + def __init__(self): + self._nick = profile.get_nick_name() + user_dir = env.get_profile_path() + + self._icon = None + self._icon_hash = "" + for fname in os.listdir(user_dir): + if not fname.startswith("buddy-icon."): + continue + fd = open(os.path.join(user_dir, fname), "r") + self._icon = fd.read() + if self._icon: + # Get the icon's hash + import md5, binascii + digest = md5.new(self._icon).digest() + self._icon_hash = util.printable_hash(digest) + fd.close() + break + + self._pservice = PresenceService.get_instance() + + self._invites = Invites() + + self._last_activity_update = time.time() + self._pending_activity_update_timer = None + self._pending_activity_update = None + + def get_invites(self): + return self._invites + + def get_name(self): + return self._nick + + def announce(self): + # Create and announce our presence + color = profile.get_color() + props = {'color': color.to_string(), 'icon-hash': self._icon_hash} + self._service = self._pservice.register_service(self._nick, + PRESENCE_SERVICE_TYPE, properties=props) + logging.debug("Owner '%s' using port %d" % (self._nick, self._service.get_port())) + self._icon_stream = Stream.Stream.new_from_service(self._service) + self._icon_stream.register_reader_handler(self._handle_buddy_icon_request, "get_buddy_icon") + self._icon_stream.register_reader_handler(self._handle_invite, "invite") + + def _handle_buddy_icon_request(self): + """XMLRPC method, return the owner's icon encoded with base64.""" + if self._icon: + return base64.b64encode(self._icon) + return "" + + def _handle_invite(self, issuer, bundle_id, activity_id): + """XMLRPC method, called when the owner is invited to an activity.""" + self._invites.add_invite(issuer, bundle_id, activity_id) + return '' + + def __update_advertised_current_activity_cb(self): + self._last_activity_update = time.time() + self._pending_activity_update_timer = None + if self._pending_activity_update: + logging.debug("*** Updating current activity to %s" % self._pending_activity_update) + self._service.set_published_value('curact', dbus.String(self._pending_activity_update)) + return False + + def set_current_activity(self, activity_id): + """Update our presence service with the latest activity, but no + more frequently than every 30 seconds""" + self._pending_activity_update = activity_id + # If there's no pending update, we must not have updated it in the + # last 30 seconds (except for the initial update, hence we also check + # for the last update) + if not self._pending_activity_update_timer or time.time() - self._last_activity_update > 30: + self.__update_advertised_current_activity_cb() + return + + # If we have a pending update already, we have nothing left to do + if self._pending_activity_update_timer: + return + + # Otherwise, we start a timer to update the activity at the next + # interval, which should be 30 seconds from the last update, or if that + # is in the past already, then now + next = 30 - max(30, time.time() - self._last_activity_update) + self._pending_activity_update_timer = gobject.timeout_add(next * 1000, + self.__update_advertised_current_activity_cb) diff --git a/shell/model/ShellModel.py b/shell/model/ShellModel.py index d2d6e9d..95ed344 100644 --- a/shell/model/ShellModel.py +++ b/shell/model/ShellModel.py @@ -24,45 +24,45 @@ from model.Owner import ShellOwner from sugar import env class ShellModel: - def __init__(self): - self._current_activity = None + def __init__(self): + self._current_activity = None - self._bundle_registry = BundleRegistry() + self._bundle_registry = BundleRegistry() - PresenceService.start() - self._pservice = PresenceService.get_instance() + PresenceService.start() + self._pservice = PresenceService.get_instance() - self._owner = ShellOwner() - self._owner.announce() + self._owner = ShellOwner() + self._owner.announce() - self._friends = Friends() - self._mesh = MeshModel(self._bundle_registry) + self._friends = Friends() + self._mesh = MeshModel(self._bundle_registry) - path = os.path.expanduser('~/Activities') - self._bundle_registry.add_search_path(path) + path = os.path.expanduser('~/Activities') + self._bundle_registry.add_search_path(path) - for path in env.get_data_dirs(): - bundles_path = os.path.join(path, 'activities') - self._bundle_registry.add_search_path(bundles_path) + for path in env.get_data_dirs(): + bundles_path = os.path.join(path, 'activities') + self._bundle_registry.add_search_path(bundles_path) - def get_bundle_registry(self): - return self._bundle_registry + def get_bundle_registry(self): + return self._bundle_registry - def get_mesh(self): - return self._mesh + def get_mesh(self): + return self._mesh - def get_friends(self): - return self._friends + def get_friends(self): + return self._friends - def get_invites(self): - return self._owner.get_invites() + def get_invites(self): + return self._owner.get_invites() - def get_owner(self): - return self._owner + def get_owner(self): + return self._owner - def set_current_activity(self, activity_id): - self._current_activity = activity_id - self._owner.set_current_activity(activity_id) + def set_current_activity(self, activity_id): + self._current_activity = activity_id + self._owner.set_current_activity(activity_id) - def get_current_activity(self): - return self._current_activity + def get_current_activity(self): + return self._current_activity diff --git a/shell/view/ActivityHost.py b/shell/view/ActivityHost.py index a7e177d..f87b65b 100644 --- a/shell/view/ActivityHost.py +++ b/shell/view/ActivityHost.py @@ -27,122 +27,122 @@ from sugar.chat import ActivityChat import OverlayWindow class ActivityChatWindow(gtk.Window): - def __init__(self, gdk_window, chat_widget): - gtk.Window.__init__(self) + def __init__(self, gdk_window, chat_widget): + gtk.Window.__init__(self) - self.realize() - self.set_decorated(False) - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) - self.window.set_accept_focus(True) - self.window.set_transient_for(gdk_window) - self.set_position(gtk.WIN_POS_CENTER_ALWAYS) - self.set_default_size(600, 450) + self.realize() + self.set_decorated(False) + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self.window.set_accept_focus(True) + self.window.set_transient_for(gdk_window) + self.set_position(gtk.WIN_POS_CENTER_ALWAYS) + self.set_default_size(600, 450) - self.add(chat_widget) + self.add(chat_widget) class ActivityHost: - def __init__(self, shell_model, window): - self._window = window - self._xid = window.get_xid() - self._pservice = PresenceService.get_instance() - - bus = dbus.SessionBus() - proxy_obj = bus.get_object(Activity.get_service_name(self._xid), - Activity.get_object_path(self._xid)) - - self._activity = dbus.Interface(proxy_obj, Activity.ACTIVITY_INTERFACE) - self._id = self._activity.get_id() - self._type = self._activity.get_type() - self._gdk_window = gtk.gdk.window_foreign_new(self._xid) - - registry = shell_model.get_bundle_registry() - info = registry.get_bundle(self._type) - self._icon_name = info.get_icon() - - try: - self._overlay_window = OverlayWindow.OverlayWindow(self._gdk_window) - win = self._overlay_window.window - except RuntimeError: - self._overlay_window = None - win = self._gdk_window - - self._chat_widget = ActivityChat.ActivityChat(self) - self._chat_window = ActivityChatWindow(win, self._chat_widget) - - self._frame_was_visible = False - - def get_id(self): - return self._id - - def get_title(self): - return self._window.get_name() - - def get_xid(self): - return self._xid - - def get_icon_name(self): - return self._icon_name - - def get_icon_color(self): - activity = self._pservice.get_activity(self._id) - if activity != None: - return IconColor(activity.get_color()) - else: - return profile.get_color() - - def share(self): - self._activity.share() - self._chat_widget.share() - - def invite(self, buddy): - if not self.get_shared(): - self.share() - - issuer = self._pservice.get_owner().get_name() - service = buddy.get_service_of_type("_presence_olpc._tcp") - stream = Stream.Stream.new_from_service(service, start_reader=False) - writer = stream.new_writer(service) - writer.custom_request("invite", None, None, issuer, - self._type, self._id) - - def get_shared(self): - return self._activity.get_shared() - - def get_type(self): - return self._type - - def present(self): - self._window.activate(gtk.get_current_event_time()) - - def close(self): - self._window.close(gtk.get_current_event_time()) - - def show_dialog(self, dialog): - dialog.show() - dialog.window.set_transient_for(self._gdk_window) - - def chat_show(self, frame_was_visible): - if self._overlay_window: - self._overlay_window.show_all() - self._chat_window.show_all() - self._frame_was_visible = frame_was_visible - - def chat_hide(self): - self._chat_window.hide() - if self._overlay_window: - self._overlay_window.hide() - wasvis = self._frame_was_visible - self._frame_was_visible = False - return wasvis - - def is_chat_visible(self): - return self._chat_window.get_property('visible') - - def set_active(self, active): - if not active: - self.chat_hide() - self._frame_was_visible = False - - def destroy(self): - self._chat_window.destroy() - self._frame_was_visible = False + def __init__(self, shell_model, window): + self._window = window + self._xid = window.get_xid() + self._pservice = PresenceService.get_instance() + + bus = dbus.SessionBus() + proxy_obj = bus.get_object(Activity.get_service_name(self._xid), + Activity.get_object_path(self._xid)) + + self._activity = dbus.Interface(proxy_obj, Activity.ACTIVITY_INTERFACE) + self._id = self._activity.get_id() + self._type = self._activity.get_type() + self._gdk_window = gtk.gdk.window_foreign_new(self._xid) + + registry = shell_model.get_bundle_registry() + info = registry.get_bundle(self._type) + self._icon_name = info.get_icon() + + try: + self._overlay_window = OverlayWindow.OverlayWindow(self._gdk_window) + win = self._overlay_window.window + except RuntimeError: + self._overlay_window = None + win = self._gdk_window + + self._chat_widget = ActivityChat.ActivityChat(self) + self._chat_window = ActivityChatWindow(win, self._chat_widget) + + self._frame_was_visible = False + + def get_id(self): + return self._id + + def get_title(self): + return self._window.get_name() + + def get_xid(self): + return self._xid + + def get_icon_name(self): + return self._icon_name + + def get_icon_color(self): + activity = self._pservice.get_activity(self._id) + if activity != None: + return IconColor(activity.get_color()) + else: + return profile.get_color() + + def share(self): + self._activity.share() + self._chat_widget.share() + + def invite(self, buddy): + if not self.get_shared(): + self.share() + + issuer = self._pservice.get_owner().get_name() + service = buddy.get_service_of_type("_presence_olpc._tcp") + stream = Stream.Stream.new_from_service(service, start_reader=False) + writer = stream.new_writer(service) + writer.custom_request("invite", None, None, issuer, + self._type, self._id) + + def get_shared(self): + return self._activity.get_shared() + + def get_type(self): + return self._type + + def present(self): + self._window.activate(gtk.get_current_event_time()) + + def close(self): + self._window.close(gtk.get_current_event_time()) + + def show_dialog(self, dialog): + dialog.show() + dialog.window.set_transient_for(self._gdk_window) + + def chat_show(self, frame_was_visible): + if self._overlay_window: + self._overlay_window.show_all() + self._chat_window.show_all() + self._frame_was_visible = frame_was_visible + + def chat_hide(self): + self._chat_window.hide() + if self._overlay_window: + self._overlay_window.hide() + wasvis = self._frame_was_visible + self._frame_was_visible = False + return wasvis + + def is_chat_visible(self): + return self._chat_window.get_property('visible') + + def set_active(self, active): + if not active: + self.chat_hide() + self._frame_was_visible = False + + def destroy(self): + self._chat_window.destroy() + self._frame_was_visible = False diff --git a/shell/view/BuddyIcon.py b/shell/view/BuddyIcon.py index f8f1013..38f8277 100644 --- a/shell/view/BuddyIcon.py +++ b/shell/view/BuddyIcon.py @@ -18,41 +18,41 @@ from sugar.graphics.menuicon import MenuIcon from view.BuddyMenu import BuddyMenu class BuddyIcon(MenuIcon): - def __init__(self, shell, menu_shell, buddy): - MenuIcon.__init__(self, menu_shell, icon_name='stock-buddy', - color=buddy.get_color()) - - self._shell = shell - self._buddy = buddy - self._buddy.connect('appeared', self._buddy_presence_change_cb) - self._buddy.connect('disappeared', self._buddy_presence_change_cb) - self._buddy.connect('color-changed', self._buddy_presence_change_cb) - - def _buddy_presence_change_cb(self, buddy, color=None): - # Update the icon's color when the buddy comes and goes - self.set_property('color', buddy.get_color()) - - def set_popup_distance(self, distance): - self._popup_distance = distance - - def create_menu(self): - menu = BuddyMenu(self._shell, self._buddy) - menu.connect('action', self._popup_action_cb) - return menu - - def _popup_action_cb(self, popup, action): - self.popdown() - - friends = self._shell.get_model().get_friends() - if action == BuddyMenu.ACTION_REMOVE_FRIEND: - friends.remove(self._buddy) - - ps_buddy = self._buddy.get_buddy() - if ps_buddy == None: - return - - if action == BuddyMenu.ACTION_INVITE: - activity = self._shell.get_current_activity() - activity.invite(ps_buddy) - elif action == BuddyMenu.ACTION_MAKE_FRIEND: - friends.make_friend(ps_buddy) + def __init__(self, shell, menu_shell, buddy): + MenuIcon.__init__(self, menu_shell, icon_name='stock-buddy', + color=buddy.get_color()) + + self._shell = shell + self._buddy = buddy + self._buddy.connect('appeared', self._buddy_presence_change_cb) + self._buddy.connect('disappeared', self._buddy_presence_change_cb) + self._buddy.connect('color-changed', self._buddy_presence_change_cb) + + def _buddy_presence_change_cb(self, buddy, color=None): + # Update the icon's color when the buddy comes and goes + self.set_property('color', buddy.get_color()) + + def set_popup_distance(self, distance): + self._popup_distance = distance + + def create_menu(self): + menu = BuddyMenu(self._shell, self._buddy) + menu.connect('action', self._popup_action_cb) + return menu + + def _popup_action_cb(self, popup, action): + self.popdown() + + friends = self._shell.get_model().get_friends() + if action == BuddyMenu.ACTION_REMOVE_FRIEND: + friends.remove(self._buddy) + + ps_buddy = self._buddy.get_buddy() + if ps_buddy == None: + return + + if action == BuddyMenu.ACTION_INVITE: + activity = self._shell.get_current_activity() + activity.invite(ps_buddy) + elif action == BuddyMenu.ACTION_MAKE_FRIEND: + friends.make_friend(ps_buddy) diff --git a/shell/view/BuddyMenu.py b/shell/view/BuddyMenu.py index f330234..069aa6a 100644 --- a/shell/view/BuddyMenu.py +++ b/shell/view/BuddyMenu.py @@ -25,73 +25,73 @@ from sugar.presence import PresenceService _ICON_SIZE = 75 class BuddyMenu(Menu): - ACTION_MAKE_FRIEND = 0 - ACTION_INVITE = 1 - ACTION_REMOVE_FRIEND = 2 - - def __init__(self, shell, buddy): - self._buddy = buddy - self._shell = shell - - icon_item = None - pixbuf = self._get_buddy_icon_pixbuf() - if pixbuf: - scaled_pixbuf = pixbuf.scale_simple(_ICON_SIZE, _ICON_SIZE, - gtk.gdk.INTERP_BILINEAR) - del pixbuf - icon_item = hippo.CanvasImage(pixbuf=scaled_pixbuf) - - Menu.__init__(self, buddy.get_name(), icon_item) - - self._buddy.connect('icon-changed', self.__buddy_icon_changed_cb) - - owner = shell.get_model().get_owner() - if buddy.get_name() != owner.get_name(): - self._add_actions() - - def _get_buddy_icon_pixbuf(self): - buddy_object = self._buddy.get_buddy() - if not buddy_object: - return None - - pixbuf = None - icon_data = buddy_object.get_icon() - icon_data_string = "" - for item in icon_data: - if item < 0: - item = item + 128 - icon_data_string += chr(item) - pbl = gtk.gdk.PixbufLoader() - pbl.write(icon_data_string) - try: - pbl.close() - pixbuf = pbl.get_pixbuf() - except gobject.GError: - pass - del pbl - return pixbuf - - def _add_actions(self): - shell_model = self._shell.get_model() - pservice = PresenceService.get_instance() - - friends = shell_model.get_friends() - if friends.has_buddy(self._buddy): - icon = CanvasIcon(icon_name='stock-remove') - self.add_action(icon, BuddyMenu.ACTION_REMOVE_FRIEND) - else: - icon = CanvasIcon(icon_name='stock-add') - self.add_action(icon, BuddyMenu.ACTION_MAKE_FRIEND) - - activity_id = shell_model.get_current_activity() - if activity_id != None: - activity_ps = pservice.get_activity(activity_id) - - # FIXME check that the buddy is not in the activity already - - icon = CanvasIcon(icon_name='stock-invite') - self.add_action(icon, BuddyMenu.ACTION_INVITE) - - def __buddy_icon_changed_cb(self, buddy): - pass + ACTION_MAKE_FRIEND = 0 + ACTION_INVITE = 1 + ACTION_REMOVE_FRIEND = 2 + + def __init__(self, shell, buddy): + self._buddy = buddy + self._shell = shell + + icon_item = None + pixbuf = self._get_buddy_icon_pixbuf() + if pixbuf: + scaled_pixbuf = pixbuf.scale_simple(_ICON_SIZE, _ICON_SIZE, + gtk.gdk.INTERP_BILINEAR) + del pixbuf + icon_item = hippo.CanvasImage(pixbuf=scaled_pixbuf) + + Menu.__init__(self, buddy.get_name(), icon_item) + + self._buddy.connect('icon-changed', self.__buddy_icon_changed_cb) + + owner = shell.get_model().get_owner() + if buddy.get_name() != owner.get_name(): + self._add_actions() + + def _get_buddy_icon_pixbuf(self): + buddy_object = self._buddy.get_buddy() + if not buddy_object: + return None + + pixbuf = None + icon_data = buddy_object.get_icon() + icon_data_string = "" + for item in icon_data: + if item < 0: + item = item + 128 + icon_data_string += chr(item) + pbl = gtk.gdk.PixbufLoader() + pbl.write(icon_data_string) + try: + pbl.close() + pixbuf = pbl.get_pixbuf() + except gobject.GError: + pass + del pbl + return pixbuf + + def _add_actions(self): + shell_model = self._shell.get_model() + pservice = PresenceService.get_instance() + + friends = shell_model.get_friends() + if friends.has_buddy(self._buddy): + icon = CanvasIcon(icon_name='stock-remove') + self.add_action(icon, BuddyMenu.ACTION_REMOVE_FRIEND) + else: + icon = CanvasIcon(icon_name='stock-add') + self.add_action(icon, BuddyMenu.ACTION_MAKE_FRIEND) + + activity_id = shell_model.get_current_activity() + if activity_id != None: + activity_ps = pservice.get_activity(activity_id) + + # FIXME check that the buddy is not in the activity already + + icon = CanvasIcon(icon_name='stock-invite') + self.add_action(icon, BuddyMenu.ACTION_INVITE) + + def __buddy_icon_changed_cb(self, buddy): + pass diff --git a/shell/view/ClipboardIcon.py b/shell/view/ClipboardIcon.py index 429fc99..41f3e09 100644 --- a/shell/view/ClipboardIcon.py +++ b/shell/view/ClipboardIcon.py @@ -5,34 +5,34 @@ from sugar.clipboard import ClipboardService class ClipboardIcon(MenuIcon): - def __init__(self, menu_shell, name, file_name): - MenuIcon.__init__(self, menu_shell, icon_name='activity-xbook') - self._name = name - self._file_name = file_name - self._percent = 0 - self.connect('activated', self._icon_activated_cb) - self._menu = None - - def create_menu(self): - self._menu = ClipboardMenu(self._name, self._percent) - self._menu.connect('action', self._popup_action_cb) - return self._menu + def __init__(self, menu_shell, name, file_name): + MenuIcon.__init__(self, menu_shell, icon_name='activity-xbook') + self._name = name + self._file_name = file_name + self._percent = 0 + self.connect('activated', self._icon_activated_cb) + self._menu = None + + def create_menu(self): + self._menu = ClipboardMenu(self._name, self._percent) + self._menu.connect('action', self._popup_action_cb) + return self._menu - def set_percent(self, percent): - self._percent = percent - if self._menu: - self._menu.set_percent(percent) + def set_percent(self, percent): + self._percent = percent + if self._menu: + self._menu.set_percent(percent) - def _icon_activated_cb(self, icon): - if self._percent == 100: - activity = ActivityFactory.create("org.laptop.sugar.Xbook") - activity.execute("open_document", [self._file_name]) + def _icon_activated_cb(self, icon): + if self._percent == 100: + activity = ActivityFactory.create("org.laptop.sugar.Xbook") + activity.execute("open_document", [self._file_name]) - def _popup_action_cb(self, popup, action): - self.popdown() - - if action == ClipboardMenu.ACTION_STOP_DOWNLOAD: - raise "Stopping downloads still not implemented." - elif action == ClipboardMenu.ACTION_DELETE: - cb_service = ClipboardService.get_instance() - cb_service.delete_object(self._file_name) + def _popup_action_cb(self, popup, action): + self.popdown() + + if action == ClipboardMenu.ACTION_STOP_DOWNLOAD: + raise "Stopping downloads still not implemented." + elif action == ClipboardMenu.ACTION_DELETE: + cb_service = ClipboardService.get_instance() + cb_service.delete_object(self._file_name) diff --git a/shell/view/ClipboardMenu.py b/shell/view/ClipboardMenu.py index 44ca798..625c897 100644 --- a/shell/view/ClipboardMenu.py +++ b/shell/view/ClipboardMenu.py @@ -9,48 +9,48 @@ from sugar.graphics import style class ClipboardMenuItem(ClipboardBubble): - def __init__(self, percent = 0, stylesheet="clipboard.Bubble"): - ClipboardBubble.__init__(self, percent = percent) - style.apply_stylesheet(self, stylesheet) + def __init__(self, percent = 0, stylesheet="clipboard.Bubble"): + ClipboardBubble.__init__(self, percent = percent) + style.apply_stylesheet(self, stylesheet) class ClipboardMenu(Menu): - ACTION_DELETE = 0 - ACTION_SHARE = 1 - ACTION_STOP_DOWNLOAD = 2 - - def __init__(self, name, percent): - Menu.__init__(self, name) - - self._progress_bar = ClipboardMenuItem(percent) - self._root.append(self._progress_bar) - - #icon = CanvasIcon(icon_name='stock-share-mesh') - #self.add_action(icon, ClipboardMenu.ACTION_SHARE) - - self._remove_icon = None - self._stop_icon = None - - self._create_icons(percent) - - def _create_icons(self, percent): - if percent == 100: - if not self._remove_icon: - self._remove_icon = CanvasIcon(icon_name='stock-remove') - self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE) - - if self._stop_icon: - self.remove_action(self._stop_icon) - self._stop_icon = None - else: - if not self._stop_icon: - self._stop_icon = CanvasIcon(icon_name='stock-close') - self.add_action(self._stop_icon, ClipboardMenu.ACTION_STOP_DOWNLOAD) + ACTION_DELETE = 0 + ACTION_SHARE = 1 + ACTION_STOP_DOWNLOAD = 2 + + def __init__(self, name, percent): + Menu.__init__(self, name) + + self._progress_bar = ClipboardMenuItem(percent) + self._root.append(self._progress_bar) + + #icon = CanvasIcon(icon_name='stock-share-mesh') + #self.add_action(icon, ClipboardMenu.ACTION_SHARE) + + self._remove_icon = None + self._stop_icon = None + + self._create_icons(percent) + + def _create_icons(self, percent): + if percent == 100: + if not self._remove_icon: + self._remove_icon = CanvasIcon(icon_name='stock-remove') + self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE) + + if self._stop_icon: + self.remove_action(self._stop_icon) + self._stop_icon = None + else: + if not self._stop_icon: + self._stop_icon = CanvasIcon(icon_name='stock-close') + self.add_action(self._stop_icon, ClipboardMenu.ACTION_STOP_DOWNLOAD) - if self._remove_icon: - self.remove_action(self._remove_icon) - self._remove_icon = None - - def set_percent(self, percent): - self._progress_bar.set_property('percent', percent) - self._create_icons(percent) + if self._remove_icon: + self.remove_action(self._remove_icon) + self._remove_icon = None + + def set_percent(self, percent): + self._progress_bar.set_property('percent', percent) + self._create_icons(percent) diff --git a/shell/view/FirstTimeDialog.py b/shell/view/FirstTimeDialog.py index 267a661..0ae82ef 100644 --- a/shell/view/FirstTimeDialog.py +++ b/shell/view/FirstTimeDialog.py @@ -24,47 +24,47 @@ from sugar.graphics.iconcolor import IconColor from sugar import env class FirstTimeDialog(gtk.Dialog): - def __init__(self): - gtk.Dialog.__init__(self) + def __init__(self): + gtk.Dialog.__init__(self) - label = gtk.Label(_('Nick Name:')) - label.set_alignment(0.0, 0.5) - self.vbox.pack_start(label) - label.show() + label = gtk.Label(_('Nick Name:')) + label.set_alignment(0.0, 0.5) + self.vbox.pack_start(label) + label.show() - self._entry = gtk.Entry() - self._entry.connect('changed', self._entry_changed_cb) - self._entry.connect('activate', self._entry_activated_cb) - self.vbox.pack_start(self._entry) - self._entry.show() + self._entry = gtk.Entry() + self._entry.connect('changed', self._entry_changed_cb) + self._entry.connect('activate', self._entry_activated_cb) + self.vbox.pack_start(self._entry) + self._entry.show() - self._ok = gtk.Button(None, gtk.STOCK_OK) - self._ok.set_sensitive(False) - self.vbox.pack_start(self._ok) - self._ok.connect('clicked', self._ok_button_clicked_cb) - self._ok.show() + self._ok = gtk.Button(None, gtk.STOCK_OK) + self._ok.set_sensitive(False) + self.vbox.pack_start(self._ok) + self._ok.connect('clicked', self._ok_button_clicked_cb) + self._ok.show() - def _entry_changed_cb(self, entry): - valid = (len(entry.get_text()) > 0) - self._ok.set_sensitive(valid) + def _entry_changed_cb(self, entry): + valid = (len(entry.get_text()) > 0) + self._ok.set_sensitive(valid) - def _entry_activated_cb(self, entry): - self._create_buddy_section() - - def _ok_button_clicked_cb(self, button): - self._create_buddy_section() - - def _create_buddy_section(self): - cp = ConfigParser() + def _entry_activated_cb(self, entry): + self._create_buddy_section() + + def _ok_button_clicked_cb(self, button): + self._create_buddy_section() + + def _create_buddy_section(self): + cp = ConfigParser() - section = 'Buddy' - cp.add_section(section) - cp.set(section, 'NickName', self._entry.get_text()) - cp.set(section, 'Color', IconColor().to_string()) + section = 'Buddy' + cp.add_section(section) + cp.set(section, 'NickName', self._entry.get_text()) + cp.set(section, 'Color', IconColor().to_string()) - config_path = os.path.join(env.get_profile_path(), 'config') - fileobject = open(config_path, 'w') - cp.write(fileobject) - fileobject.close() + config_path = os.path.join(env.get_profile_path(), 'config') + fileobject = open(config_path, 'w') + cp.write(fileobject) + fileobject.close() - self.destroy() + self.destroy() diff --git a/shell/view/OverlayWindow.py b/shell/view/OverlayWindow.py index 93719a7..7980a1d 100644 --- a/shell/view/OverlayWindow.py +++ b/shell/view/OverlayWindow.py @@ -19,32 +19,32 @@ import cairo class OverlayWindow(gtk.Window): - def __init__(self, lower_window): - gtk.Window.__init__(self) - - colormap = self.get_screen().get_rgba_colormap() - colormap=None - if not colormap: - raise RuntimeError("The window manager doesn't support compositing.") - self.set_colormap(colormap) - - self.realize() - - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) - self.window.set_accept_focus(False) - self.window.set_transient_for(lower_window) - - self.set_decorated(False) - self.set_position(gtk.WIN_POS_CENTER_ALWAYS) - self.set_default_size(gtk.gdk.screen_width(), gtk.gdk.screen_height()) - self.set_app_paintable(True) - - self.connect('expose-event', self._expose_cb) - - def _expose_cb(self, widget, event): - cr = widget.window.cairo_create() - cr.set_source_rgba(0.0, 0.0, 0.0, 0.4) # Transparent - cr.set_operator(cairo.OPERATOR_SOURCE) - cr.paint() - return False + def __init__(self, lower_window): + gtk.Window.__init__(self) + + colormap = self.get_screen().get_rgba_colormap() + colormap=None + if not colormap: + raise RuntimeError("The window manager doesn't support compositing.") + self.set_colormap(colormap) + + self.realize() + + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self.window.set_accept_focus(False) + self.window.set_transient_for(lower_window) + + self.set_decorated(False) + self.set_position(gtk.WIN_POS_CENTER_ALWAYS) + self.set_default_size(gtk.gdk.screen_width(), gtk.gdk.screen_height()) + self.set_app_paintable(True) + + self.connect('expose-event', self._expose_cb) + + def _expose_cb(self, widget, event): + cr = widget.window.cairo_create() + cr.set_source_rgba(0.0, 0.0, 0.0, 0.4) # Transparent + cr.set_operator(cairo.OPERATOR_SOURCE) + cr.paint() + return False diff --git a/shell/view/Shell.py b/shell/view/Shell.py index 585f480..9166cef 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -31,206 +31,206 @@ from _sugar import KeyGrabber import sugar class Shell(gobject.GObject): - __gsignals__ = { - 'activity-opened': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'activity-changed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), - 'activity-closed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) - } - - def __init__(self, model): - gobject.GObject.__init__(self) - - self._model = model - self._hosts = {} - self._screen = wnck.screen_get_default() - self._current_host = None - - style.load_stylesheet(view.stylesheet) - - self._dcon_manager = DCONManager() - - self._key_grabber = KeyGrabber() - self._key_grabber.connect('key-pressed', - self.__global_key_pressed_cb) - self._key_grabber.connect('key-released', - self.__global_key_released_cb) - self._key_grabber.grab('F1') - self._key_grabber.grab('F2') - self._key_grabber.grab('F3') - self._key_grabber.grab('F4') - self._key_grabber.grab('F5') - self._key_grabber.grab('F6') - self._key_grabber.grab('F7') - self._key_grabber.grab('F8') - self._key_grabber.grab('0xDC') # Camera key - self._key_grabber.grab('0xE0') # Overlay key - self._key_grabber.grab('0x93') # Frame key - - # For non-OLPC machines - self._key_grabber.grab('F9') - self._key_grabber.grab('F10') - self._key_grabber.grab('F11') - - self._home_window = HomeWindow(self) - self._home_window.show() - self.set_zoom_level(sugar.ZOOM_HOME) - - self._screen.connect('window-opened', self.__window_opened_cb) - self._screen.connect('window-closed', self.__window_closed_cb) - self._screen.connect('active-window-changed', - self.__active_window_changed_cb) - - self._frame = Frame(self) - self._frame.show_and_hide(3) - - def _open_terminal_cb(self): - self.start_activity('org.sugar.Terminal') - return False - - def __global_key_pressed_cb(self, grabber, key): - if key == 'F1': - self.set_zoom_level(sugar.ZOOM_MESH) - elif key == 'F2': - self.set_zoom_level(sugar.ZOOM_FRIENDS) - elif key == 'F3': - self.set_zoom_level(sugar.ZOOM_HOME) - elif key == 'F4': - self.set_zoom_level(sugar.ZOOM_ACTIVITY) - elif key == 'F5': - self._dcon_manager.decrease_brightness() - elif key == 'F6': - self._dcon_manager.increase_brightness() - elif key == 'F7': - self._dcon_manager.set_mode(DCONManager.COLOR_MODE) - elif key == 'F8': - self._dcon_manager.set_mode(DCONManager.BLACK_AND_WHITE_MODE) - elif key == 'F9': - self._frame.notify_key_press() - elif key == 'F10': - self.toggle_chat_visibility() - elif key == 'F11': - gobject.idle_add(self._open_terminal_cb) - elif key == '0xDC': # Camera key - pass - elif key == '0xE0': # Overlay key - self.toggle_chat_visibility() - elif key == '0x93': # Frame key - self._frame.notify_key_press() - - def __global_key_released_cb(self, grabber, key): - if key == 'F9': - self._frame.notify_key_release() - elif key == '0x93': - self._frame.notify_key_release() - - def __window_opened_cb(self, screen, window): - if window.get_window_type() == wnck.WINDOW_NORMAL: - activity_host = ActivityHost(self.get_model(), window) - self._hosts[activity_host.get_xid()] = activity_host - self.emit('activity-opened', activity_host) - - def __active_window_changed_cb(self, screen): - window = screen.get_active_window() - if not window or window.get_window_type() != wnck.WINDOW_NORMAL: - return - if not self._hosts.has_key(window.get_xid()): - return - - activity_host = self._hosts[window.get_xid()] - current = self._model.get_current_activity() - if activity_host.get_id() == current: - return - - self._set_current_activity(activity_host) - - def __window_closed_cb(self, screen, window): - if window.get_window_type() != wnck.WINDOW_NORMAL: - return - - if not self._hosts.has_key(window.get_xid()): - return - - host = self._hosts[window.get_xid()] - host.destroy() - - self.emit('activity-closed', host) - del self._hosts[window.get_xid()] - - if len(self._hosts) == 0: - self._set_current_activity(None) - - def _set_current_activity(self, host): - if host: - self._model.set_current_activity(host.get_id()) - else: - self._model.set_current_activity(None) - - if self._current_host: - self._current_host.set_active(False) - - self._current_host = host - - if self._current_host: - self._current_host.set_active(True) - - self.emit('activity-changed', host) - - def get_model(self): - return self._model - - def join_activity(self, bundle_id, activity_id): - pservice = PresenceService.get_instance() - - activity = self._get_activity(activity_id) - if activity: - activity.present() - else: - activity_ps = pservice.get_activity(activity_id) - - if activity_ps: - activity = ActivityFactory.create(bundle_id) - activity.join(activity_ps.object_path()) - else: - logging.error('Cannot start activity.') - - def start_activity(self, activity_type): - activity = ActivityFactory.create(activity_type) - activity.execute('test', []) - return activity - - def set_zoom_level(self, level): - if level == sugar.ZOOM_ACTIVITY: - self._screen.toggle_showing_desktop(False) - else: - self._screen.toggle_showing_desktop(True) - self._home_window.set_zoom_level(level) - - def get_current_activity(self): - activity_id = self._model.get_current_activity() - if activity_id: - return self._get_activity(activity_id) - else: - return None - - def _get_activity(self, activity_id): - for host in self._hosts.values(): - if host.get_id() == activity_id: - return host - return None - - def toggle_chat_visibility(self): - act = self.get_current_activity() - if not act: - return - is_visible = self._frame.is_visible() - if act.is_chat_visible(): - frame_was_visible = act.chat_hide() - if not frame_was_visible: - self._frame.do_slide_out() - else: - if not is_visible: - self._frame.do_slide_in() - act.chat_show(is_visible) + __gsignals__ = { + 'activity-opened': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + 'activity-changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), + 'activity-closed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, model): + gobject.GObject.__init__(self) + + self._model = model + self._hosts = {} + self._screen = wnck.screen_get_default() + self._current_host = None + + style.load_stylesheet(view.stylesheet) + + self._dcon_manager = DCONManager() + + self._key_grabber = KeyGrabber() + self._key_grabber.connect('key-pressed', + self.__global_key_pressed_cb) + self._key_grabber.connect('key-released', + self.__global_key_released_cb) + self._key_grabber.grab('F1') + self._key_grabber.grab('F2') + self._key_grabber.grab('F3') + self._key_grabber.grab('F4') + self._key_grabber.grab('F5') + self._key_grabber.grab('F6') + self._key_grabber.grab('F7') + self._key_grabber.grab('F8') + self._key_grabber.grab('0xDC') # Camera key + self._key_grabber.grab('0xE0') # Overlay key + self._key_grabber.grab('0x93') # Frame key + + # For non-OLPC machines + self._key_grabber.grab('F9') + self._key_grabber.grab('F10') + self._key_grabber.grab('F11') + + self._home_window = HomeWindow(self) + self._home_window.show() + self.set_zoom_level(sugar.ZOOM_HOME) + + self._screen.connect('window-opened', self.__window_opened_cb) + self._screen.connect('window-closed', self.__window_closed_cb) + self._screen.connect('active-window-changed', + self.__active_window_changed_cb) + + self._frame = Frame(self) + self._frame.show_and_hide(3) + + def _open_terminal_cb(self): + self.start_activity('org.sugar.Terminal') + return False + + def __global_key_pressed_cb(self, grabber, key): + if key == 'F1': + self.set_zoom_level(sugar.ZOOM_MESH) + elif key == 'F2': + self.set_zoom_level(sugar.ZOOM_FRIENDS) + elif key == 'F3': + self.set_zoom_level(sugar.ZOOM_HOME) + elif key == 'F4': + self.set_zoom_level(sugar.ZOOM_ACTIVITY) + elif key == 'F5': + self._dcon_manager.decrease_brightness() + elif key == 'F6': + self._dcon_manager.increase_brightness() + elif key == 'F7': + self._dcon_manager.set_mode(DCONManager.COLOR_MODE) + elif key == 'F8': + self._dcon_manager.set_mode(DCONManager.BLACK_AND_WHITE_MODE) + elif key == 'F9': + self._frame.notify_key_press() + elif key == 'F10': + self.toggle_chat_visibility() + elif key == 'F11': + gobject.idle_add(self._open_terminal_cb) + elif key == '0xDC': # Camera key + pass + elif key == '0xE0': # Overlay key + self.toggle_chat_visibility() + elif key == '0x93': # Frame key + self._frame.notify_key_press() + + def __global_key_released_cb(self, grabber, key): + if key == 'F9': + self._frame.notify_key_release() + elif key == '0x93': + self._frame.notify_key_release() + + def __window_opened_cb(self, screen, window): + if window.get_window_type() == wnck.WINDOW_NORMAL: + activity_host = ActivityHost(self.get_model(), window) + self._hosts[activity_host.get_xid()] = activity_host + self.emit('activity-opened', activity_host) + + def __active_window_changed_cb(self, screen): + window = screen.get_active_window() + if not window or window.get_window_type() != wnck.WINDOW_NORMAL: + return + if not self._hosts.has_key(window.get_xid()): + return + + activity_host = self._hosts[window.get_xid()] + current = self._model.get_current_activity() + if activity_host.get_id() == current: + return + + self._set_current_activity(activity_host) + + def __window_closed_cb(self, screen, window): + if window.get_window_type() != wnck.WINDOW_NORMAL: + return + + if not self._hosts.has_key(window.get_xid()): + return + + host = self._hosts[window.get_xid()] + host.destroy() + + self.emit('activity-closed', host) + del self._hosts[window.get_xid()] + + if len(self._hosts) == 0: + self._set_current_activity(None) + + def _set_current_activity(self, host): + if host: + self._model.set_current_activity(host.get_id()) + else: + self._model.set_current_activity(None) + + if self._current_host: + self._current_host.set_active(False) + + self._current_host = host + + if self._current_host: + self._current_host.set_active(True) + + self.emit('activity-changed', host) + + def get_model(self): + return self._model + + def join_activity(self, bundle_id, activity_id): + pservice = PresenceService.get_instance() + + activity = self._get_activity(activity_id) + if activity: + activity.present() + else: + activity_ps = pservice.get_activity(activity_id) + + if activity_ps: + activity = ActivityFactory.create(bundle_id) + activity.join(activity_ps.object_path()) + else: + logging.error('Cannot start activity.') + + def start_activity(self, activity_type): + activity = ActivityFactory.create(activity_type) + activity.execute('test', []) + return activity + + def set_zoom_level(self, level): + if level == sugar.ZOOM_ACTIVITY: + self._screen.toggle_showing_desktop(False) + else: + self._screen.toggle_showing_desktop(True) + self._home_window.set_zoom_level(level) + + def get_current_activity(self): + activity_id = self._model.get_current_activity() + if activity_id: + return self._get_activity(activity_id) + else: + return None + + def _get_activity(self, activity_id): + for host in self._hosts.values(): + if host.get_id() == activity_id: + return host + return None + + def toggle_chat_visibility(self): + act = self.get_current_activity() + if not act: + return + is_visible = self._frame.is_visible() + if act.is_chat_visible(): + frame_was_visible = act.chat_hide() + if not frame_was_visible: + self._frame.do_slide_out() + else: + if not is_visible: + self._frame.do_slide_in() + act.chat_show(is_visible) diff --git a/shell/view/dconmanager.py b/shell/view/dconmanager.py index 2cf40b5..2db626a 100644 --- a/shell/view/dconmanager.py +++ b/shell/view/dconmanager.py @@ -21,23 +21,23 @@ DCON_MANAGER_SERVICE = 'org.laptop.DCONManager' DCON_MANAGER_OBJECT_PATH = '/org/laptop/DCONManager' class DCONManager(object): - COLOR_MODE = 0 - BLACK_AND_WHITE_MODE = 1 + COLOR_MODE = 0 + BLACK_AND_WHITE_MODE = 1 - def __init__(self): - bus = dbus.SystemBus() - proxy = bus.get_object(DCON_MANAGER_SERVICE, DCON_MANAGER_OBJECT_PATH) - self._service = dbus.Interface(proxy, DCON_MANAGER_INTERFACE) + def __init__(self): + bus = dbus.SystemBus() + proxy = bus.get_object(DCON_MANAGER_SERVICE, DCON_MANAGER_OBJECT_PATH) + self._service = dbus.Interface(proxy, DCON_MANAGER_INTERFACE) - def set_mode(self, mode): - self._service.set_mode(mode) + def set_mode(self, mode): + self._service.set_mode(mode) - def increase_brightness(self): - level = self._service.get_backlight_level() - if level >= 0: - self._service.set_backlight_level(level + 1) + def increase_brightness(self): + level = self._service.get_backlight_level() + if level >= 0: + self._service.set_backlight_level(level + 1) - def decrease_brightness(self): - level = self._service.get_backlight_level() - if level >= 0: - self._service.set_backlight_level(level - 1) + def decrease_brightness(self): + level = self._service.get_backlight_level() + if level >= 0: + self._service.set_backlight_level(level - 1) diff --git a/shell/view/frame/ActivitiesBox.py b/shell/view/frame/ActivitiesBox.py index 2e3a75a..aa96585 100644 --- a/shell/view/frame/ActivitiesBox.py +++ b/shell/view/frame/ActivitiesBox.py @@ -22,80 +22,80 @@ from sugar.presence import PresenceService from sugar.graphics import style class ActivityItem(CanvasIcon): - def __init__(self, activity): - icon_name = activity.get_icon() - CanvasIcon.__init__(self, icon_name=icon_name) - style.apply_stylesheet(self, 'frame.ActivityIcon') - self._activity = activity + def __init__(self, activity): + icon_name = activity.get_icon() + CanvasIcon.__init__(self, icon_name=icon_name) + style.apply_stylesheet(self, 'frame.ActivityIcon') + self._activity = activity - def get_bundle_id(self): - return self._activity.get_service_name() + def get_bundle_id(self): + return self._activity.get_service_name() class InviteItem(CanvasIcon): - def __init__(self, activity, invite): - CanvasIcon.__init__(self, icon_name=activity.get_icon()) + def __init__(self, activity, invite): + CanvasIcon.__init__(self, icon_name=activity.get_icon()) - style.apply_stylesheet(self, 'frame.ActivityIcon') - self.props.color = activity.get_color() + style.apply_stylesheet(self, 'frame.ActivityIcon') + self.props.color = activity.get_color() - self._invite = invite + self._invite = invite - def get_activity_id(self): - return self._invite.get_activity_id() + def get_activity_id(self): + return self._invite.get_activity_id() - def get_bundle_id(self): - return self._invite.get_bundle_id() + def get_bundle_id(self): + return self._invite.get_bundle_id() - def get_invite(self): - return self._invite + def get_invite(self): + return self._invite class ActivitiesBox(hippo.CanvasBox): - def __init__(self, shell): - hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) - - self._shell = shell - self._shell_model = self._shell.get_model() - self._invite_to_item = {} - self._invites = self._shell_model.get_invites() - - for bundle in self._shell_model.get_bundle_registry(): - if bundle.get_show_launcher(): - self.add_activity(bundle) - - for invite in self._invites: - self.add_invite(invite) - self._invites.connect('invite-added', self._invite_added_cb) - self._invites.connect('invite-removed', self._invite_removed_cb) - - def _activity_clicked_cb(self, icon): - self._shell.start_activity(icon.get_bundle_id()) - - def _invite_clicked_cb(self, icon): - self._invites.remove_invite(icon.get_invite()) - self._shell.join_activity(icon.get_bundle_id(), - icon.get_activity_id()) - - def _invite_added_cb(self, invites, invite): - self.add_invite(invite) - - def _invite_removed_cb(self, invites, invite): - self.remove_invite(invite) - - def add_activity(self, activity): - item = ActivityItem(activity) - item.connect('activated', self._activity_clicked_cb) - self.append(item, 0) - - def add_invite(self, invite): - mesh = self._shell_model.get_mesh() - activity = mesh.get_activity(invite.get_activity_id()) - if activity: - item = InviteItem(activity, invite) - item.connect('activated', self._invite_clicked_cb) - self.append(item, 0) - - self._invite_to_item[invite] = item - - def remove_invite(self, invite): - self.remove(self._invite_to_item[invite]) - del self._invite_to_item[invite] + def __init__(self, shell): + hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) + + self._shell = shell + self._shell_model = self._shell.get_model() + self._invite_to_item = {} + self._invites = self._shell_model.get_invites() + + for bundle in self._shell_model.get_bundle_registry(): + if bundle.get_show_launcher(): + self.add_activity(bundle) + + for invite in self._invites: + self.add_invite(invite) + self._invites.connect('invite-added', self._invite_added_cb) + self._invites.connect('invite-removed', self._invite_removed_cb) + + def _activity_clicked_cb(self, icon): + self._shell.start_activity(icon.get_bundle_id()) + + def _invite_clicked_cb(self, icon): + self._invites.remove_invite(icon.get_invite()) + self._shell.join_activity(icon.get_bundle_id(), + icon.get_activity_id()) + + def _invite_added_cb(self, invites, invite): + self.add_invite(invite) + + def _invite_removed_cb(self, invites, invite): + self.remove_invite(invite) + + def add_activity(self, activity): + item = ActivityItem(activity) + item.connect('activated', self._activity_clicked_cb) + self.append(item, 0) + + def add_invite(self, invite): + mesh = self._shell_model.get_mesh() + activity = mesh.get_activity(invite.get_activity_id()) + if activity: + item = InviteItem(activity, invite) + item.connect('activated', self._invite_clicked_cb) + self.append(item, 0) + + self._invite_to_item[invite] = item + + def remove_invite(self, invite): + self.remove(self._invite_to_item[invite]) + del self._invite_to_item[invite] diff --git a/shell/view/frame/ClipboardBox.py b/shell/view/frame/ClipboardBox.py index 849c7e0..82dccb6 100644 --- a/shell/view/frame/ClipboardBox.py +++ b/shell/view/frame/ClipboardBox.py @@ -7,36 +7,36 @@ from view.ClipboardIcon import ClipboardIcon from sugar.clipboard import ClipboardService class ClipboardBox(hippo.CanvasBox): - - def __init__(self, frame, menu_shell): - hippo.CanvasBox.__init__(self) - self._frame = frame - self._menu_shell = menu_shell - self._icons = {} - - cb_service = ClipboardService.get_instance() - cb_service.connect('object-added', self._object_added_cb) - cb_service.connect('object-deleted', self._object_deleted_cb) - cb_service.connect('object-state-changed', self._object_state_changed_cb) + + def __init__(self, frame, menu_shell): + hippo.CanvasBox.__init__(self) + self._frame = frame + self._menu_shell = menu_shell + self._icons = {} + + cb_service = ClipboardService.get_instance() + cb_service.connect('object-added', self._object_added_cb) + cb_service.connect('object-deleted', self._object_deleted_cb) + cb_service.connect('object-state-changed', self._object_state_changed_cb) - def _object_added_cb(self, cb_service, name, mimeType, fileName): - icon = ClipboardIcon(self._menu_shell, name, fileName) - style.apply_stylesheet(icon, 'frame.BuddyIcon') - self.append(icon) - self._icons[fileName] = icon - - if not self._frame.is_visible(): - self._frame.show_and_hide(0.1) - - logging.debug('ClipboardBox: ' + fileName + ' was added.') + def _object_added_cb(self, cb_service, name, mimeType, fileName): + icon = ClipboardIcon(self._menu_shell, name, fileName) + style.apply_stylesheet(icon, 'frame.BuddyIcon') + self.append(icon) + self._icons[fileName] = icon + + if not self._frame.is_visible(): + self._frame.show_and_hide(0.1) + + logging.debug('ClipboardBox: ' + fileName + ' was added.') - def _object_deleted_cb(self, cb_service, fileName): - icon = self._icons[fileName] - self.remove(icon) - del self._icons[fileName] - logging.debug('ClipboardBox: ' + fileName + ' was deleted.') + def _object_deleted_cb(self, cb_service, fileName): + icon = self._icons[fileName] + self.remove(icon) + del self._icons[fileName] + logging.debug('ClipboardBox: ' + fileName + ' was deleted.') - def _object_state_changed_cb(self, cb_service, fileName, percent): - icon = self._icons[fileName] - icon.set_percent(percent) - logging.debug('ClipboardBox: ' + fileName + ' state was changed.') + def _object_state_changed_cb(self, cb_service, fileName, percent): + icon = self._icons[fileName] + icon.set_percent(percent) + logging.debug('ClipboardBox: ' + fileName + ' state was changed.') diff --git a/shell/view/frame/Frame.py b/shell/view/frame/Frame.py index 37f28cb..c865219 100644 --- a/shell/view/frame/Frame.py +++ b/shell/view/frame/Frame.py @@ -32,268 +32,268 @@ from sugar.graphics.grid import Grid from sugar.graphics.menushell import MenuShell class EventFrame(gobject.GObject): - __gsignals__ = { - 'enter-edge': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), - 'enter-corner': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])), - 'leave': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([])) - } - - HOVER_NONE = 0 - HOVER_CORNER = 1 - HOVER_EDGE = 2 - - def __init__(self): - gobject.GObject.__init__(self) - - self._windows = [] - self._hover = EventFrame.HOVER_NONE - self._active = False - - invisible = self._create_invisible(0, 0, gtk.gdk.screen_width(), 1) - self._windows.append(invisible) - - invisible = self._create_invisible(0, 0, 1, gtk.gdk.screen_height()) - self._windows.append(invisible) - - invisible = self._create_invisible(gtk.gdk.screen_width() - 1, 0, - gtk.gdk.screen_width(), - gtk.gdk.screen_height()) - self._windows.append(invisible) - - invisible = self._create_invisible(0, gtk.gdk.screen_height() - 1, - gtk.gdk.screen_width(), - gtk.gdk.screen_height()) - self._windows.append(invisible) - - screen = wnck.screen_get_default() - screen.connect('active-window-changed', - self._active_window_changed_cb) - - def _create_invisible(self, x, y, width, height): - invisible = gtk.Invisible() - invisible.connect('motion-notify-event', self._motion_notify_cb) - invisible.connect('enter-notify-event', self._enter_notify_cb) - invisible.connect('leave-notify-event', self._leave_notify_cb) - - invisible.realize() - invisible.window.set_events(gtk.gdk.POINTER_MOTION_MASK | - gtk.gdk.ENTER_NOTIFY_MASK | - gtk.gdk.LEAVE_NOTIFY_MASK) - invisible.window.move_resize(x, y, width, height) - - return invisible - - def _enter_notify_cb(self, widget, event): - self._notify_enter(event.x, event.y) - - def _motion_notify_cb(self, widget, event): - self._notify_enter(event.x, event.y) - - def _notify_enter(self, x, y): - screen_w = gtk.gdk.screen_width() - screen_h = gtk.gdk.screen_height() - - if (x == 0 and y == 0) or \ - (x == 0 and y == screen_h - 1) or \ - (x == screen_w - 1 and y == 0) or \ - (x == screen_w - 1 and y == screen_h - 1): - if self._hover != EventFrame.HOVER_CORNER: - self._hover = EventFrame.HOVER_CORNER - self.emit('enter-corner') - else: - if self._hover != EventFrame.HOVER_EDGE: - self._hover = EventFrame.HOVER_EDGE - self.emit('enter-edge') - - def _leave_notify_cb(self, widget, event): - self._hover = EventFrame.HOVER_NONE - if self._active: - self.emit('leave') - - def show(self): - self._active = True - for window in self._windows: - window.show() - - def hide(self): - self._active = False - for window in self._windows: - window.hide() - - def _active_window_changed_cb(self, screen): - for window in self._windows: - window.window.raise_() + __gsignals__ = { + 'enter-edge': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + 'enter-corner': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])), + 'leave': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])) + } + + HOVER_NONE = 0 + HOVER_CORNER = 1 + HOVER_EDGE = 2 + + def __init__(self): + gobject.GObject.__init__(self) + + self._windows = [] + self._hover = EventFrame.HOVER_NONE + self._active = False + + invisible = self._create_invisible(0, 0, gtk.gdk.screen_width(), 1) + self._windows.append(invisible) + + invisible = self._create_invisible(0, 0, 1, gtk.gdk.screen_height()) + self._windows.append(invisible) + + invisible = self._create_invisible(gtk.gdk.screen_width() - 1, 0, + gtk.gdk.screen_width(), + gtk.gdk.screen_height()) + self._windows.append(invisible) + + invisible = self._create_invisible(0, gtk.gdk.screen_height() - 1, + gtk.gdk.screen_width(), + gtk.gdk.screen_height()) + self._windows.append(invisible) + + screen = wnck.screen_get_default() + screen.connect('active-window-changed', + self._active_window_changed_cb) + + def _create_invisible(self, x, y, width, height): + invisible = gtk.Invisible() + invisible.connect('motion-notify-event', self._motion_notify_cb) + invisible.connect('enter-notify-event', self._enter_notify_cb) + invisible.connect('leave-notify-event', self._leave_notify_cb) + + invisible.realize() + invisible.window.set_events(gtk.gdk.POINTER_MOTION_MASK | + gtk.gdk.ENTER_NOTIFY_MASK | + gtk.gdk.LEAVE_NOTIFY_MASK) + invisible.window.move_resize(x, y, width, height) + + return invisible + + def _enter_notify_cb(self, widget, event): + self._notify_enter(event.x, event.y) + + def _motion_notify_cb(self, widget, event): + self._notify_enter(event.x, event.y) + + def _notify_enter(self, x, y): + screen_w = gtk.gdk.screen_width() + screen_h = gtk.gdk.screen_height() + + if (x == 0 and y == 0) or \ + (x == 0 and y == screen_h - 1) or \ + (x == screen_w - 1 and y == 0) or \ + (x == screen_w - 1 and y == screen_h - 1): + if self._hover != EventFrame.HOVER_CORNER: + self._hover = EventFrame.HOVER_CORNER + self.emit('enter-corner') + else: + if self._hover != EventFrame.HOVER_EDGE: + self._hover = EventFrame.HOVER_EDGE + self.emit('enter-edge') + + def _leave_notify_cb(self, widget, event): + self._hover = EventFrame.HOVER_NONE + if self._active: + self.emit('leave') + + def show(self): + self._active = True + for window in self._windows: + window.show() + + def hide(self): + self._active = False + for window in self._windows: + window.hide() + + def _active_window_changed_cb(self, screen): + for window in self._windows: + window.window.raise_() class Frame: - INACTIVE = 0 - TEMPORARY = 1 - STICKY = 2 - HIDE_ON_LEAVE = 3 - AUTOMATIC = 4 + INACTIVE = 0 + TEMPORARY = 1 + STICKY = 2 + HIDE_ON_LEAVE = 3 + AUTOMATIC = 4 - def __init__(self, shell): - self._windows = [] - self._active_menus = 0 - self._shell = shell - self._mode = Frame.INACTIVE + def __init__(self, shell): + self._windows = [] + self._active_menus = 0 + self._shell = shell + self._mode = Frame.INACTIVE - self._timeline = Timeline(self) - self._timeline.add_tag('slide_in', 6, 12) - self._timeline.add_tag('before_slide_out', 36, 36) - self._timeline.add_tag('slide_out', 37, 42) + self._timeline = Timeline(self) + self._timeline.add_tag('slide_in', 6, 12) + self._timeline.add_tag('before_slide_out', 36, 36) + self._timeline.add_tag('slide_out', 37, 42) - self._event_frame = EventFrame() - self._event_frame.connect('enter-edge', self._enter_edge_cb) - self._event_frame.connect('enter-corner', self._enter_corner_cb) - self._event_frame.connect('leave', self._event_frame_leave_cb) - self._event_frame.show() + self._event_frame = EventFrame() + self._event_frame.connect('enter-edge', self._enter_edge_cb) + self._event_frame.connect('enter-corner', self._enter_corner_cb) + self._event_frame.connect('leave', self._event_frame_leave_cb) + self._event_frame.show() - grid = Grid() + grid = Grid() - # Top panel - [menu_shell, root] = self._create_panel(grid, 0, 0, 16, 1) - menu_shell.set_position(MenuShell.BOTTOM) + # Top panel + [menu_shell, root] = self._create_panel(grid, 0, 0, 16, 1) + menu_shell.set_position(MenuShell.BOTTOM) - box = ZoomBox(self._shell, menu_shell) + box = ZoomBox(self._shell, menu_shell) - [x, y] = grid.point(1, 0) - root.append(box, hippo.PACK_FIXED) - root.move(box, x, y) + [x, y] = grid.point(1, 0) + root.append(box, hippo.PACK_FIXED) + root.move(box, x, y) - tray = NotificationTray() - tray_box = hippo.CanvasBox(box_width=grid.dimension(1), - box_height=grid.dimension(1), - xalign=hippo.ALIGNMENT_END) + tray = NotificationTray() + tray_box = hippo.CanvasBox(box_width=grid.dimension(1), + box_height=grid.dimension(1), + xalign=hippo.ALIGNMENT_END) - tray_widget = hippo.CanvasWidget() - tray_widget.props.widget = tray - tray_box.append(tray_widget, gtk.EXPAND) + tray_widget = hippo.CanvasWidget() + tray_widget.props.widget = tray + tray_box.append(tray_widget, gtk.EXPAND) - [x, y] = grid.point(13, 0) - root.append(tray_box, hippo.PACK_FIXED) - root.move(tray_box, x, y) + [x, y] = grid.point(13, 0) + root.append(tray_box, hippo.PACK_FIXED) + root.move(tray_box, x, y) - box = OverlayBox(self._shell) + box = OverlayBox(self._shell) - [x, y] = grid.point(14, 0) - root.append(box, hippo.PACK_FIXED) - root.move(box, x, y) + [x, y] = grid.point(14, 0) + root.append(box, hippo.PACK_FIXED) + root.move(box, x, y) - shutdown_icon = ShutdownIcon(menu_shell) + shutdown_icon = ShutdownIcon(menu_shell) - [x, y] = grid.point(12, 0) - root.append(shutdown_icon, hippo.PACK_FIXED) - root.move(shutdown_icon, x, y) + [x, y] = grid.point(12, 0) + root.append(shutdown_icon, hippo.PACK_FIXED) + root.move(shutdown_icon, x, y) - # Bottom panel - [menu_shell, root] = self._create_panel(grid, 0, 11, 16, 1) - menu_shell.set_position(MenuShell.TOP) + # Bottom panel + [menu_shell, root] = self._create_panel(grid, 0, 11, 16, 1) + menu_shell.set_position(MenuShell.TOP) - box = ActivitiesBox(self._shell) - root.append(box, hippo.PACK_FIXED) + box = ActivitiesBox(self._shell) + root.append(box, hippo.PACK_FIXED) - [x, y] = grid.point(1, 0) - root.move(box, x, y) + [x, y] = grid.point(1, 0) + root.move(box, x, y) - # Right panel - [menu_shell, root] = self._create_panel(grid, 15, 1, 1, 10) - menu_shell.set_position(MenuShell.LEFT) + # Right panel + [menu_shell, root] = self._create_panel(grid, 15, 1, 1, 10) + menu_shell.set_position(MenuShell.LEFT) - box = FriendsBox(self._shell, menu_shell) - root.append(box) + box = FriendsBox(self._shell, menu_shell) + root.append(box) - # Left panel - [menu_shell, root] = self._create_panel(grid, 0, 1, 1, 10) + # Left panel + [menu_shell, root] = self._create_panel(grid, 0, 1, 1, 10) - box = ClipboardBox(self, menu_shell) - root.append(box) + box = ClipboardBox(self, menu_shell) + root.append(box) - def _create_panel(self, grid, x, y, width, height): - panel = PanelWindow() + def _create_panel(self, grid, x, y, width, height): + panel = PanelWindow() - panel.connect('enter-notify-event', self._enter_notify_cb) - panel.connect('leave-notify-event', self._leave_notify_cb) + panel.connect('enter-notify-event', self._enter_notify_cb) + panel.connect('leave-notify-event', self._leave_notify_cb) - menu_shell = panel.get_menu_shell() - menu_shell.connect('activated', self._menu_shell_activated_cb) - menu_shell.connect('deactivated', self._menu_shell_deactivated_cb) + menu_shell = panel.get_menu_shell() + menu_shell.connect('activated', self._menu_shell_activated_cb) + menu_shell.connect('deactivated', self._menu_shell_deactivated_cb) - [x, y, width, height] = grid.rectangle(x, y, width, height) + [x, y, width, height] = grid.rectangle(x, y, width, height) - panel.move(x, y) - panel.resize(width, height) + panel.move(x, y) + panel.resize(width, height) - self._windows.append(panel) + self._windows.append(panel) - return [panel.get_menu_shell(), panel.get_root()] + return [panel.get_menu_shell(), panel.get_root()] - def _menu_shell_activated_cb(self, menu_shell): - self._active_menus += 1 - self._timeline.goto('slide_in', True) + def _menu_shell_activated_cb(self, menu_shell): + self._active_menus += 1 + self._timeline.goto('slide_in', True) - def _menu_shell_deactivated_cb(self, menu_shell): - self._active_menus -= 1 - if self._mode != Frame.STICKY: - self._timeline.play('before_slide_out', 'slide_out') + def _menu_shell_deactivated_cb(self, menu_shell): + self._active_menus -= 1 + if self._mode != Frame.STICKY: + self._timeline.play('before_slide_out', 'slide_out') - def _enter_notify_cb(self, window, event): - self._timeline.goto('slide_in', True) + def _enter_notify_cb(self, window, event): + self._timeline.goto('slide_in', True) - def _leave_notify_cb(self, window, event): - # FIXME for some reason every click cause also a leave-notify - if event.state == gtk.gdk.BUTTON1_MASK: - return + def _leave_notify_cb(self, window, event): + # FIXME for some reason every click cause also a leave-notify + if event.state == gtk.gdk.BUTTON1_MASK: + return - if self._active_menus == 0 and \ - (self._mode == Frame.HIDE_ON_LEAVE or \ - self._mode == Frame.AUTOMATIC): - self._timeline.play('before_slide_out', 'slide_out') + if self._active_menus == 0 and \ + (self._mode == Frame.HIDE_ON_LEAVE or \ + self._mode == Frame.AUTOMATIC): + self._timeline.play('before_slide_out', 'slide_out') - def _enter_edge_cb(self, event_frame): - self._mode = Frame.HIDE_ON_LEAVE - self._timeline.play(None, 'slide_in') + def _enter_edge_cb(self, event_frame): + self._mode = Frame.HIDE_ON_LEAVE + self._timeline.play(None, 'slide_in') - def _enter_corner_cb(self, event_frame): - self._mode = Frame.HIDE_ON_LEAVE - self._timeline.play('slide_in', 'slide_in') + def _enter_corner_cb(self, event_frame): + self._mode = Frame.HIDE_ON_LEAVE + self._timeline.play('slide_in', 'slide_in') - def _event_frame_leave_cb(self, event_frame): - if self._mode != Frame.STICKY: - self._timeline.goto('slide_out', True) + def _event_frame_leave_cb(self, event_frame): + if self._mode != Frame.STICKY: + self._timeline.goto('slide_out', True) - def show_and_hide(self, seconds): - self._mode = Frame.AUTOMATIC - self._timeline.play() + def show_and_hide(self, seconds): + self._mode = Frame.AUTOMATIC + self._timeline.play() - def notify_key_press(self): - if self._timeline.on_tag('slide_in'): - self._timeline.play('before_slide_out', 'slide_out') - elif self._timeline.on_tag('before_slide_out'): - self._mode = Frame.TEMPORARY - else: - self._mode = Frame.STICKY - self._timeline.play('slide_in', 'slide_in') + def notify_key_press(self): + if self._timeline.on_tag('slide_in'): + self._timeline.play('before_slide_out', 'slide_out') + elif self._timeline.on_tag('before_slide_out'): + self._mode = Frame.TEMPORARY + else: + self._mode = Frame.STICKY + self._timeline.play('slide_in', 'slide_in') - def notify_key_release(self): - if self._mode == Frame.TEMPORARY: - self._timeline.play('before_slide_out', 'slide_out') + def notify_key_release(self): + if self._mode == Frame.TEMPORARY: + self._timeline.play('before_slide_out', 'slide_out') - def do_slide_in(self, current=0, n_frames=0): - if not self._windows[0].props.visible: - for panel in self._windows: - panel.show() - self._event_frame.hide() + def do_slide_in(self, current=0, n_frames=0): + if not self._windows[0].props.visible: + for panel in self._windows: + panel.show() + self._event_frame.hide() - def do_slide_out(self, current=0, n_frames=0): - if self._windows[0].props.visible: - for panel in self._windows: - panel.hide() - self._event_frame.show() + def do_slide_out(self, current=0, n_frames=0): + if self._windows[0].props.visible: + for panel in self._windows: + panel.hide() + self._event_frame.show() - def is_visible(self): - if self._windows[0].props.visible: - return True - return False + def is_visible(self): + if self._windows[0].props.visible: + return True + return False diff --git a/shell/view/frame/FriendsBox.py b/shell/view/frame/FriendsBox.py index 14f1290..cdc07ad 100644 --- a/shell/view/frame/FriendsBox.py +++ b/shell/view/frame/FriendsBox.py @@ -24,85 +24,85 @@ from view.BuddyIcon import BuddyIcon from model.BuddyModel import BuddyModel class FriendsBox(hippo.CanvasBox): - def __init__(self, shell, menu_shell): - hippo.CanvasBox.__init__(self) - self._shell = shell - self._menu_shell = menu_shell - self._activity_ps = None - self._joined_hid = -1 - self._left_hid = -1 - self._buddies = {} - - self._pservice = PresenceService.get_instance() - self._pservice.connect('activity-appeared', - self.__activity_appeared_cb) - - # Add initial activities the PS knows about - for activity in self._pservice.get_activities(): - self.__activity_appeared_cb(self._pservice, activity) - - shell.connect('activity-changed', self.__activity_changed_cb) - - def add_buddy(self, buddy): - if self._buddies.has_key(buddy.get_name()): - return - - model = BuddyModel(buddy=buddy) - icon = BuddyIcon(self._shell, self._menu_shell, model) - style.apply_stylesheet(icon, 'frame.BuddyIcon') - self.append(icon) - - self._buddies[buddy.get_name()] = icon - - def remove_buddy(self, buddy): - if not self._buddies.has_key(buddy.get_name()): - return - - self.remove(self._buddies[buddy.get_name()]) - - def clear(self): - for item in self.get_children(): - self.remove(item) - self._buddies = {} - - def __activity_appeared_cb(self, pservice, activity_ps): - activity = self._shell.get_current_activity() - if activity and activity_ps.get_id() == activity.get_id(): - self._set_activity_ps(activity_ps) - - def _set_activity_ps(self, activity_ps): - if self._activity_ps == activity_ps: - return - - if self._joined_hid > 0: - self._activity_ps.disconnect(self._joined_hid) - self._joined_hid = -1 - if self._left_hid > 0: - self._activity_ps.disconnect(self._left_hid) - self._left_hid = -1 - - self._activity_ps = activity_ps - - self.clear() - - if activity_ps != None: - for buddy in activity_ps.get_joined_buddies(): - self.add_buddy(buddy) - - self._joined_hid = activity_ps.connect( - 'buddy-joined', self.__buddy_joined_cb) - self._left_hid = activity_ps.connect( - 'buddy-left', self.__buddy_left_cb) - - def __activity_changed_cb(self, group, activity): - if activity: - ps = self._pservice.get_activity(activity.get_id()) - self._set_activity_ps(ps) - else: - self._set_activity_ps(None) - - def __buddy_joined_cb(self, activity, buddy): - self.add_buddy(buddy) - - def __buddy_left_cb(self, activity, buddy): - self.remove_buddy(buddy) + def __init__(self, shell, menu_shell): + hippo.CanvasBox.__init__(self) + self._shell = shell + self._menu_shell = menu_shell + self._activity_ps = None + self._joined_hid = -1 + self._left_hid = -1 + self._buddies = {} + + self._pservice = PresenceService.get_instance() + self._pservice.connect('activity-appeared', + self.__activity_appeared_cb) + + # Add initial activities the PS knows about + for activity in self._pservice.get_activities(): + self.__activity_appeared_cb(self._pservice, activity) + + shell.connect('activity-changed', self.__activity_changed_cb) + + def add_buddy(self, buddy): + if self._buddies.has_key(buddy.get_name()): + return + + model = BuddyModel(buddy=buddy) + icon = BuddyIcon(self._shell, self._menu_shell, model) + style.apply_stylesheet(icon, 'frame.BuddyIcon') + self.append(icon) + + self._buddies[buddy.get_name()] = icon + + def remove_buddy(self, buddy): + if not self._buddies.has_key(buddy.get_name()): + return + + self.remove(self._buddies[buddy.get_name()]) + + def clear(self): + for item in self.get_children(): + self.remove(item) + self._buddies = {} + + def __activity_appeared_cb(self, pservice, activity_ps): + activity = self._shell.get_current_activity() + if activity and activity_ps.get_id() == activity.get_id(): + self._set_activity_ps(activity_ps) + + def _set_activity_ps(self, activity_ps): + if self._activity_ps == activity_ps: + return + + if self._joined_hid > 0: + self._activity_ps.disconnect(self._joined_hid) + self._joined_hid = -1 + if self._left_hid > 0: + self._activity_ps.disconnect(self._left_hid) + self._left_hid = -1 + + self._activity_ps = activity_ps + + self.clear() + + if activity_ps != None: + for buddy in activity_ps.get_joined_buddies(): + self.add_buddy(buddy) + + self._joined_hid = activity_ps.connect( + 'buddy-joined', self.__buddy_joined_cb) + self._left_hid = activity_ps.connect( + 'buddy-left', self.__buddy_left_cb) + + def __activity_changed_cb(self, group, activity): + if activity: + ps = self._pservice.get_activity(activity.get_id()) + self._set_activity_ps(ps) + else: + self._set_activity_ps(None) + + def __buddy_joined_cb(self, activity, buddy): + self.add_buddy(buddy) + + def __buddy_left_cb(self, activity, buddy): + self.remove_buddy(buddy) diff --git a/shell/view/frame/PanelWindow.py b/shell/view/frame/PanelWindow.py index 14e72b1..8fd8145 100644 --- a/shell/view/frame/PanelWindow.py +++ b/shell/view/frame/PanelWindow.py @@ -20,28 +20,28 @@ import hippo from sugar.graphics.menushell import MenuShell class PanelWindow(gtk.Window): - def __init__(self): - gtk.Window.__init__(self) + def __init__(self): + gtk.Window.__init__(self) - self.set_decorated(False) - self.connect('realize', self._realize_cb) + self.set_decorated(False) + self.connect('realize', self._realize_cb) - canvas = hippo.Canvas() + canvas = hippo.Canvas() - self._bg = hippo.CanvasBox(background_color=0x414141ff) - canvas.set_root(self._bg) + self._bg = hippo.CanvasBox(background_color=0x414141ff) + canvas.set_root(self._bg) - self.add(canvas) - canvas.show() + self.add(canvas) + canvas.show() - self._menu_shell = MenuShell(canvas) + self._menu_shell = MenuShell(canvas) - def get_menu_shell(self): - return self._menu_shell + def get_menu_shell(self): + return self._menu_shell - def get_root(self): - return self._bg + def get_root(self): + return self._bg - def _realize_cb(self, widget): - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) - self.window.set_accept_focus(False) + def _realize_cb(self, widget): + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self.window.set_accept_focus(False) diff --git a/shell/view/frame/ZoomBox.py b/shell/view/frame/ZoomBox.py index 45c76cd..ed7e0a1 100644 --- a/shell/view/frame/ZoomBox.py +++ b/shell/view/frame/ZoomBox.py @@ -23,98 +23,98 @@ from sugar.graphics import style import sugar class ActivityMenu(Menu): - ACTION_SHARE = 1 - ACTION_CLOSE = 2 + ACTION_SHARE = 1 + ACTION_CLOSE = 2 - def __init__(self, activity_host): - Menu.__init__(self, activity_host.get_title()) + def __init__(self, activity_host): + Menu.__init__(self, activity_host.get_title()) - if not activity_host.get_shared(): - self._add_mesh_action() + if not activity_host.get_shared(): + self._add_mesh_action() - self._add_close_action() + self._add_close_action() - def _add_mesh_action(self): - icon = CanvasIcon(icon_name='stock-share-mesh') - self.add_action(icon, ActivityMenu.ACTION_SHARE) + def _add_mesh_action(self): + icon = CanvasIcon(icon_name='stock-share-mesh') + self.add_action(icon, ActivityMenu.ACTION_SHARE) - def _add_close_action(self): - icon = CanvasIcon(icon_name='stock-close') - self.add_action(icon, ActivityMenu.ACTION_CLOSE) + def _add_close_action(self): + icon = CanvasIcon(icon_name='stock-close') + self.add_action(icon, ActivityMenu.ACTION_CLOSE) class ActivityIcon(MenuIcon): - def __init__(self, shell, menu_shell, activity_host): - self._shell = shell - self._activity_host = activity_host + def __init__(self, shell, menu_shell, activity_host): + self._shell = shell + self._activity_host = activity_host - icon_name = activity_host.get_icon_name() - icon_color = activity_host.get_icon_color() + icon_name = activity_host.get_icon_name() + icon_color = activity_host.get_icon_color() - MenuIcon.__init__(self, menu_shell, icon_name=icon_name, - color=icon_color) + MenuIcon.__init__(self, menu_shell, icon_name=icon_name, + color=icon_color) - def create_menu(self): - menu = ActivityMenu(self._activity_host) - menu.connect('action', self._action_cb) - return menu + def create_menu(self): + menu = ActivityMenu(self._activity_host) + menu.connect('action', self._action_cb) + return menu - def _action_cb(self, menu, action): - self.popdown() + def _action_cb(self, menu, action): + self.popdown() - activity = self._shell.get_current_activity() - if activity == None: - return + activity = self._shell.get_current_activity() + if activity == None: + return - if action == ActivityMenu.ACTION_SHARE: - activity.share() - if action == ActivityMenu.ACTION_CLOSE: - activity.close() + if action == ActivityMenu.ACTION_SHARE: + activity.share() + if action == ActivityMenu.ACTION_CLOSE: + activity.close() class ZoomBox(hippo.CanvasBox): - def __init__(self, shell, menu_shell): - hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) - - self._shell = shell - self._menu_shell = menu_shell - self._activity_icon = None - - icon = CanvasIcon(icon_name='stock-zoom-mesh') - style.apply_stylesheet(icon, 'frame.ZoomIcon') - icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_MESH) - self.append(icon) - - icon = CanvasIcon(icon_name='stock-zoom-friends') - style.apply_stylesheet(icon, 'frame.ZoomIcon') - icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_FRIENDS) - self.append(icon) - - icon = CanvasIcon(icon_name='stock-zoom-home') - style.apply_stylesheet(icon, 'frame.ZoomIcon') - icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_HOME) - self.append(icon) - - icon = CanvasIcon(icon_name='stock-zoom-activity') - style.apply_stylesheet(icon, 'frame.ZoomIcon') - icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_ACTIVITY) - self.append(icon) - - shell.connect('activity-changed', self._activity_changed_cb) - self._set_current_activity(shell.get_current_activity()) - - def _set_current_activity(self, activity): - if self._activity_icon: - self.remove(self._activity_icon) - - if activity: - icon = ActivityIcon(self._shell, self._menu_shell, activity) - style.apply_stylesheet(icon, 'frame.ZoomIcon') - self.append(icon, 0) - self._activity_icon = icon - else: - self._activity_icon = None - - def _activity_changed_cb(self, shell_model, activity): - self._set_current_activity(activity) - - def _level_clicked_cb(self, item, level): - self._shell.set_zoom_level(level) + def __init__(self, shell, menu_shell): + hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) + + self._shell = shell + self._menu_shell = menu_shell + self._activity_icon = None + + icon = CanvasIcon(icon_name='stock-zoom-mesh') + style.apply_stylesheet(icon, 'frame.ZoomIcon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_MESH) + self.append(icon) + + icon = CanvasIcon(icon_name='stock-zoom-friends') + style.apply_stylesheet(icon, 'frame.ZoomIcon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_FRIENDS) + self.append(icon) + + icon = CanvasIcon(icon_name='stock-zoom-home') + style.apply_stylesheet(icon, 'frame.ZoomIcon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_HOME) + self.append(icon) + + icon = CanvasIcon(icon_name='stock-zoom-activity') + style.apply_stylesheet(icon, 'frame.ZoomIcon') + icon.connect('activated', self._level_clicked_cb, sugar.ZOOM_ACTIVITY) + self.append(icon) + + shell.connect('activity-changed', self._activity_changed_cb) + self._set_current_activity(shell.get_current_activity()) + + def _set_current_activity(self, activity): + if self._activity_icon: + self.remove(self._activity_icon) + + if activity: + icon = ActivityIcon(self._shell, self._menu_shell, activity) + style.apply_stylesheet(icon, 'frame.ZoomIcon') + self.append(icon, 0) + self._activity_icon = icon + else: + self._activity_icon = None + + def _activity_changed_cb(self, shell_model, activity): + self._set_current_activity(activity) + + def _level_clicked_cb(self, item, level): + self._shell.set_zoom_level(level) diff --git a/shell/view/frame/notificationtray.py b/shell/view/frame/notificationtray.py index 5fa5123..b304868 100644 --- a/shell/view/frame/notificationtray.py +++ b/shell/view/frame/notificationtray.py @@ -3,16 +3,16 @@ import gtk from _sugar import TrayManager class NotificationTray(gtk.HBox): - def __init__(self): - gtk.HBox.__init__(self) + def __init__(self): + gtk.HBox.__init__(self) - self._manager = TrayManager() - self._manager.connect('tray-icon-added', self._icon_added_cb) - self._manager.connect('tray-icon-removed', self._icon_removed_cb) - self._manager.manage_screen(gtk.gdk.screen_get_default()) + self._manager = TrayManager() + self._manager.connect('tray-icon-added', self._icon_added_cb) + self._manager.connect('tray-icon-removed', self._icon_removed_cb) + self._manager.manage_screen(gtk.gdk.screen_get_default()) - def _icon_added_cb(self, manager, icon): - self.pack_start(icon, False) + def _icon_added_cb(self, manager, icon): + self.pack_start(icon, False) - def _icon_removed_cb(self, manager, icon): - icon.destroy() + def _icon_removed_cb(self, manager, icon): + icon.destroy() diff --git a/shell/view/frame/overlaybox.py b/shell/view/frame/overlaybox.py index eaa1e5d..406173a 100644 --- a/shell/view/frame/overlaybox.py +++ b/shell/view/frame/overlaybox.py @@ -4,15 +4,15 @@ from sugar.graphics import style from sugar.graphics.canvasicon import CanvasIcon class OverlayBox(hippo.CanvasBox): - def __init__(self, shell): - hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) + def __init__(self, shell): + hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) - self._shell = shell + self._shell = shell - icon = CanvasIcon(icon_name='stock-chat') - style.apply_stylesheet(icon, 'frame.OverlayIcon') - icon.connect('activated', self._overlay_clicked_cb) - self.append(icon) + icon = CanvasIcon(icon_name='stock-chat') + style.apply_stylesheet(icon, 'frame.OverlayIcon') + icon.connect('activated', self._overlay_clicked_cb) + self.append(icon) - def _overlay_clicked_cb(self, item): - self._shell.toggle_chat_visibility() + def _overlay_clicked_cb(self, item): + self._shell.toggle_chat_visibility() diff --git a/shell/view/frame/shutdownicon.py b/shell/view/frame/shutdownicon.py index 520ba20..5814f72 100644 --- a/shell/view/frame/shutdownicon.py +++ b/shell/view/frame/shutdownicon.py @@ -21,24 +21,24 @@ from sugar.graphics.menu import Menu from sugar.graphics import style class ShutdownIcon(MenuIcon): - ACTION_SHUTDOWN = 2 + ACTION_SHUTDOWN = 2 - def __init__(self, menu_shell): - MenuIcon.__init__(self, menu_shell, icon_name='stock-close') - style.apply_stylesheet(self, 'menu.ActionIcon') + def __init__(self, menu_shell): + MenuIcon.__init__(self, menu_shell, icon_name='stock-close') + style.apply_stylesheet(self, 'menu.ActionIcon') - def create_menu(self): - menu = Menu() - menu.add_item('Shut Down', ShutdownIcon.ACTION_SHUTDOWN) - menu.connect('action', self._action_cb) - return menu + def create_menu(self): + menu = Menu() + menu.add_item('Shut Down', ShutdownIcon.ACTION_SHUTDOWN) + menu.connect('action', self._action_cb) + return menu - def _action_cb(self, menu, action): - self.popdown() + def _action_cb(self, menu, action): + self.popdown() - if action == ShutdownIcon.ACTION_SHUTDOWN: - bus = dbus.SystemBus() - proxy = bus.get_object('org.freedesktop.Hal', - '/org/freedesktop/Hal/devices/computer') - mgr = dbus.Interface(proxy, 'org.freedesktop.Hal.Device.SystemPowerManagement') - mgr.Shutdown() + if action == ShutdownIcon.ACTION_SHUTDOWN: + bus = dbus.SystemBus() + proxy = bus.get_object('org.freedesktop.Hal', + '/org/freedesktop/Hal/devices/computer') + mgr = dbus.Interface(proxy, 'org.freedesktop.Hal.Device.SystemPowerManagement') + mgr.Shutdown() diff --git a/shell/view/home/FriendView.py b/shell/view/home/FriendView.py index 2b5cf7c..6d24f76 100644 --- a/shell/view/home/FriendView.py +++ b/shell/view/home/FriendView.py @@ -23,67 +23,67 @@ from sugar.graphics import style from sugar.presence import PresenceService class FriendView(hippo.CanvasBox): - def __init__(self, shell, menu_shell, buddy, **kwargs): - hippo.CanvasBox.__init__(self, **kwargs) + def __init__(self, shell, menu_shell, buddy, **kwargs): + hippo.CanvasBox.__init__(self, **kwargs) - self._pservice = PresenceService.get_instance() + self._pservice = PresenceService.get_instance() - self._buddy = buddy - self._buddy_icon = BuddyIcon(shell, menu_shell, buddy) - style.apply_stylesheet(self._buddy_icon, 'friends.FriendIcon') - self.append(self._buddy_icon) + self._buddy = buddy + self._buddy_icon = BuddyIcon(shell, menu_shell, buddy) + style.apply_stylesheet(self._buddy_icon, 'friends.FriendIcon') + self.append(self._buddy_icon) - self._activity_icon = CanvasIcon() - style.apply_stylesheet(self._activity_icon, 'friends.ActivityIcon') - self._activity_icon_visible = False + self._activity_icon = CanvasIcon() + style.apply_stylesheet(self._activity_icon, 'friends.ActivityIcon') + self._activity_icon_visible = False - if self._buddy.is_present(): - self._buddy_appeared_cb(buddy) + if self._buddy.is_present(): + self._buddy_appeared_cb(buddy) - self._buddy.connect('current-activity-changed', self._buddy_activity_changed_cb) - self._buddy.connect('appeared', self._buddy_appeared_cb) - self._buddy.connect('disappeared', self._buddy_disappeared_cb) - self._buddy.connect('color-changed', self._buddy_color_changed_cb) + self._buddy.connect('current-activity-changed', self._buddy_activity_changed_cb) + self._buddy.connect('appeared', self._buddy_appeared_cb) + self._buddy.connect('disappeared', self._buddy_disappeared_cb) + self._buddy.connect('color-changed', self._buddy_color_changed_cb) - def _get_new_icon_name(self, activity): - # FIXME: do something better here; we probably need to use "flagship" - # services like mDNS where activities default services are marked - # somehow. - activity_registry = shell.get_model().get_bundle_registry() - for serv in activity.get_services(): - bundle = activity_registry.get_bundle(serv.get_type()) - if bundle: - return bundle.get_icon() - return None + def _get_new_icon_name(self, activity): + # FIXME: do something better here; we probably need to use "flagship" + # services like mDNS where activities default services are marked + # somehow. + activity_registry = shell.get_model().get_bundle_registry() + for serv in activity.get_services(): + bundle = activity_registry.get_bundle(serv.get_type()) + if bundle: + return bundle.get_icon() + return None - def _remove_activity_icon(self): - if self._activity_icon_visible: - self.remove(self._activity_icon) - self._activity_icon_visible = False + def _remove_activity_icon(self): + if self._activity_icon_visible: + self.remove(self._activity_icon) + self._activity_icon_visible = False - def _buddy_activity_changed_cb(self, buddy, activity=None): - if not activity: - self._remove_activity_icon() - return + def _buddy_activity_changed_cb(self, buddy, activity=None): + if not activity: + self._remove_activity_icon() + return - # FIXME: use some sort of "unknown activity" icon rather - # than hiding the icon? - name = self._get_new_icon_name(activity) - if name: - self._activity_icon.props.icon_name = name - self._activity_icon.props.color = buddy.get_color() - if not self._activity_icon_visible: - self.append(self._activity_icon, hippo.PACK_EXPAND) - self._activity_icon_visible = True - else: - self._remove_activity_icon() + # FIXME: use some sort of "unknown activity" icon rather + # than hiding the icon? + name = self._get_new_icon_name(activity) + if name: + self._activity_icon.props.icon_name = name + self._activity_icon.props.color = buddy.get_color() + if not self._activity_icon_visible: + self.append(self._activity_icon, hippo.PACK_EXPAND) + self._activity_icon_visible = True + else: + self._remove_activity_icon() - def _buddy_appeared_cb(self, buddy): - activity = self._buddy.get_current_activity() - self._buddy_activity_changed_cb(buddy, activity) + def _buddy_appeared_cb(self, buddy): + activity = self._buddy.get_current_activity() + self._buddy_activity_changed_cb(buddy, activity) - def _buddy_disappeared_cb(self, buddy): - self._buddy_activity_changed_cb(buddy, None) + def _buddy_disappeared_cb(self, buddy): + self._buddy_activity_changed_cb(buddy, None) - def _buddy_color_changed_cb(self, buddy, color): - self._activity_icon.props.color = buddy.get_color() + def _buddy_color_changed_cb(self, buddy, color): + self._activity_icon.props.color = buddy.get_color() diff --git a/shell/view/home/FriendsBox.py b/shell/view/home/FriendsBox.py index 28a745e..ec4aad6 100644 --- a/shell/view/home/FriendsBox.py +++ b/shell/view/home/FriendsBox.py @@ -25,42 +25,42 @@ from view.home.MyIcon import MyIcon from view.home.FriendView import FriendView class FriendsBox(SpreadBox, hippo.CanvasItem): - __gtype_name__ = 'SugarFriendsBox' - def __init__(self, shell, menu_shell): - SpreadBox.__init__(self, background_color=0xe2e2e2ff) + __gtype_name__ = 'SugarFriendsBox' + def __init__(self, shell, menu_shell): + SpreadBox.__init__(self, background_color=0xe2e2e2ff) - self._shell = shell - self._menu_shell = menu_shell - self._friends = {} + self._shell = shell + self._menu_shell = menu_shell + self._friends = {} - self._my_icon = MyIcon() - style.apply_stylesheet(self._my_icon, 'friends.MyIcon') - self.append(self._my_icon, hippo.PACK_FIXED) + self._my_icon = MyIcon() + style.apply_stylesheet(self._my_icon, 'friends.MyIcon') + self.append(self._my_icon, hippo.PACK_FIXED) - friends = self._shell.get_model().get_friends() + friends = self._shell.get_model().get_friends() - for friend in friends: - self.add_friend(friend) + for friend in friends: + self.add_friend(friend) - friends.connect('friend-added', self._friend_added_cb) - friends.connect('friend-removed', self._friend_removed_cb) + friends.connect('friend-added', self._friend_added_cb) + friends.connect('friend-removed', self._friend_removed_cb) - def add_friend(self, buddy_info): - icon = FriendView(self._shell, self._menu_shell, buddy_info) - self.add_item(icon) + def add_friend(self, buddy_info): + icon = FriendView(self._shell, self._menu_shell, buddy_info) + self.add_item(icon) - self._friends[buddy_info.get_name()] = icon + self._friends[buddy_info.get_name()] = icon - def _friend_added_cb(self, data_model, buddy_info): - self.add_friend(buddy_info) + def _friend_added_cb(self, data_model, buddy_info): + self.add_friend(buddy_info) - def _friend_removed_cb(self, data_model, name): - self.remove_item(self._friends[name]) - del self._friends[name] + def _friend_removed_cb(self, data_model, name): + self.remove_item(self._friends[name]) + del self._friends[name] - def do_allocate(self, width, height): - SpreadBox.do_allocate(self, width, height) + def do_allocate(self, width, height): + SpreadBox.do_allocate(self, width, height) - [icon_width, icon_height] = self._my_icon.get_allocation() - self.move(self._my_icon, (width - icon_width) / 2, - (height - icon_height) / 2) + [icon_width, icon_height] = self._my_icon.get_allocation() + self.move(self._my_icon, (width - icon_width) / 2, + (height - icon_height) / 2) diff --git a/shell/view/home/HomeBox.py b/shell/view/home/HomeBox.py index 146056b..a24402d 100644 --- a/shell/view/home/HomeBox.py +++ b/shell/view/home/HomeBox.py @@ -22,24 +22,24 @@ from sugar.graphics.grid import Grid from sugar.graphics import style class HomeBox(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'SugarHomeBox' + __gtype_name__ = 'SugarHomeBox' - def __init__(self, shell): - hippo.CanvasBox.__init__(self, background_color=0xe2e2e2ff, - yalign=2) + def __init__(self, shell): + hippo.CanvasBox.__init__(self, background_color=0xe2e2e2ff, + yalign=2) - grid = Grid() - donut = ActivitiesDonut(shell, box_width=grid.dimension(7), - box_height=grid.dimension(7)) - self.append(donut) + grid = Grid() + donut = ActivitiesDonut(shell, box_width=grid.dimension(7), + box_height=grid.dimension(7)) + self.append(donut) - self._my_icon = MyIcon() - style.apply_stylesheet(self._my_icon, 'home.MyIcon') - self.append(self._my_icon, hippo.PACK_FIXED) + self._my_icon = MyIcon() + style.apply_stylesheet(self._my_icon, 'home.MyIcon') + self.append(self._my_icon, hippo.PACK_FIXED) - 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) - [icon_width, icon_height] = self._my_icon.get_allocation() - self.move(self._my_icon, (width - icon_width) / 2, - (height - icon_height) / 2) + [icon_width, icon_height] = self._my_icon.get_allocation() + self.move(self._my_icon, (width - icon_width) / 2, + (height - icon_height) / 2) diff --git a/shell/view/home/HomeWindow.py b/shell/view/home/HomeWindow.py index e07c7d0..b450b8c 100644 --- a/shell/view/home/HomeWindow.py +++ b/shell/view/home/HomeWindow.py @@ -25,45 +25,45 @@ from view.home.HomeBox import HomeBox from view.home.FriendsBox import FriendsBox class HomeWindow(gtk.Window): - def __init__(self, shell): - gtk.Window.__init__(self) - self._shell = shell + def __init__(self, shell): + gtk.Window.__init__(self) + self._shell = shell - self.set_default_size(gtk.gdk.screen_width(), - gtk.gdk.screen_height()) + self.set_default_size(gtk.gdk.screen_width(), + gtk.gdk.screen_height()) - self.realize() - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP) + self.realize() + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DESKTOP) - self._nb = gtk.Notebook() - self._nb.set_show_border(False) - self._nb.set_show_tabs(False) + self._nb = gtk.Notebook() + self._nb.set_show_border(False) + self._nb.set_show_tabs(False) - self.add(self._nb) - self._nb.show() + self.add(self._nb) + self._nb.show() - canvas = hippo.Canvas() - box = HomeBox(shell) - canvas.set_root(box) - self._nb.append_page(canvas) - canvas.show() + canvas = hippo.Canvas() + box = HomeBox(shell) + canvas.set_root(box) + self._nb.append_page(canvas) + canvas.show() - canvas = hippo.Canvas() - box = FriendsBox(shell, MenuShell(canvas)) - canvas.set_root(box) - self._nb.append_page(canvas) - canvas.show() + canvas = hippo.Canvas() + box = FriendsBox(shell, MenuShell(canvas)) + canvas.set_root(box) + self._nb.append_page(canvas) + canvas.show() - canvas = hippo.Canvas() - box = MeshBox(shell, MenuShell(canvas)) - canvas.set_root(box) - self._nb.append_page(canvas) - canvas.show() + canvas = hippo.Canvas() + box = MeshBox(shell, MenuShell(canvas)) + canvas.set_root(box) + self._nb.append_page(canvas) + canvas.show() - def set_zoom_level(self, level): - if level == sugar.ZOOM_HOME: - self._nb.set_current_page(0) - elif level == sugar.ZOOM_FRIENDS: - self._nb.set_current_page(1) - elif level == sugar.ZOOM_MESH: - self._nb.set_current_page(2) + def set_zoom_level(self, level): + if level == sugar.ZOOM_HOME: + self._nb.set_current_page(0) + elif level == sugar.ZOOM_FRIENDS: + self._nb.set_current_page(1) + elif level == sugar.ZOOM_MESH: + self._nb.set_current_page(2) diff --git a/shell/view/home/MeshBox.py b/shell/view/home/MeshBox.py index 1c61d1a..5ea4aef 100644 --- a/shell/view/home/MeshBox.py +++ b/shell/view/home/MeshBox.py @@ -25,116 +25,116 @@ from sugar.graphics.canvasicon import CanvasIcon from view.BuddyIcon import BuddyIcon class ActivityView(SnowflakeBox): - def __init__(self, shell, menu_shell, model): - SnowflakeBox.__init__(self) + def __init__(self, shell, menu_shell, model): + SnowflakeBox.__init__(self) - self._shell = shell - self._model = model - self._icons = {} + self._shell = shell + self._model = model + self._icons = {} - icon = CanvasIcon(icon_name=model.get_icon_name(), - color=model.get_color(), size=80) - icon.connect('activated', self._clicked_cb) - self.append(icon, hippo.PACK_FIXED) - self.set_root(icon) + icon = CanvasIcon(icon_name=model.get_icon_name(), + color=model.get_color(), size=80) + icon.connect('activated', self._clicked_cb) + self.append(icon, hippo.PACK_FIXED) + self.set_root(icon) - def has_buddy_icon(self, name): - return self._icons.has_key(name) + def has_buddy_icon(self, name): + return self._icons.has_key(name) - def add_buddy_icon(self, name, icon): - self._icons[name] = icon - self.append(icon, hippo.PACK_FIXED) + def add_buddy_icon(self, name, icon): + self._icons[name] = icon + self.append(icon, hippo.PACK_FIXED) - def remove_buddy_icon(self, name): - icon = self._icons[name] - self.remove(icon) - del self._icons[name] + def remove_buddy_icon(self, name): + icon = self._icons[name] + self.remove(icon) + del self._icons[name] - def _clicked_cb(self, item): - bundle_id = self._model.get_service().get_type() - self._shell.join_activity(bundle_id, self._model.get_id()) + def _clicked_cb(self, item): + bundle_id = self._model.get_service().get_type() + self._shell.join_activity(bundle_id, self._model.get_id()) class MeshBox(SpreadBox): - def __init__(self, shell, menu_shell): - SpreadBox.__init__(self, background_color=0xe2e2e2ff) + def __init__(self, shell, menu_shell): + SpreadBox.__init__(self, background_color=0xe2e2e2ff) - self._shell = shell - self._menu_shell = menu_shell - self._model = shell.get_model().get_mesh() - self._buddies = {} - self._activities = {} - self._buddy_to_activity = {} + self._shell = shell + self._menu_shell = menu_shell + self._model = shell.get_model().get_mesh() + self._buddies = {} + self._activities = {} + self._buddy_to_activity = {} - for buddy_model in self._model.get_buddies(): - self._add_alone_buddy(buddy_model) + for buddy_model in self._model.get_buddies(): + self._add_alone_buddy(buddy_model) - self._model.connect('buddy-added', self._buddy_added_cb) - self._model.connect('buddy-removed', self._buddy_removed_cb) - self._model.connect('buddy-moved', self._buddy_moved_cb) + self._model.connect('buddy-added', self._buddy_added_cb) + self._model.connect('buddy-removed', self._buddy_removed_cb) + self._model.connect('buddy-moved', self._buddy_moved_cb) - for activity_model in self._model.get_activities(): - self._add_activity(activity_model) + for activity_model in self._model.get_activities(): + self._add_activity(activity_model) - self._model.connect('activity-added', self._activity_added_cb) - self._model.connect('activity-removed', self._activity_removed_cb) + self._model.connect('activity-added', self._activity_added_cb) + self._model.connect('activity-removed', self._activity_removed_cb) - def _buddy_added_cb(self, model, buddy_model): - self._add_alone_buddy(buddy_model) + def _buddy_added_cb(self, model, buddy_model): + self._add_alone_buddy(buddy_model) - def _buddy_removed_cb(self, model, buddy_model): - self._remove_buddy(buddy_model) + def _buddy_removed_cb(self, model, buddy_model): + self._remove_buddy(buddy_model) - def _buddy_moved_cb(self, model, buddy_model, activity_model): - self._move_buddy(buddy_model, activity_model) + def _buddy_moved_cb(self, model, buddy_model, activity_model): + self._move_buddy(buddy_model, activity_model) - def _activity_added_cb(self, model, activity_model): - self._add_activity(activity_model) + def _activity_added_cb(self, model, activity_model): + self._add_activity(activity_model) - def _activity_removed_cb(self, model, activity_model): - self._remove_activity(activity_model) + def _activity_removed_cb(self, model, activity_model): + self._remove_activity(activity_model) - def _add_alone_buddy(self, buddy_model): - icon = BuddyIcon(self._shell, self._menu_shell, buddy_model) - icon.props.size = 80 - self.add_item(icon) + def _add_alone_buddy(self, buddy_model): + icon = BuddyIcon(self._shell, self._menu_shell, buddy_model) + icon.props.size = 80 + self.add_item(icon) - self._buddies[buddy_model.get_name()] = icon + self._buddies[buddy_model.get_name()] = icon - def _remove_alone_buddy(self, buddy_model): - icon = self._buddies[buddy_model.get_name()] - self.remove_item(icon) - del self._buddies[buddy_model.get_name()] + def _remove_alone_buddy(self, buddy_model): + icon = self._buddies[buddy_model.get_name()] + self.remove_item(icon) + del self._buddies[buddy_model.get_name()] - def _remove_buddy(self, buddy_model): - name = buddy_model.get_name() - if self._buddies.has_key(name): - self._remove_alone_buddy(buddy_model) - else: - for activity in self._activities.values(): - if activity.has_buddy_icon(name): - activity.remove_buddy_icon(name) + def _remove_buddy(self, buddy_model): + name = buddy_model.get_name() + if self._buddies.has_key(name): + self._remove_alone_buddy(buddy_model) + else: + for activity in self._activities.values(): + if activity.has_buddy_icon(name): + activity.remove_buddy_icon(name) - def _move_buddy(self, buddy_model, activity_model): - name = buddy_model.get_name() + def _move_buddy(self, buddy_model, activity_model): + name = buddy_model.get_name() - self._remove_buddy(buddy_model) + self._remove_buddy(buddy_model) - if activity_model == None: - self._add_alone_buddy(buddy_model) - else: - activity = self._activities[activity_model.get_id()] + if activity_model == None: + self._add_alone_buddy(buddy_model) + else: + activity = self._activities[activity_model.get_id()] - icon = BuddyIcon(self._shell, self._menu_shell, buddy_model) - icon.props.size = 60 - activity.add_buddy_icon(buddy_model.get_name(), icon) + icon = BuddyIcon(self._shell, self._menu_shell, buddy_model) + icon.props.size = 60 + activity.add_buddy_icon(buddy_model.get_name(), icon) - def _add_activity(self, activity_model): - icon = ActivityView(self._shell, self._menu_shell, activity_model) - self.add_item(icon) + def _add_activity(self, activity_model): + icon = ActivityView(self._shell, self._menu_shell, activity_model) + self.add_item(icon) - self._activities[activity_model.get_id()] = icon + self._activities[activity_model.get_id()] = icon - def _remove_activity(self, activity_model): - icon = self._activities[activity_model.get_id()] - self.remove_item(icon) - del self._activities[activity_model.get_id()] + def _remove_activity(self, activity_model): + icon = self._activities[activity_model.get_id()] + self.remove_item(icon) + del self._activities[activity_model.get_id()] diff --git a/shell/view/home/MyIcon.py b/shell/view/home/MyIcon.py index df59317..80e3a04 100644 --- a/shell/view/home/MyIcon.py +++ b/shell/view/home/MyIcon.py @@ -18,6 +18,6 @@ from sugar.graphics.canvasicon import CanvasIcon from sugar import profile class MyIcon(CanvasIcon): - def __init__(self): - CanvasIcon.__init__(self, icon_name='stock-buddy', - color=profile.get_color()) + def __init__(self): + CanvasIcon.__init__(self, icon_name='stock-buddy', + color=profile.get_color()) diff --git a/shell/view/home/activitiesdonut.py b/shell/view/home/activitiesdonut.py index f7d24fc..74f3bbb 100644 --- a/shell/view/home/activitiesdonut.py +++ b/shell/view/home/activitiesdonut.py @@ -21,100 +21,100 @@ from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics import style class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'SugarActivitiesDonut' - def __init__(self, shell, **kwargs): - hippo.CanvasBox.__init__(self, **kwargs) + __gtype_name__ = 'SugarActivitiesDonut' + def __init__(self, shell, **kwargs): + hippo.CanvasBox.__init__(self, **kwargs) - self._activities = {} + self._activities = {} - shell.connect('activity_opened', self.__activity_opened_cb) - shell.connect('activity_closed', self.__activity_closed_cb) + shell.connect('activity_opened', self.__activity_opened_cb) + shell.connect('activity_closed', self.__activity_closed_cb) - def __activity_opened_cb(self, model, activity): - self._add_activity(activity) + def __activity_opened_cb(self, model, activity): + self._add_activity(activity) - def __activity_closed_cb(self, model, activity): - self._remove_activity(activity) - - def _remove_activity(self, activity): - icon = self._activities[activity.get_id()] - self.remove(icon) - del self._activities[activity.get_id()] + def __activity_closed_cb(self, model, activity): + self._remove_activity(activity) + + def _remove_activity(self, activity): + icon = self._activities[activity.get_id()] + self.remove(icon) + del self._activities[activity.get_id()] - def _add_activity(self, activity): - icon_name = activity.get_icon_name() - icon_color = activity.get_icon_color() + def _add_activity(self, activity): + icon_name = activity.get_icon_name() + icon_color = activity.get_icon_color() - icon = CanvasIcon(icon_name=icon_name, color=icon_color) - style.apply_stylesheet(icon, 'ring.ActivityIcon') - icon.connect('activated', self.__activity_icon_clicked_cb, activity) - self.append(icon, hippo.PACK_FIXED) + icon = CanvasIcon(icon_name=icon_name, color=icon_color) + style.apply_stylesheet(icon, 'ring.ActivityIcon') + icon.connect('activated', self.__activity_icon_clicked_cb, activity) + self.append(icon, hippo.PACK_FIXED) - self._activities[activity.get_id()] = icon + self._activities[activity.get_id()] = icon - self.emit_paint_needed(0, 0, -1, -1) + self.emit_paint_needed(0, 0, -1, -1) - def __activity_icon_clicked_cb(self, item, activity): - activity.present() + def __activity_icon_clicked_cb(self, item, activity): + activity.present() - def _get_angles(self, index): - angle = 2 * math.pi / 8 - return [index * angle, (index + 1) * angle] + def _get_angles(self, index): + angle = 2 * math.pi / 8 + return [index * angle, (index + 1) * angle] - def _get_radius(self): - [width, height] = self.get_allocation() - return min(width, height) / 2 + def _get_radius(self): + [width, height] = self.get_allocation() + return min(width, height) / 2 - def _get_inner_radius(self): - return self._get_radius() * 0.5 + def _get_inner_radius(self): + return self._get_radius() * 0.5 - 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() - cr.translate(width / 2, height / 2) + cr.translate(width / 2, height / 2) - radius = self._get_radius() + radius = self._get_radius() - cr.set_source_rgb(0xf1 / 255.0, 0xf1 / 255.0, 0xf1 / 255.0) - cr.arc(0, 0, radius, 0, 2 * math.pi) - cr.fill() + cr.set_source_rgb(0xf1 / 255.0, 0xf1 / 255.0, 0xf1 / 255.0) + cr.arc(0, 0, radius, 0, 2 * math.pi) + cr.fill() - angle_end = 0 - for i in range(0, len(self._activities)): - [angle_start, angle_end] = self._get_angles(i) + angle_end = 0 + for i in range(0, len(self._activities)): + [angle_start, angle_end] = self._get_angles(i) - cr.new_path() - cr.move_to(0, 0) - cr.line_to(radius * math.cos(angle_start), - radius * math.sin(angle_start)) - cr.arc(0, 0, radius, angle_start, angle_end) - cr.line_to(0, 0) + cr.new_path() + cr.move_to(0, 0) + cr.line_to(radius * math.cos(angle_start), + radius * math.sin(angle_start)) + cr.arc(0, 0, radius, angle_start, angle_end) + cr.line_to(0, 0) - cr.set_source_rgb(0xe2 / 255.0, 0xe2 / 255.0, 0xe2 / 255.0) - cr.set_line_width(4) - cr.stroke_preserve() + cr.set_source_rgb(0xe2 / 255.0, 0xe2 / 255.0, 0xe2 / 255.0) + cr.set_line_width(4) + cr.stroke_preserve() - cr.set_source_rgb(1, 1, 1) - cr.fill() + cr.set_source_rgb(1, 1, 1) + cr.fill() - cr.set_source_rgb(0xe2 / 255.0, 0xe2 / 255.0, 0xe2 / 255.0) - cr.arc(0, 0, self._get_inner_radius(), 0, 2 * math.pi) - cr.fill() + cr.set_source_rgb(0xe2 / 255.0, 0xe2 / 255.0, 0xe2 / 255.0) + cr.arc(0, 0, self._get_inner_radius(), 0, 2 * math.pi) + cr.fill() - 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) - radius = (self._get_inner_radius() + self._get_radius()) / 2 + radius = (self._get_inner_radius() + self._get_radius()) / 2 - i = 0 - for icon in self._activities.values(): - [angle_start, angle_end] = self._get_angles(i) - angle = angle_start + (angle_end - angle_start) / 2 + i = 0 + for icon in self._activities.values(): + [angle_start, angle_end] = self._get_angles(i) + angle = angle_start + (angle_end - angle_start) / 2 - [icon_width, icon_height] = icon.get_allocation() + [icon_width, icon_height] = icon.get_allocation() - x = int(radius * math.cos(angle)) - icon_width / 2 - y = int(radius * math.sin(angle)) - icon_height / 2 - self.move(icon, x + width / 2, y + height / 2) + x = int(radius * math.cos(angle)) - icon_width / 2 + y = int(radius * math.sin(angle)) - icon_height / 2 + self.move(icon, x + width / 2, y + height / 2) - i += 1 + i += 1 diff --git a/shell/view/stylesheet.py b/shell/view/stylesheet.py index 1870f9c..5b1c4c6 100644 --- a/shell/view/stylesheet.py +++ b/shell/view/stylesheet.py @@ -21,59 +21,59 @@ from sugar.graphics.iconcolor import IconColor from sugar.graphics import style frame_ActivityIcon = { - 'color' : IconColor('white'), - 'size' : style.standard_icon_size + 'color' : IconColor('white'), + 'size' : style.standard_icon_size } frame_ShutdownIcon = { - 'size' : style.standard_icon_size + 'size' : style.standard_icon_size } frame_OverlayIcon = { - 'size' : style.standard_icon_size + 'size' : style.standard_icon_size } frame_ZoomIcon = { - 'size' : style.standard_icon_size + 'size' : style.standard_icon_size } frame_BuddyIcon = { - 'size' : style.standard_icon_size + 'size' : style.standard_icon_size } home_MyIcon = { - 'size' : style.xlarge_icon_size + 'size' : style.xlarge_icon_size } ring_ActivityIcon = { - 'size' : style.medium_icon_size + 'size' : style.medium_icon_size } friends_MyIcon = { - 'size' : style.large_icon_size + 'size' : style.large_icon_size } friends_FriendIcon = { - 'size' : style.large_icon_size + 'size' : style.large_icon_size } friends_ActivityIcon = { - 'size' : style.standard_icon_size + 'size' : style.standard_icon_size } clipboard_bubble = { - 'fill-color' : 0x646464FF, - 'stroke-color' : 0x646464FF, - 'progress-color': 0x333333FF, - 'spacing' : style.space_unit, - 'padding' : style.space_unit * 1.5 + 'fill-color' : 0x646464FF, + 'stroke-color' : 0x646464FF, + 'progress-color': 0x333333FF, + 'spacing' : style.space_unit, + 'padding' : style.space_unit * 1.5 } clipboard_menu_item_title = { - 'xalign': hippo.ALIGNMENT_START, - 'padding-left': 5, - 'color' : 0xFFFFFFFF, - 'font' : style.get_font_description('Bold', 1.2) + 'xalign': hippo.ALIGNMENT_START, + 'padding-left': 5, + 'color' : 0xFFFFFFFF, + 'font' : style.get_font_description('Bold', 1.2) } style.register_stylesheet("clipboard.Bubble", clipboard_bubble) 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() diff --git a/tests/bundle/Test.activity/testactivity.py b/tests/bundle/Test.activity/testactivity.py index 34cf8b4..ec8032c 100644 --- a/tests/bundle/Test.activity/testactivity.py +++ b/tests/bundle/Test.activity/testactivity.py @@ -1,5 +1,5 @@ from sugar.activity.Activity import Activity class TestActivity(Activity): - def __init__(self): - Activity.__init__(self) + def __init__(self): + Activity.__init__(self) diff --git a/tests/simulator/demo/others.py b/tests/simulator/demo/others.py index 7fee290..0b87c0c 100644 --- a/tests/simulator/demo/others.py +++ b/tests/simulator/demo/others.py @@ -19,19 +19,19 @@ import random from sugar.simulator import Bot for i in range(0, 8): - bot = Bot() + bot = Bot() - bot.wait(random.randint(10, 20)) - bot.join_activity('giraffes') - bot.change_activity('giraffes') + bot.wait(random.randint(10, 20)) + bot.join_activity('giraffes') + bot.change_activity('giraffes') - bot.start() + bot.start() for i in range(0, 6): - bot = Bot() + bot = Bot() - bot.wait(random.randint(10, 20)) - bot.join_activity('nekkhamma') - bot.change_activity('nekkhamma') + bot.wait(random.randint(10, 20)) + bot.join_activity('nekkhamma') + bot.change_activity('nekkhamma') - bot.start() + bot.start() diff --git a/tests/sketch/sketchactivity.py b/tests/sketch/sketchactivity.py index 007d96e..31aff35 100644 --- a/tests/sketch/sketchactivity.py +++ b/tests/sketch/sketchactivity.py @@ -30,205 +30,205 @@ from sugar.graphics.iconcolor import IconColor from sugar import profile class NetworkController(gobject.GObject): - __gsignals__ = { - 'new-path':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), - } - - def __init__(self, parent, ps_owner): - gobject.GObject.__init__(self) - self._parent = parent - self._parent.connect('buddy-joined', self._buddy_joined) - self._parent.connect('buddy-left', self._buddy_left) - self._stream = None - self._stream_writer = None - self._joined_buddies = {} # IP address -> buddy - self._ps_owner = ps_owner - - def init_stream(self, service): - self._stream = Stream.new_from_service(service) - self._stream.set_data_listener(self._recv_message) - self._stream_writer = self._stream.new_writer() - - def _recv_message(self, address, msg): - # Ignore multicast messages from ourself - if self._ps_owner and address == self._ps_owner.get_ip4_address(): - return - - # Ensure the message comes from somebody in this activity - if not self._joined_buddies.has_key(address): - logging.debug("Message from unjoined buddy.") - return - - # Convert the points to an array and send to the sketchpad - points = [] - msg = msg.strip() - split_coords = msg.split(" ") - for item in split_coords: - x = 0 - y = 0 - try: - (x, y) = item.split(",") - x = float(x) - y = float(y) - except ValueError: - continue - if x < 0 or y < 0: - continue - points.append((x, y)) - - buddy = self._joined_buddies[address] - self.emit("new-path", buddy, points) - - def _buddy_joined(self, widget, activity, buddy, activity_type): - activity_service = buddy.get_service_of_type(activity_type, activity) - if not activity_service: - logging.debug("Buddy Joined, but could not get activity service " \ - "of %s" % activity_type) - return - - address = activity_service.get_source_address() - port = activity_service.get_port() - if not address or not port: - logging.debug("Buddy Joined, but could not get address/port from" \ - " activity service %s" % activity_type) - return - if not self._joined_buddies.has_key(address): - logging.debug("Buddy joined: %s (%s)" % (address, port)) - self._joined_buddies[address] = buddy - - def _buddy_left(self, widget, activity, buddy, activity_type): - buddy_key = None - for (key, value) in self._joined_buddies.items(): - if value == buddy: - buddy_key = key - break - if buddy_key: - del self._joined_buddies[buddy_key] - - def new_local_sketch(self, path): - """ Receive an array of point tuples the local user created """ - cmd = "" - # Convert points into the wire format - for point in path: - cmd = cmd + "%d,%d " % (point[0], point[1]) - - # If there were no points, or we aren't in a shared activity yet, - # don't send anything - if not len(cmd) or not self._stream_writer: - return - - # Send the points to other buddies - self._stream_writer.write(cmd) + __gsignals__ = { + 'new-path':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), + } + + def __init__(self, parent, ps_owner): + gobject.GObject.__init__(self) + self._parent = parent + self._parent.connect('buddy-joined', self._buddy_joined) + self._parent.connect('buddy-left', self._buddy_left) + self._stream = None + self._stream_writer = None + self._joined_buddies = {} # IP address -> buddy + self._ps_owner = ps_owner + + def init_stream(self, service): + self._stream = Stream.new_from_service(service) + self._stream.set_data_listener(self._recv_message) + self._stream_writer = self._stream.new_writer() + + def _recv_message(self, address, msg): + # Ignore multicast messages from ourself + if self._ps_owner and address == self._ps_owner.get_ip4_address(): + return + + # Ensure the message comes from somebody in this activity + if not self._joined_buddies.has_key(address): + logging.debug("Message from unjoined buddy.") + return + + # Convert the points to an array and send to the sketchpad + points = [] + msg = msg.strip() + split_coords = msg.split(" ") + for item in split_coords: + x = 0 + y = 0 + try: + (x, y) = item.split(",") + x = float(x) + y = float(y) + except ValueError: + continue + if x < 0 or y < 0: + continue + points.append((x, y)) + + buddy = self._joined_buddies[address] + self.emit("new-path", buddy, points) + + def _buddy_joined(self, widget, activity, buddy, activity_type): + activity_service = buddy.get_service_of_type(activity_type, activity) + if not activity_service: + logging.debug("Buddy Joined, but could not get activity service " \ + "of %s" % activity_type) + return + + address = activity_service.get_source_address() + port = activity_service.get_port() + if not address or not port: + logging.debug("Buddy Joined, but could not get address/port from" \ + " activity service %s" % activity_type) + return + if not self._joined_buddies.has_key(address): + logging.debug("Buddy joined: %s (%s)" % (address, port)) + self._joined_buddies[address] = buddy + + def _buddy_left(self, widget, activity, buddy, activity_type): + buddy_key = None + for (key, value) in self._joined_buddies.items(): + if value == buddy: + buddy_key = key + break + if buddy_key: + del self._joined_buddies[buddy_key] + + def new_local_sketch(self, path): + """ Receive an array of point tuples the local user created """ + cmd = "" + # Convert points into the wire format + for point in path: + cmd = cmd + "%d,%d " % (point[0], point[1]) + + # If there were no points, or we aren't in a shared activity yet, + # don't send anything + if not len(cmd) or not self._stream_writer: + return + + # Send the points to other buddies + self._stream_writer.write(cmd) def _html_to_rgb_color(colorstring): - """ converts #RRGGBB to cairo-suitable floats""" - colorstring = colorstring.strip() - while colorstring[0] == '#': - colorstring = colorstring[1:] - r = int(colorstring[:2], 16) - g = int(colorstring[2:4], 16) - b = int(colorstring[4:6], 16) - color = ((float(r) / 255.0), (float(g) / 255.0), (float(b) / 255.0)) - return color + """ converts #RRGGBB to cairo-suitable floats""" + colorstring = colorstring.strip() + while colorstring[0] == '#': + colorstring = colorstring[1:] + r = int(colorstring[:2], 16) + g = int(colorstring[2:4], 16) + b = int(colorstring[4:6], 16) + color = ((float(r) / 255.0), (float(g) / 255.0), (float(b) / 255.0)) + return color class SharedSketchPad(SketchPad.SketchPad): - def __init__(self, net_controller, color): - SketchPad.SketchPad.__init__(self, bgcolor=(1.0, 0.984313725, 0.560784314)) - self._net_controller = net_controller - self._user_color = _html_to_rgb_color(color) - self.set_color(self._user_color) - - # Receive notifications when our buddies send us new sketches - self._net_controller.connect('new-path', self._new_buddy_path) - - self.connect('new-user-sketch', self._new_local_sketch_cb) - - def _new_buddy_path(self, net_controller, buddy, path): - """ Called whenever a buddy on the mesh sends us a new sketch path """ - str_color = buddy.get_color() - if not str_color: - str_color = "#348798" # FIXME - color = IconColor(str_color) - stroke_color = _html_to_rgb_color(color.get_stroke_color()) - sketch = Sketch.Sketch(stroke_color) - for item in path: - sketch.add_point(item[0], item[1]) - self.add_sketch(sketch) - - def _new_local_sketch_cb(self, widget, sketch): - """ Send the sketch the user just made to the network """ - self._net_controller.new_local_sketch(sketch.get_points()) + def __init__(self, net_controller, color): + SketchPad.SketchPad.__init__(self, bgcolor=(1.0, 0.984313725, 0.560784314)) + self._net_controller = net_controller + self._user_color = _html_to_rgb_color(color) + self.set_color(self._user_color) + + # Receive notifications when our buddies send us new sketches + self._net_controller.connect('new-path', self._new_buddy_path) + + self.connect('new-user-sketch', self._new_local_sketch_cb) + + def _new_buddy_path(self, net_controller, buddy, path): + """ Called whenever a buddy on the mesh sends us a new sketch path """ + str_color = buddy.get_color() + if not str_color: + str_color = "#348798" # FIXME + color = IconColor(str_color) + stroke_color = _html_to_rgb_color(color.get_stroke_color()) + sketch = Sketch.Sketch(stroke_color) + for item in path: + sketch.add_point(item[0], item[1]) + self.add_sketch(sketch) + + def _new_local_sketch_cb(self, widget, sketch): + """ Send the sketch the user just made to the network """ + self._net_controller.new_local_sketch(sketch.get_points()) class SketchActivity(Activity): - __gsignals__ = { - 'buddy-joined':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), - 'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, - ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])) - } - - def __init__(self): - Activity.__init__(self) - self.connect('destroy', self._cleanup_cb) - - self.set_title("Sketch") - - self._ps = PresenceService.get_instance() - self._ps_activity = None - self._owner = self._ps.get_owner() - - self._net_controller = NetworkController(self, self._owner) - self._sketchpad = SharedSketchPad(self._net_controller, - profile.get_color().get_stroke_color()) - self.add(self._sketchpad) - self.show_all() - - def get_ps(self): - return self._ps - - def _cleanup_cb(self): - del self._net_controller - - def share(self): - Activity.share(self) - self._net_controller.init_stream(self._service) - self._ps.connect('activity-appeared', self._activity_appeared_cb) - - def join(self, activity_ps): - Activity.join(self, activity_ps) - self._net_controller.init_stream(self._service) - self._ps.connect('activity-appeared', self._activity_appeared_cb) - self._activity_appeared_cb(self._ps, activity_ps) - - def _activity_appeared_cb(self, ps, activity): - # Only care about our own activity - if activity.get_id() != self.get_id(): - return - - # If we already have found our shared activity, do nothing - if self._ps_activity: - return - - self._ps_activity = activity - - # Connect signals to the shared activity so we are notified when - # buddies join and leave - self._ps_activity.connect('buddy-joined', self._add_buddy) - self._ps_activity.connect('buddy-left', self._remove_buddy) - - # Get the list of buddies already in this shared activity so we can - # connect to them - buddies = self._ps_activity.get_joined_buddies() - for buddy in buddies: - self._add_buddy(self._ps_activity, buddy) - - def _add_buddy(self, ps_activity, buddy): - service_type = self._ps_activity - self.emit('buddy-joined', ps_activity, buddy, self.get_default_type()) - - def _remove_buddy(self, ps_activity, buddy): - self.emit('buddy-left', ps_activity, buddy, self.get_default_type()) + __gsignals__ = { + 'buddy-joined':(gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])), + 'buddy-left': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT])) + } + + def __init__(self): + Activity.__init__(self) + self.connect('destroy', self._cleanup_cb) + + self.set_title("Sketch") + + self._ps = PresenceService.get_instance() + self._ps_activity = None + self._owner = self._ps.get_owner() + + self._net_controller = NetworkController(self, self._owner) + self._sketchpad = SharedSketchPad(self._net_controller, + profile.get_color().get_stroke_color()) + self.add(self._sketchpad) + self.show_all() + + def get_ps(self): + return self._ps + + def _cleanup_cb(self): + del self._net_controller + + def share(self): + Activity.share(self) + self._net_controller.init_stream(self._service) + self._ps.connect('activity-appeared', self._activity_appeared_cb) + + def join(self, activity_ps): + Activity.join(self, activity_ps) + self._net_controller.init_stream(self._service) + self._ps.connect('activity-appeared', self._activity_appeared_cb) + self._activity_appeared_cb(self._ps, activity_ps) + + def _activity_appeared_cb(self, ps, activity): + # Only care about our own activity + if activity.get_id() != self.get_id(): + return + + # If we already have found our shared activity, do nothing + if self._ps_activity: + return + + self._ps_activity = activity + + # Connect signals to the shared activity so we are notified when + # buddies join and leave + self._ps_activity.connect('buddy-joined', self._add_buddy) + self._ps_activity.connect('buddy-left', self._remove_buddy) + + # Get the list of buddies already in this shared activity so we can + # connect to them + buddies = self._ps_activity.get_joined_buddies() + for buddy in buddies: + self._add_buddy(self._ps_activity, buddy) + + def _add_buddy(self, ps_activity, buddy): + service_type = self._ps_activity + self.emit('buddy-joined', ps_activity, buddy, self.get_default_type()) + + def _remove_buddy(self, ps_activity, buddy): + self.emit('buddy-left', ps_activity, buddy, self.get_default_type()) diff --git a/tests/test-snowflake-box.py b/tests/test-snowflake-box.py index 242ba42..d56993b 100755 --- a/tests/test-snowflake-box.py +++ b/tests/test-snowflake-box.py @@ -32,17 +32,17 @@ from sugar.graphics.iconcolor import IconColor from sugar.graphics.canvasicon import CanvasIcon def _create_snowflake(parent, children): - color = IconColor() - icon = CanvasIcon(size=40, color=color, - icon_name='activity-groupchat') - parent.append(icon, hippo.PACK_FIXED) - parent.set_root(icon) - - for i in range(0, children): - color = IconColor() - icon = CanvasIcon(size=60, color=color, - icon_name='stock-buddy') - parent.append(icon, hippo.PACK_FIXED) + color = IconColor() + icon = CanvasIcon(size=40, color=color, + icon_name='activity-groupchat') + parent.append(icon, hippo.PACK_FIXED) + parent.set_root(icon) + + for i in range(0, children): + color = IconColor() + icon = CanvasIcon(size=60, color=color, + icon_name='stock-buddy') + parent.append(icon, hippo.PACK_FIXED) window = gtk.Window() window.set_default_size(gtk.gdk.screen_width(), gtk.gdk.screen_height()) diff --git a/tests/test-spread-box.py b/tests/test-spread-box.py index a5da735..0f138cc 100755 --- a/tests/test-spread-box.py +++ b/tests/test-spread-box.py @@ -31,13 +31,13 @@ from sugar.graphics.iconcolor import IconColor from sugar.graphics.canvasicon import CanvasIcon def _create_icon(): - color = IconColor() + color = IconColor() - icon = CanvasIcon(size=100, color=color, - icon_name='stock-buddy') - box.add_item(icon) + icon = CanvasIcon(size=100, color=color, + icon_name='stock-buddy') + box.add_item(icon) - return (len(box.get_children()) < 20) + return (len(box.get_children()) < 20) window = gtk.Window() window.connect("destroy", lambda w: gtk.main_quit()) diff --git a/tests/test-window-manager.py b/tests/test-window-manager.py index 5a0274b..dff2b98 100644 --- a/tests/test-window-manager.py +++ b/tests/test-window-manager.py @@ -27,15 +27,15 @@ session.start() import gtk def _show_dialog(window): - dialog = gtk.Dialog(title='No Unviewed Media', - parent=window, flags=gtk.DIALOG_MODAL, - buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) - label = gtk.Label('There is no unviewed media to download.') - dialog.vbox.pack_start(label, True, True, 0) - label.show() - response = dialog.run() - dialog.hide() - del dialog + dialog = gtk.Dialog(title='No Unviewed Media', + parent=window, flags=gtk.DIALOG_MODAL, + buttons=(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) + label = gtk.Label('There is no unviewed media to download.') + dialog.vbox.pack_start(label, True, True, 0) + label.show() + response = dialog.run() + dialog.hide() + del dialog window = gtk.Window() window.show() -- cgit v0.9.1