#!/usr/bin/env python import dbus import xml.etree.ElementTree as ElementTree from efl import evas from efl import elementary as elm from efl.elementary.window import StandardWindow from efl.elementary.background import Background from efl.elementary.box import Box from efl.elementary.button import Button from efl.elementary.flipselector import FlipSelector from efl.elementary.panes import Panes from efl.elementary.genlist import Genlist, GenlistItem, GenlistItemClass from efl.dbus_mainloop import DBusEcoreMainLoop ### connect to session and system buses, and set session as the current one session_bus = dbus.SessionBus(mainloop=DBusEcoreMainLoop()) system_bus = dbus.SystemBus(mainloop=DBusEcoreMainLoop()) bus = session_bus ### Classes to describe various DBus nodes class DBusNode(object): """base object for the others DBus nodes""" def __init__(self, name, parent): self._name = name self._parent = parent @property def name(self): return self._name @property def parent(self): return self._parent class DBusObject(DBusNode): """object to represent a DBus Object """ def __init__(self, name, parent_service): DBusNode.__init__(self, name, parent_service) self._interfaces = [] @property def interfaces(self): return self._interfaces class DBusInterface(DBusNode): """object to represent a DBus Interface""" def __init__(self, name, parent_obj): DBusNode.__init__(self, name, parent_obj) self._properties = [] self._methods = [] self._signals = [] parent_obj.interfaces.append(self) @property def properties(self): return self._properties @property def methods(self): return self._methods @property def signals(self): return self._signals class DBusProperty(DBusNode): """object to represent a DBus Property""" def __init__(self, name, parent_iface): DBusNode.__init__(self, name, parent_iface) parent_iface.properties.append(self) class DBusMethod(DBusNode): """object to represent a DBus Method""" def __init__(self, name, parent_iface): DBusNode.__init__(self, name, parent_iface) parent_iface.methods.append(self) class DBusSignal(DBusNode): """object to represent a DBus Signal""" def __init__(self, name, parent_iface): DBusNode.__init__(self, name, parent_iface) parent_iface.signals.append(self) ### Introspect a named service and return a list of DBusObjects def recursive_introspect(bus, named_service, object_path, ret_data=None): # first recursion, create an empty list if ret_data is None: ret_data = [] # parse the xml string from the Introspectable interface obj = bus.get_object(named_service, object_path) iface = dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable') xml_data = iface.Introspect() xml_root = ElementTree.fromstring(xml_data) # debug print('=' * 80) print("Introspecting path:'%s' on service:'%s'" % (object_path, named_service)) print('=' * 80) print(xml_data) print('=' * 80) # traverse the xml tree if xml_root.find('interface'): # found a new object obj = DBusObject(object_path, named_service) ret_data.append(obj) for xml_node in xml_root: # found an interface if xml_node.tag == 'interface': iface = DBusInterface(xml_node.attrib['name'], obj) for child in xml_node: if child.tag == 'property': prop = DBusProperty(child.attrib['name'], iface) if child.tag == 'method': meth = DBusMethod(child.attrib['name'], iface) if child.tag == 'signal': sig = DBusSignal(child.attrib['name'], iface) # found another node, introspect it... if xml_node.tag == 'node': if object_path == '/': object_path = '' new_path = '/'.join((object_path, xml_node.attrib['name'])) recursive_introspect(bus, named_service, new_path, ret_data) return ret_data ### Names genlist (the one on the left) class NamesListGroupItemClass(GenlistItemClass): def __init__(self): GenlistItemClass.__init__(self, item_style="group_index") def text_get(self, gl, part, name): return name class NamesListItemClass(GenlistItemClass): def __init__(self): GenlistItemClass.__init__(self, item_style="default") def text_get(self, gl, part, name): return name class NamesList(Genlist): def __init__(self, parent): self.win = parent self.sig1 = None # create the genlist Genlist.__init__(self, parent) self.itc = NamesListItemClass() self.itc_g = NamesListGroupItemClass() self.callback_selected_add(self.item_selected_cb) # add private & public group items self.public_group = self.item_append(self.itc_g, "Public Services", flags=elm.ELM_GENLIST_ITEM_GROUP) self.public_group.select_mode_set(elm.ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY) self.private_group = self.item_append(self.itc_g, "Private Services", flags=elm.ELM_GENLIST_ITEM_GROUP) self.private_group.select_mode_set(elm.ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY) # populate the genlist self.populate() def populate(self): for name in bus.list_names(): self.service_add(name) # keep the list updated when a name changes if self.sig1: self.sig1.remove() self.sig1 = bus.add_signal_receiver(self.name_owner_changed_cb, "NameOwnerChanged") # bus.add_signal_receiver(self.name_acquired_cb, "NameAcquired") # bus.add_signal_receiver(self.name_lost_cb, "NameLost") def clear(self): self.public_group.subitems_clear() self.private_group.subitems_clear() def item_selected_cb(self, gl, item): name = item.data self.win.detail_list.populate(name) def sort_cb(self, it1, it2): return 1 if it1.data.lower() < it2.data.lower() else -1 def service_add(self, name): print("service_add('%s')" % name) if name.startswith(":"): self.item_sorted_insert(self.itc, name, self.sort_cb, self.private_group, 0, None) else: self.item_sorted_insert(self.itc, name, self.sort_cb, self.public_group, 0, None) def service_del(self, name): print("service_del('%s')" % name) item = self.first_item while item: if item.data == name: item.delete() return item = item.next def name_owner_changed_cb(self, name, old_owner, new_owner): print("NameOwnerChanged(name='%s', old_owner='%s', new_owner='%s')" % (name, old_owner, new_owner)) if old_owner == '': self.service_add(name) elif new_owner == '': self.service_del(name) ### Detail genlist (the one on the right) class ObjectItemClass(GenlistItemClass): def __init__(self): GenlistItemClass.__init__(self, item_style="group_index") def text_get(self, gl, part, obj): return '[OBJ] ' + obj.name class NodeItemClass(GenlistItemClass): def __init__(self): GenlistItemClass.__init__(self, item_style="default") def text_get(self, gl, part, obj): if isinstance(obj, DBusInterface): return '[IFACE] ' + obj.name if isinstance(obj, DBusProperty): return '[PROP] ' + obj.name if isinstance(obj, DBusMethod): return '[METH] ' + obj.name + '()' if isinstance(obj, DBusSignal): return '[SIG] ' + obj.name class DetailList(Genlist): def __init__(self, parent): Genlist.__init__(self, parent) self.itc = NodeItemClass() self.itc_g = ObjectItemClass() self.callback_expand_request_add(self.expand_request_cb) self.callback_expanded_add(self.expanded_cb) self.callback_contract_request_add(self.contract_request_cb) self.callback_contracted_add(self.contracted_cb) def populate(self, name): print("populate: %s" % name) self.clear() # objects for obj in recursive_introspect(bus, name, '/'): obj_item = self.item_append(self.itc_g, obj, flags=elm.ELM_GENLIST_ITEM_GROUP) obj_item.select_mode_set(elm.ELM_OBJECT_SELECT_MODE_DISPLAY_ONLY) # interfaces for iface in obj.interfaces: iface_item = self.item_append(self.itc, iface, parent_item=obj_item, flags=elm.ELM_GENLIST_ITEM_TREE) def expand_request_cb(self, genlist, item): item.expanded = True def expanded_cb(self, genlist, item): iface = item.data for prop in iface.properties: self.item_append(self.itc, prop, parent_item=item) for method in iface.methods: self.item_append(self.itc, method, parent_item=item) for signal in iface.signals: self.item_append(self.itc, signal, parent_item=item) def contract_request_cb(self, genlist, item): item.expanded = False def contracted_cb(self, genlist, item): item.subitems_clear() ### The main window class MyWin(StandardWindow): def __init__(self): StandardWindow.__init__(self, "efl-dbus-spy", "EFL DBus Spy - Espionage") self.autodel_set(True) self.callback_delete_request_add(lambda o: elm.exit()) bg = Background(self) self.resize_object_add(bg) bg.size_hint_weight_set(evas.EVAS_HINT_EXPAND, evas.EVAS_HINT_EXPAND) bg.show() box = Box(self) self.resize_object_add(box) box.size_hint_weight_set(evas.EVAS_HINT_EXPAND, evas.EVAS_HINT_EXPAND) box.show() flip = FlipSelector(self) flip.item_append("Session Bus", self.flip_selected_cb, session_bus) flip.item_append("System Bus", self.flip_selected_cb, system_bus) box.pack_end(flip) flip.show() panes = Panes(self) panes.size_hint_weight = (evas.EVAS_HINT_EXPAND, evas.EVAS_HINT_EXPAND) panes.size_hint_align = (evas.EVAS_HINT_FILL, evas.EVAS_HINT_FILL) # panes.content_left_size = 0.333 # panes.fixed = True box.pack_end(panes) panes.show() self.names_list = NamesList(self) panes.part_content_set("left", self.names_list) self.names_list.show() self.detail_list = DetailList(self) panes.part_content_set("right", self.detail_list) self.detail_list.show() self.resize(700, 500) self.show() def flip_selected_cb(self, flipselector, item, selected_bus): global bus bus = selected_bus self.detail_list.clear() self.names_list.clear() self.names_list.populate() if __name__ == "__main__": elm.init() win = MyWin() elm.run() elm.shutdown()