#!/usr/bin/env ruby # # This is a quite complex example using internal lower level API. # Not a good starting point, but might be usefull if you want to do tricky # stuff. # -- Arnaud require 'dbus' require "gtk2" $enable_system = false class MethodCallWindow def initialize(pwindow, intf, meth) @intf, @meth = intf, meth @entries = Array.new @dialog = Gtk::Dialog.new(meth.name, pwindow, Gtk::Dialog::MODAL | Gtk::Dialog::NO_SEPARATOR, [Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK], [Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL]) @meth.params.each do |param| shbox = Gtk::HBox.new(true, 0) label = Gtk::Label.new("#{param[0]} (#{param[1]})") input = Gtk::Entry.new @entries << input shbox.pack_start(label, true, true, 0) shbox.pack_start(input, true, true, 0) @dialog.vbox.pack_start(shbox, true, true, 0) @dialog.vbox.show_all end end def run on_ok if @dialog.run == Gtk::Dialog::RESPONSE_OK @dialog.destroy end def on_ok bus = @intf.object.bus m = DBus::Message.new(DBus::Message::METHOD_CALL) m.path = @intf.object.path m.interface = @intf.name m.destination = @intf.object.destination m.member = @meth.name m.sender = bus.unique_name @meth.params.each_with_index do |param, idx| entry = @entries[idx] data = nil case param[1] when "u", "i" data = entry.text.to_i when "s" data = entry.text when /^a/ begin data = eval(entry.text) rescue puts "Incorrect data: #{data}" end end m.add_param(param[1], data) end bus.on_return(m) do |retm| if retm.is_a?(DBus::Error) puts "Error: #{retm.inspect}" else puts "Method #{m.member} returns: #{retm.params.inspect}" end end bus.send(m.marshall) end end class DBusUI def initialize @glade = Gtk::Builder.new @glade << "gdbus.glade" @sessiontreeview = @glade.get_object("sessiontreeview") setup_treeview_renderer(@sessiontreeview, 'D-Bus Objects') @sessiontreeview.selection.signal_connect("changed") do |selection| on_treeview_selection_changed(selection) end @systemtreeview = @glade.get_object("systemtreeview") setup_treeview_renderer(@systemtreeview, 'D-Bus Objects') @systemtreeview.selection.signal_connect("changed") do |selection| on_treeview_selection_changed(selection) end @methsigtreeview = @glade.get_object("methsigtreeview") # ierk setup_methodview_renderer(@methsigtreeview) @window = @glade.get_object("window1") @window.show_all start_buses end def beautify_method(meth) # Damn, this need to be rewritten :p s = meth.name + "(" if meth.kind_of?(DBus::Method) s += (meth.params.collect { |a| "in #{a[0]}:#{a[1]}" } + meth.rets.collect { |a| "out #{a[0]}:#{a[1]}" }).join(", ") elsif meth.kind_of?(DBus::Signal) s += (meth.params.collect { |a| "in #{a[0]}:#{a[1]}" }).join(", ") end s += ")" s end def on_treeview_selection_changed(selection) selected = selection.selected model = Gtk::ListStore.new(String, String, DBus::Method, DBus::ProxyObjectInterface) @methsigtreeview.model = model if selected if intf = selected[1] intf.methods.keys.sort.each do |mi| m = intf.methods[mi] subiter = model.append subiter[0] = beautify_method(m) subiter[1] = "M" subiter[2] = m subiter[3] = intf end intf.signals.keys.sort.each do |mi| m = intf.signals[mi] subiter = model.append subiter[0] = beautify_method(m) subiter[1] = "S" subiter[2] = m subiter[3] = intf end end end end def on_method_activated(view, path, column) name = view.model.get_iter(path)[0] puts "Clicked on: #{name.inspect}" type = view.model.get_iter(path)[1] intf = view.model.get_iter(path)[2] if type == "M" method = view.model.get_iter(path)[2] intf = view.model.get_iter(path)[3] MethodCallWindow.new(@window, intf, method).run elsif type == "S" signal = view.model.get_iter(path)[2] intf = view.model.get_iter(path)[3] mr = DBus::MatchRule.new.from_signal(intf, signal) puts "*** Registering matchrule: #{mr.to_s} ***" intf.object.bus.add_match(mr) do |sig| puts "Got #{sig.member}(#{sig.params.join(',')})" end end end def on_sessiontreeview_row_activated(view, path, column) name = view.model.get_iter(path)[0] puts "Clicked on: #{name.inspect}" intf = view.model.get_iter(path)[1] end def on_window_delete_event(window, event) Gtk.main_quit end def setup_methodview_renderer(treeview) renderer = Gtk::CellRendererText.new col_offset = treeview.insert_column(-1, "T", renderer, 'text' => 1) col_offset = treeview.insert_column(-1, "Name", renderer, 'text' => 0) column = treeview.get_column(col_offset - 1) column.clickable = true end def setup_treeview_renderer(treeview, str) renderer = Gtk::CellRendererText.new col_offset = treeview.insert_column(-1, str, renderer, 'text' => 0) column = treeview.get_column(col_offset - 1) column.clickable = true end def process_input(bus) # THIS is the bad ass loop # we should return to the glib main loop from time to time. Anyone with a # proper way to handle it ? bus.update_buffer bus.messages.each do |msg| bus.process(msg) end end def start_buses # call glibize to get dbus messages from the glib mainloop DBus::SessionBus.instance.glibize DBus::SystemBus.instance.glibize if $enable_system DBus::SessionBus.instance.proxy.ListNames do |msg, names| fill_treeview(DBus::SessionBus.instance, @sessiontreeview, names) end if $enable_system DBus::SystemBus.instance.proxy.ListNames do |msg, names| fill_treeview(DBus::SystemBus.instance, @systemtreeview, names) end end end def walk_node(model, iter, node) node.each_pair do |key, val| subiter = model.append(iter) subiter[0] = key walk_node(model, subiter, val) end unless node.object.nil? node.object.interfaces.sort.each do |ifname| subiter = model.append(iter) subiter[0] = ifname subiter[1] = node.object[ifname] end end end def introspect_services(model, bus) el = @introspect_array.shift if not el =~ /^:/ iter = model.append(nil) iter[0] = el puts "introspecting: #{el}" begin service = bus.service(el).introspect walk_node(model, iter, service.root) rescue Exception => e puts "DBus Error:" puts e.backtrace.join("\n") end end not @introspect_array.empty? end def fill_treeview(bus, treeview, array) model = Gtk::TreeStore.new(String, DBus::ProxyObjectInterface) treeview.model = model @introspect_array = array.sort Gtk::idle_add { introspect_services(model, bus) } end def main Gtk.main end end DBusUI.new.main