2013-04-07 06:18:32 -07:00
|
|
|
#!/usr/bin/env python
|
|
|
|
|
|
|
|
import dbus
|
2013-04-13 02:28:18 -07:00
|
|
|
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
|
2013-04-07 06:18:32 -07:00
|
|
|
from efl.dbus_mainloop import DBusEcoreMainLoop
|
|
|
|
|
|
|
|
|
2013-04-13 02:28:18 -07:00
|
|
|
### 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()
|
2013-04-07 06:18:32 -07:00
|
|
|
|
2013-04-13 02:28:18 -07:00
|
|
|
def flip_selected_cb(self, flipselector, item, selected_bus):
|
|
|
|
global bus
|
2013-04-07 06:18:32 -07:00
|
|
|
|
2013-04-13 02:28:18 -07:00
|
|
|
bus = selected_bus
|
|
|
|
self.detail_list.clear()
|
|
|
|
self.names_list.clear()
|
|
|
|
self.names_list.populate()
|
2013-04-07 06:18:32 -07:00
|
|
|
|
|
|
|
|
2013-04-13 02:28:18 -07:00
|
|
|
if __name__ == "__main__":
|
|
|
|
elm.init()
|
|
|
|
win = MyWin()
|
|
|
|
elm.run()
|
|
|
|
elm.shutdown()
|
2013-04-07 06:18:32 -07:00
|
|
|
|