/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* dbus-viewer.c Graphical D-Bus frontend utility * * Copyright (C) 2003 Red Hat, Inc. * * Licensed under the Academic Free License version 2.1 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include "dbus-tree-view.h" #include "dbus-names-model.h" #include #include #include #include static void show_error_dialog (GtkWindow *transient_parent, GtkWidget **weak_ptr, const char *message_format, ...) { char *message; va_list args; if (message_format) { va_start (args, message_format); message = g_strdup_vprintf (message_format, args); va_end (args); } else message = NULL; if (weak_ptr == NULL || *weak_ptr == NULL) { GtkWidget *dialog; dialog = gtk_message_dialog_new (transient_parent, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, message); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (gtk_widget_destroy), NULL); if (weak_ptr != NULL) { *weak_ptr = dialog; g_object_add_weak_pointer (G_OBJECT (dialog), (void**)weak_ptr); } gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE); gtk_widget_show_all (dialog); } else { g_return_if_fail (GTK_IS_MESSAGE_DIALOG (*weak_ptr)); gtk_label_set_text (GTK_LABEL (GTK_MESSAGE_DIALOG (*weak_ptr)->label), message); gtk_window_present (GTK_WINDOW (*weak_ptr)); } } typedef struct { DBusGConnection *connection; GtkWidget *window; GtkWidget *treeview; GtkWidget *name_menu; GtkTreeModel *names_model; GtkWidget *error_dialog; } TreeWindow; static void tree_window_set_node (TreeWindow *w, NodeInfo *node) { char **path; const char *name; name = node_info_get_name (node); if (name == NULL || name[0] != '/') { g_printerr (_("Assuming root node is at path /, since no absolute path is specified")); name = "/"; } path = _dbus_gutils_split_path (name); dbus_tree_view_update (GTK_TREE_VIEW (w->treeview), (const char**) path, node); g_strfreev (path); } typedef struct { DBusGConnection *connection; char *service_name; GError *error; NodeInfo *node; TreeWindow *window; /* Not touched from child thread */ } LoadFromServiceData; static gboolean load_child_nodes (const char *service_name, NodeInfo *parent, GString *path, GError **error) { DBusGConnection *connection; GSList *tmp; connection = dbus_g_bus_get (DBUS_BUS_SESSION, error); if (connection == NULL) return FALSE; tmp = node_info_get_nodes (parent); while (tmp != NULL) { DBusGProxy *proxy; char *data; NodeInfo *child; NodeInfo *complete_child; int save_len; complete_child = NULL; child = tmp->data; save_len = path->len; if (save_len > 1) g_string_append (path, "/"); g_string_append (path, base_info_get_name ((BaseInfo*)child)); if (*service_name == ':') { proxy = dbus_g_proxy_new_for_name (connection, service_name, path->str, DBUS_INTERFACE_INTROSPECTABLE); g_assert (proxy != NULL); } else { proxy = dbus_g_proxy_new_for_name_owner (connection, service_name, path->str, DBUS_INTERFACE_INTROSPECTABLE, error); if (proxy == NULL) goto done; } if (!dbus_g_proxy_call (proxy, "Introspect", error, G_TYPE_INVALID, G_TYPE_STRING, &data, G_TYPE_INVALID)) goto done; complete_child = description_load_from_string (data, -1, error); g_free (data); if (complete_child == NULL) { g_printerr ("%s\n", data); goto done; } done: g_object_unref (proxy); if (complete_child == NULL) return FALSE; /* change complete_child's name to relative */ base_info_set_name ((BaseInfo*)complete_child, base_info_get_name ((BaseInfo*)child)); /* Stitch in complete_child rather than child */ node_info_replace_node (parent, child, complete_child); node_info_unref (complete_child); /* ref still held by parent */ /* Now recurse */ if (!load_child_nodes (service_name, complete_child, path, error)) return FALSE; /* restore path */ g_string_set_size (path, save_len); tmp = tmp->next; } return TRUE; } static gboolean load_from_service_complete_idle (void *data) { /* Called in main thread */ GThread *thread = data; LoadFromServiceData *d; NodeInfo *node; d = g_thread_join (thread); node = d->node; if (d->error) { g_assert (d->node == NULL); show_error_dialog (GTK_WINDOW (d->window->window), &d->window->error_dialog, _("Unable to load \"%s\": %s\n"), d->service_name, d->error->message); g_error_free (d->error); } else { g_assert (d->error == NULL); tree_window_set_node (d->window, node); node_info_unref (node); } g_free (d->service_name); dbus_g_connection_unref (d->connection); g_free (d); return FALSE; } static void* load_from_service_thread_func (void *thread_data) { DBusGProxy *root_proxy; const char *data; NodeInfo *node; GString *path; LoadFromServiceData *lfsd; lfsd = thread_data; node = NULL; path = NULL; #if 1 /* this will end up autolaunching the service when we introspect it */ root_proxy = dbus_g_proxy_new_for_name (lfsd->connection, lfsd->service_name, "/", DBUS_INTERFACE_INTROSPECTABLE); g_assert (root_proxy != NULL); #else /* this will be an error if the service doesn't exist */ root_proxy = dbus_g_proxy_new_for_name_owner (lfsd->connection, lfsd->service_name, "/", DBUS_INTERFACE_INTROSPECTABLE, &lfsd->error); if (root_proxy == NULL) { g_printerr ("Failed to get owner of '%s'\n", lfsd->service_name); return lfsd->data; } #endif if (!dbus_g_proxy_call (root_proxy, "Introspect", &lfsd->error, G_TYPE_INVALID, G_TYPE_STRING, &data, G_TYPE_INVALID)) { g_printerr ("Failed to Introspect() %s\n", dbus_g_proxy_get_bus_name (root_proxy)); goto out; } node = description_load_from_string (data, -1, &lfsd->error); /* g_print ("%s\n", data); */ if (node == NULL) goto out; base_info_set_name ((BaseInfo*)node, "/"); path = g_string_new ("/"); if (!load_child_nodes (dbus_g_proxy_get_bus_name (root_proxy), node, path, &lfsd->error)) { node_info_unref (node); node = NULL; goto out; } out: g_object_unref (root_proxy); if (path) g_string_free (path, TRUE); lfsd->node = node; g_assert (lfsd->node || lfsd->error); g_assert (lfsd->node == NULL || lfsd->error == NULL); /* Add idle to main thread that will join us back */ g_idle_add (load_from_service_complete_idle, g_thread_self ()); return lfsd; } static void start_load_from_service (TreeWindow *w, DBusGConnection *connection, const char *service_name) { LoadFromServiceData *d; d = g_new0 (LoadFromServiceData, 1); d->connection = dbus_g_connection_ref (connection); d->service_name = g_strdup (service_name); d->error = NULL; d->node = NULL; d->window = w; g_thread_create (load_from_service_thread_func, d, TRUE, NULL); } static void tree_window_set_service (TreeWindow *w, const char *service_name) { start_load_from_service (w, w->connection, service_name); } static void name_combo_changed_callback (GtkComboBox *combo, TreeWindow *w) { GtkTreeIter iter; if (gtk_combo_box_get_active_iter (combo, &iter)) { GtkTreeModel *model; char *text; model = gtk_combo_box_get_model (combo); gtk_tree_model_get (model, &iter, 0, &text, -1); if (text) { tree_window_set_service (w, text); g_free (text); } } } static void window_closed_callback (GtkWidget *window, TreeWindow *w) { g_assert (window == w->window); w->window = NULL; gtk_main_quit (); } static TreeWindow* tree_window_new (DBusGConnection *connection, GtkTreeModel *names_model) { TreeWindow *w; GtkWidget *sw; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *combo; /* Should use glade, blah */ w = g_new0 (TreeWindow, 1); w->window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (w->window), "D-Bus Viewer"); gtk_window_set_default_size (GTK_WINDOW (w->window), 400, 500); g_signal_connect (w->window, "destroy", G_CALLBACK (window_closed_callback), w); gtk_container_set_border_width (GTK_CONTAINER (w->window), 6); vbox = gtk_vbox_new (FALSE, 6); gtk_container_add (GTK_CONTAINER (w->window), vbox); /* Create names option menu */ if (connection) { GtkCellRenderer *cell; w->connection = connection; w->names_model = names_model; combo = gtk_combo_box_new_with_model (w->names_model); cell = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE); gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell, "text", 0, NULL); gtk_box_pack_start (GTK_BOX (vbox), combo, FALSE, FALSE, 0); g_signal_connect (combo, "changed", G_CALLBACK (name_combo_changed_callback), w); } /* Create tree view */ hbox = gtk_hbox_new (FALSE, 6); gtk_container_add (GTK_CONTAINER (vbox), hbox); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start (GTK_BOX (hbox), sw, TRUE, TRUE, 0); w->treeview = dbus_tree_view_new (); gtk_container_add (GTK_CONTAINER (sw), w->treeview); /* Show everything */ gtk_widget_show_all (w->window); return w; } static void usage (int ecode) { fprintf (stderr, "dbus-viewer [--version] [--help]\n"); exit (ecode); } static void version (void) { printf ("D-Bus Message Bus Viewer %s\n" "Copyright (C) 2003 Red Hat, Inc.\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n", VERSION); exit (0); } int main (int argc, char **argv) { int i; GSList *files; gboolean end_of_args; GSList *tmp; gboolean services; DBusGConnection *connection; GError *error; GtkTreeModel *names_model; g_thread_init (NULL); dbus_g_thread_init (); bindtextdomain (GETTEXT_PACKAGE, DBUS_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); gtk_init (&argc, &argv); services = FALSE; end_of_args = FALSE; files = NULL; i = 1; while (i < argc) { const char *arg = argv[i]; if (!end_of_args) { if (strcmp (arg, "--help") == 0 || strcmp (arg, "-h") == 0 || strcmp (arg, "-?") == 0) usage (0); else if (strcmp (arg, "--version") == 0) version (); else if (strcmp (arg, "--services") == 0) services = TRUE; else if (arg[0] == '-' && arg[1] == '-' && arg[2] == '\0') end_of_args = TRUE; else if (arg[0] == '-') { usage (1); } else { files = g_slist_prepend (files, (char*) arg); } } else files = g_slist_prepend (files, (char*) arg); ++i; } if (services || files == NULL) { error = NULL; connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); if (connection == NULL) { g_printerr ("Could not open bus connection: %s\n", error->message); g_error_free (error); exit (1); } g_assert (connection == dbus_g_bus_get (DBUS_BUS_SESSION, NULL)); names_model = names_model_new (connection); } else { connection = NULL; names_model = NULL; } if (files == NULL) { TreeWindow *w; w = tree_window_new (connection, names_model); } files = g_slist_reverse (files); tmp = files; while (tmp != NULL) { const char *filename; TreeWindow *w; filename = tmp->data; if (services) { w = tree_window_new (connection, names_model); tree_window_set_service (w, filename); } else { NodeInfo *node; error = NULL; node = description_load_from_file (filename, &error); if (node == NULL) { g_assert (error != NULL); show_error_dialog (NULL, NULL, _("Unable to load \"%s\": %s\n"), filename, error->message); g_error_free (error); } else { w = tree_window_new (connection, names_model); tree_window_set_node (w, node); node_info_unref (node); } } tmp = tmp->next; } gtk_main (); return 0; }