#include #include #ifdef G_OS_UNIX #include /* For STDOUT_FILENO */ #include #endif #define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties" /* ---------------------------------------------------------------------------------------------------- */ static GDBusNodeInfo *introspection_data = NULL; /* Introspection data for the service we are exporting */ static const gchar introspection_xml[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* ---------------------------------------------------------------------------------------------------- */ static void handle_method_call (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data) { if (g_strcmp0 (method_name, "HelloWorld") == 0) { const gchar *greeting; g_variant_get (parameters, "(&s)", &greeting); if (g_strcmp0 (greeting, "Return Unregistered") == 0) { g_dbus_method_invocation_return_error (invocation, G_IO_ERROR, G_IO_ERROR_FAILED_HANDLED, "As requested, here's a GError not registered (G_IO_ERROR_FAILED_HANDLED)"); } else if (g_strcmp0 (greeting, "Return Registered") == 0) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_MATCH_RULE_NOT_FOUND, "As requested, here's a GError that is registered (G_DBUS_ERROR_MATCH_RULE_NOT_FOUND)"); } else if (g_strcmp0 (greeting, "Return Raw") == 0) { g_dbus_method_invocation_return_dbus_error (invocation, "org.gtk.GDBus.SomeErrorName", "As requested, here's a raw D-Bus error"); } else { gchar *response; response = g_strdup_printf ("You greeted me with '%s'. Thanks!", greeting); g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", response)); g_free (response); } } else if (g_strcmp0 (method_name, "EmitSignal") == 0) { GError *local_error; gdouble speed_in_mph; gchar *speed_as_string; g_variant_get (parameters, "(d)", &speed_in_mph); speed_as_string = g_strdup_printf ("%g mph!", speed_in_mph); local_error = NULL; g_dbus_connection_emit_signal (connection, NULL, object_path, interface_name, "VelocityChanged", g_variant_new ("(ds)", speed_in_mph, speed_as_string), &local_error); g_assert_no_error (local_error); g_free (speed_as_string); g_dbus_method_invocation_return_value (invocation, NULL); } else if (g_strcmp0 (method_name, "GimmeStdout") == 0) { #ifdef G_OS_UNIX if (g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING) { GDBusMessage *reply; GUnixFDList *fd_list; GError *error; fd_list = g_unix_fd_list_new (); error = NULL; g_unix_fd_list_append (fd_list, STDOUT_FILENO, &error); g_assert_no_error (error); reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation)); g_dbus_message_set_unix_fd_list (reply, fd_list); error = NULL; g_dbus_connection_send_message (connection, reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, /* out_serial */ &error); g_assert_no_error (error); g_object_unref (invocation); g_object_unref (fd_list); g_object_unref (reply); } else { g_dbus_method_invocation_return_dbus_error (invocation, "org.gtk.GDBus.Failed", "Your message bus daemon does not support file descriptor passing (need D-Bus >= 1.3.0)"); } #else g_dbus_method_invocation_return_dbus_error (invocation, "org.gtk.GDBus.NotOnUnix", "Your OS does not support file descriptor passing"); #endif } } static gchar *_global_title = NULL; static gboolean swap_a_and_b = FALSE; static GVariant * handle_get_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data) { GVariant *ret; ret = NULL; if (g_strcmp0 (property_name, "FluxCapicitorName") == 0) { ret = g_variant_new_string ("DeLorean"); } else if (g_strcmp0 (property_name, "Title") == 0) { if (_global_title == NULL) _global_title = g_strdup ("Back To C!"); ret = g_variant_new_string (_global_title); } else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Hello %s. I thought I said reading this property " "always results in an error. kthxbye", sender); } else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0) { ret = g_variant_new_string ("There's no home like home"); } else if (g_strcmp0 (property_name, "Foo") == 0) { ret = g_variant_new_string (swap_a_and_b ? "Tock" : "Tick"); } else if (g_strcmp0 (property_name, "Bar") == 0) { ret = g_variant_new_string (swap_a_and_b ? "Tick" : "Tock"); } return ret; } static gboolean handle_set_property (GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, gpointer user_data) { if (g_strcmp0 (property_name, "Title") == 0) { if (g_strcmp0 (_global_title, g_variant_get_string (value, NULL)) != 0) { GVariantBuilder *builder; GError *local_error; g_free (_global_title); _global_title = g_variant_dup_string (value, NULL); local_error = NULL; builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); g_variant_builder_add (builder, "{sv}", "Title", g_variant_new_string (_global_title)); g_dbus_connection_emit_signal (connection, NULL, object_path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", g_variant_new ("(sa{sv}as)", interface_name, builder, NULL), &local_error); g_assert_no_error (local_error); } } else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0) { /* do nothing - they can't read it after all! */ } else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Hello AGAIN %s. I thought I said writing this property " "always results in an error. kthxbye", sender); } return *error == NULL; } /* for now */ static const GDBusInterfaceVTable interface_vtable = { handle_method_call, handle_get_property, handle_set_property, { 0 } }; /* ---------------------------------------------------------------------------------------------------- */ static gboolean on_timeout_cb (gpointer user_data) { GDBusConnection *connection = G_DBUS_CONNECTION (user_data); GVariantBuilder *builder; GVariantBuilder *invalidated_builder; GError *error; swap_a_and_b = !swap_a_and_b; error = NULL; builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); g_variant_builder_add (builder, "{sv}", "Foo", g_variant_new_string (swap_a_and_b ? "Tock" : "Tick")); g_variant_builder_add (builder, "{sv}", "Bar", g_variant_new_string (swap_a_and_b ? "Tick" : "Tock")); g_dbus_connection_emit_signal (connection, NULL, "/org/gtk/GDBus/TestObject", DBUS_INTERFACE_PROPERTIES, "PropertiesChanged", g_variant_new ("(sa{sv}as)", "org.gtk.GDBus.TestInterface", builder, invalidated_builder), &error); g_assert_no_error (error); return G_SOURCE_CONTINUE; } /* ---------------------------------------------------------------------------------------------------- */ static void on_bus_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { guint registration_id; registration_id = g_dbus_connection_register_object (connection, "/org/gtk/GDBus/TestObject", introspection_data->interfaces[0], &interface_vtable, NULL, /* user_data */ NULL, /* user_data_free_func */ NULL); /* GError** */ g_assert (registration_id > 0); /* swap value of properties Foo and Bar every two seconds */ g_timeout_add_seconds (2, on_timeout_cb, connection); } static void on_name_acquired (GDBusConnection *connection, const gchar *name, gpointer user_data) { } static void on_name_lost (GDBusConnection *connection, const gchar *name, gpointer user_data) { exit (1); } int main (int argc, char *argv[]) { guint owner_id; GMainLoop *loop; /* We are lazy here - we don't want to manually provide * the introspection data structures - so we just build * them from XML. */ introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL); g_assert (introspection_data != NULL); owner_id = g_bus_own_name (G_BUS_TYPE_SESSION, "org.gtk.GDBus.TestServer", G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); loop = g_main_loop_new (NULL, FALSE); g_main_loop_run (loop); g_bus_unown_name (owner_id); g_dbus_node_info_unref (introspection_data); return 0; }