#!/usr/bin/env ruby # frozen_string_literal: true # # 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 = intf @meth = meth @entries = [] @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.send_sync_or_async(m) do |retm| if retm.is_a?(DBus::Error) puts "Error: #{retm.inspect}" else puts "Method #{m.member} returns: #{retm.params.inspect}" end end 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) @methsigtreeview.signal_connect("row-activated") do |view, path, column| on_method_activated(view, path, column) end @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}(" case meth when DBus::Method s += (meth.params.collect { |a| "in #{a[0]}:#{a[1]}" } + meth.rets.collect { |a| "out #{a[0]}:#{a[1]}" }).join(", ") when 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 return unless selected intf = selected[1] return unless intf 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 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] case type when "M" method = view.model.get_iter(path)[2] intf = view.model.get_iter(path)[3] MethodCallWindow.new(@window, intf, method).run when "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} ***" 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}" 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 return unless ENABLE_SYSTEM DBus::SystemBus.instance.proxy.ListNames do |_msg, names| fill_treeview(DBus::SystemBus.instance, @systemtreeview, names) 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 return if node.object.nil? node.object.interfaces.sort.each do |ifname| subiter = model.append(iter) subiter[0] = ifname subiter[1] = node.object[ifname] end end def introspect_services(model, bus) el = @introspect_array.shift if 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 !@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