Branch data Line data Source code
1 : : #include <gio/gio.h>
2 : : #include <stdlib.h>
3 : :
4 : : #ifdef G_OS_UNIX
5 : : #include <gio/gunixfdlist.h>
6 : : /* For STDOUT_FILENO */
7 : : #include <unistd.h>
8 : : #endif
9 : :
10 : : #define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
11 : :
12 : : /* ---------------------------------------------------------------------------------------------------- */
13 : :
14 : : static GDBusNodeInfo *introspection_data = NULL;
15 : :
16 : : /* Introspection data for the service we are exporting */
17 : : static const gchar introspection_xml[] =
18 : : "<node>"
19 : : " <interface name='org.gtk.GDBus.TestInterface'>"
20 : : " <annotation name='org.gtk.GDBus.Annotation' value='OnInterface'/>"
21 : : " <annotation name='org.gtk.GDBus.Annotation' value='AlsoOnInterface'/>"
22 : : " <method name='HelloWorld'>"
23 : : " <annotation name='org.gtk.GDBus.Annotation' value='OnMethod'/>"
24 : : " <arg type='s' name='greeting' direction='in'/>"
25 : : " <arg type='s' name='response' direction='out'/>"
26 : : " </method>"
27 : : " <method name='EmitSignal'>"
28 : : " <arg type='d' name='speed_in_mph' direction='in'>"
29 : : " <annotation name='org.gtk.GDBus.Annotation' value='OnArg'/>"
30 : : " </arg>"
31 : : " </method>"
32 : : " <method name='GimmeStdout'/>"
33 : : " <signal name='VelocityChanged'>"
34 : : " <annotation name='org.gtk.GDBus.Annotation' value='Onsignal'/>"
35 : : " <arg type='d' name='speed_in_mph'/>"
36 : : " <arg type='s' name='speed_as_string'>"
37 : : " <annotation name='org.gtk.GDBus.Annotation' value='OnArg_NonFirst'/>"
38 : : " </arg>"
39 : : " </signal>"
40 : : " <property type='s' name='FluxCapicitorName' access='read'>"
41 : : " <annotation name='org.gtk.GDBus.Annotation' value='OnProperty'>"
42 : : " <annotation name='org.gtk.GDBus.Annotation' value='OnAnnotation_YesThisIsCrazy'/>"
43 : : " </annotation>"
44 : : " </property>"
45 : : " <property type='s' name='Title' access='readwrite'/>"
46 : : " <property type='s' name='ReadingAlwaysThrowsError' access='read'/>"
47 : : " <property type='s' name='WritingAlwaysThrowsError' access='readwrite'/>"
48 : : " <property type='s' name='OnlyWritable' access='write'/>"
49 : : " <property type='s' name='Foo' access='read'/>"
50 : : " <property type='s' name='Bar' access='read'/>"
51 : : " </interface>"
52 : : "</node>";
53 : :
54 : : /* ---------------------------------------------------------------------------------------------------- */
55 : :
56 : : static void
57 : 0 : handle_method_call (GDBusConnection *connection,
58 : : const gchar *sender,
59 : : const gchar *object_path,
60 : : const gchar *interface_name,
61 : : const gchar *method_name,
62 : : GVariant *parameters,
63 : : GDBusMethodInvocation *invocation,
64 : : gpointer user_data)
65 : : {
66 : 0 : if (g_strcmp0 (method_name, "HelloWorld") == 0)
67 : : {
68 : : const gchar *greeting;
69 : :
70 : 0 : g_variant_get (parameters, "(&s)", &greeting);
71 : :
72 : 0 : if (g_strcmp0 (greeting, "Return Unregistered") == 0)
73 : : {
74 : 0 : g_dbus_method_invocation_return_error (invocation,
75 : : G_IO_ERROR,
76 : : G_IO_ERROR_FAILED_HANDLED,
77 : : "As requested, here's a GError not registered (G_IO_ERROR_FAILED_HANDLED)");
78 : : }
79 : 0 : else if (g_strcmp0 (greeting, "Return Registered") == 0)
80 : : {
81 : 0 : g_dbus_method_invocation_return_error (invocation,
82 : : G_DBUS_ERROR,
83 : : G_DBUS_ERROR_MATCH_RULE_NOT_FOUND,
84 : : "As requested, here's a GError that is registered (G_DBUS_ERROR_MATCH_RULE_NOT_FOUND)");
85 : : }
86 : 0 : else if (g_strcmp0 (greeting, "Return Raw") == 0)
87 : : {
88 : 0 : g_dbus_method_invocation_return_dbus_error (invocation,
89 : : "org.gtk.GDBus.SomeErrorName",
90 : : "As requested, here's a raw D-Bus error");
91 : : }
92 : : else
93 : : {
94 : : gchar *response;
95 : 0 : response = g_strdup_printf ("You greeted me with '%s'. Thanks!", greeting);
96 : 0 : g_dbus_method_invocation_return_value (invocation,
97 : : g_variant_new ("(s)", response));
98 : 0 : g_free (response);
99 : : }
100 : : }
101 : 0 : else if (g_strcmp0 (method_name, "EmitSignal") == 0)
102 : : {
103 : : GError *local_error;
104 : : gdouble speed_in_mph;
105 : : gchar *speed_as_string;
106 : :
107 : 0 : g_variant_get (parameters, "(d)", &speed_in_mph);
108 : 0 : speed_as_string = g_strdup_printf ("%g mph!", speed_in_mph);
109 : :
110 : 0 : local_error = NULL;
111 : 0 : g_dbus_connection_emit_signal (connection,
112 : : NULL,
113 : : object_path,
114 : : interface_name,
115 : : "VelocityChanged",
116 : : g_variant_new ("(ds)",
117 : : speed_in_mph,
118 : : speed_as_string),
119 : : &local_error);
120 : 0 : g_assert_no_error (local_error);
121 : 0 : g_free (speed_as_string);
122 : :
123 : 0 : g_dbus_method_invocation_return_value (invocation, NULL);
124 : : }
125 : 0 : else if (g_strcmp0 (method_name, "GimmeStdout") == 0)
126 : : {
127 : : #ifdef G_OS_UNIX
128 : 0 : if (g_dbus_connection_get_capabilities (connection) & G_DBUS_CAPABILITY_FLAGS_UNIX_FD_PASSING)
129 : : {
130 : : GDBusMessage *reply;
131 : : GUnixFDList *fd_list;
132 : : GError *error;
133 : :
134 : 0 : fd_list = g_unix_fd_list_new ();
135 : 0 : error = NULL;
136 : 0 : g_unix_fd_list_append (fd_list, STDOUT_FILENO, &error);
137 : 0 : g_assert_no_error (error);
138 : :
139 : 0 : reply = g_dbus_message_new_method_reply (g_dbus_method_invocation_get_message (invocation));
140 : 0 : g_dbus_message_set_unix_fd_list (reply, fd_list);
141 : :
142 : 0 : error = NULL;
143 : 0 : g_dbus_connection_send_message (connection,
144 : : reply,
145 : : G_DBUS_SEND_MESSAGE_FLAGS_NONE,
146 : : NULL, /* out_serial */
147 : : &error);
148 : 0 : g_assert_no_error (error);
149 : :
150 : 0 : g_object_unref (invocation);
151 : 0 : g_object_unref (fd_list);
152 : 0 : g_object_unref (reply);
153 : : }
154 : : else
155 : : {
156 : 0 : g_dbus_method_invocation_return_dbus_error (invocation,
157 : : "org.gtk.GDBus.Failed",
158 : : "Your message bus daemon does not support file descriptor passing (need D-Bus >= 1.3.0)");
159 : : }
160 : : #else
161 : : g_dbus_method_invocation_return_dbus_error (invocation,
162 : : "org.gtk.GDBus.NotOnUnix",
163 : : "Your OS does not support file descriptor passing");
164 : : #endif
165 : : }
166 : 0 : }
167 : :
168 : : static gchar *_global_title = NULL;
169 : :
170 : : static gboolean swap_a_and_b = FALSE;
171 : :
172 : : static GVariant *
173 : 0 : handle_get_property (GDBusConnection *connection,
174 : : const gchar *sender,
175 : : const gchar *object_path,
176 : : const gchar *interface_name,
177 : : const gchar *property_name,
178 : : GError **error,
179 : : gpointer user_data)
180 : : {
181 : : GVariant *ret;
182 : :
183 : 0 : ret = NULL;
184 : 0 : if (g_strcmp0 (property_name, "FluxCapicitorName") == 0)
185 : : {
186 : 0 : ret = g_variant_new_string ("DeLorean");
187 : : }
188 : 0 : else if (g_strcmp0 (property_name, "Title") == 0)
189 : : {
190 : 0 : if (_global_title == NULL)
191 : 0 : _global_title = g_strdup ("Back To C!");
192 : 0 : ret = g_variant_new_string (_global_title);
193 : : }
194 : 0 : else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0)
195 : : {
196 : 0 : g_set_error (error,
197 : : G_IO_ERROR,
198 : : G_IO_ERROR_FAILED,
199 : : "Hello %s. I thought I said reading this property "
200 : : "always results in an error. kthxbye",
201 : : sender);
202 : : }
203 : 0 : else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0)
204 : : {
205 : 0 : ret = g_variant_new_string ("There's no home like home");
206 : : }
207 : 0 : else if (g_strcmp0 (property_name, "Foo") == 0)
208 : : {
209 : 0 : ret = g_variant_new_string (swap_a_and_b ? "Tock" : "Tick");
210 : : }
211 : 0 : else if (g_strcmp0 (property_name, "Bar") == 0)
212 : : {
213 : 0 : ret = g_variant_new_string (swap_a_and_b ? "Tick" : "Tock");
214 : : }
215 : :
216 : 0 : return ret;
217 : : }
218 : :
219 : : static gboolean
220 : 0 : handle_set_property (GDBusConnection *connection,
221 : : const gchar *sender,
222 : : const gchar *object_path,
223 : : const gchar *interface_name,
224 : : const gchar *property_name,
225 : : GVariant *value,
226 : : GError **error,
227 : : gpointer user_data)
228 : : {
229 : 0 : if (g_strcmp0 (property_name, "Title") == 0)
230 : : {
231 : 0 : if (g_strcmp0 (_global_title, g_variant_get_string (value, NULL)) != 0)
232 : : {
233 : : GVariantBuilder *builder;
234 : : GError *local_error;
235 : :
236 : 0 : g_free (_global_title);
237 : 0 : _global_title = g_variant_dup_string (value, NULL);
238 : :
239 : 0 : local_error = NULL;
240 : 0 : builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
241 : 0 : g_variant_builder_add (builder,
242 : : "{sv}",
243 : : "Title",
244 : : g_variant_new_string (_global_title));
245 : 0 : g_dbus_connection_emit_signal (connection,
246 : : NULL,
247 : : object_path,
248 : : DBUS_INTERFACE_PROPERTIES,
249 : : "PropertiesChanged",
250 : : g_variant_new ("(sa{sv}as)",
251 : : interface_name,
252 : : builder,
253 : : NULL),
254 : : &local_error);
255 : 0 : g_assert_no_error (local_error);
256 : : }
257 : : }
258 : 0 : else if (g_strcmp0 (property_name, "ReadingAlwaysThrowsError") == 0)
259 : : {
260 : : /* do nothing - they can't read it after all! */
261 : : }
262 : 0 : else if (g_strcmp0 (property_name, "WritingAlwaysThrowsError") == 0)
263 : : {
264 : 0 : g_set_error (error,
265 : : G_IO_ERROR,
266 : : G_IO_ERROR_FAILED,
267 : : "Hello AGAIN %s. I thought I said writing this property "
268 : : "always results in an error. kthxbye",
269 : : sender);
270 : : }
271 : :
272 : 0 : return *error == NULL;
273 : : }
274 : :
275 : :
276 : : /* for now */
277 : : static const GDBusInterfaceVTable interface_vtable =
278 : : {
279 : : handle_method_call,
280 : : handle_get_property,
281 : : handle_set_property,
282 : : { 0 }
283 : : };
284 : :
285 : : /* ---------------------------------------------------------------------------------------------------- */
286 : :
287 : : static gboolean
288 : 0 : on_timeout_cb (gpointer user_data)
289 : : {
290 : 0 : GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
291 : : GVariantBuilder *builder;
292 : : GVariantBuilder *invalidated_builder;
293 : : GError *error;
294 : :
295 : 0 : swap_a_and_b = !swap_a_and_b;
296 : :
297 : 0 : error = NULL;
298 : 0 : builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
299 : 0 : invalidated_builder = g_variant_builder_new (G_VARIANT_TYPE ("as"));
300 : 0 : g_variant_builder_add (builder,
301 : : "{sv}",
302 : : "Foo",
303 : 0 : g_variant_new_string (swap_a_and_b ? "Tock" : "Tick"));
304 : 0 : g_variant_builder_add (builder,
305 : : "{sv}",
306 : : "Bar",
307 : 0 : g_variant_new_string (swap_a_and_b ? "Tick" : "Tock"));
308 : 0 : g_dbus_connection_emit_signal (connection,
309 : : NULL,
310 : : "/org/gtk/GDBus/TestObject",
311 : : DBUS_INTERFACE_PROPERTIES,
312 : : "PropertiesChanged",
313 : : g_variant_new ("(sa{sv}as)",
314 : : "org.gtk.GDBus.TestInterface",
315 : : builder,
316 : : invalidated_builder),
317 : : &error);
318 : 0 : g_assert_no_error (error);
319 : :
320 : :
321 : 0 : return G_SOURCE_CONTINUE;
322 : : }
323 : :
324 : : /* ---------------------------------------------------------------------------------------------------- */
325 : :
326 : : static void
327 : 0 : on_bus_acquired (GDBusConnection *connection,
328 : : const gchar *name,
329 : : gpointer user_data)
330 : : {
331 : : guint registration_id;
332 : :
333 : 0 : registration_id = g_dbus_connection_register_object (connection,
334 : : "/org/gtk/GDBus/TestObject",
335 : 0 : introspection_data->interfaces[0],
336 : : &interface_vtable,
337 : : NULL, /* user_data */
338 : : NULL, /* user_data_free_func */
339 : : NULL); /* GError** */
340 : 0 : g_assert (registration_id > 0);
341 : :
342 : : /* swap value of properties Foo and Bar every two seconds */
343 : 0 : g_timeout_add_seconds (2,
344 : : on_timeout_cb,
345 : : connection);
346 : 0 : }
347 : :
348 : : static void
349 : 0 : on_name_acquired (GDBusConnection *connection,
350 : : const gchar *name,
351 : : gpointer user_data)
352 : : {
353 : 0 : }
354 : :
355 : : static void
356 : 0 : on_name_lost (GDBusConnection *connection,
357 : : const gchar *name,
358 : : gpointer user_data)
359 : : {
360 : 0 : exit (1);
361 : : }
362 : :
363 : : int
364 : 0 : main (int argc, char *argv[])
365 : : {
366 : : guint owner_id;
367 : : GMainLoop *loop;
368 : :
369 : : /* We are lazy here - we don't want to manually provide
370 : : * the introspection data structures - so we just build
371 : : * them from XML.
372 : : */
373 : 0 : introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
374 : 0 : g_assert (introspection_data != NULL);
375 : :
376 : 0 : owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
377 : : "org.gtk.GDBus.TestServer",
378 : : G_BUS_NAME_OWNER_FLAGS_NONE,
379 : : on_bus_acquired,
380 : : on_name_acquired,
381 : : on_name_lost,
382 : : NULL,
383 : : NULL);
384 : :
385 : 0 : loop = g_main_loop_new (NULL, FALSE);
386 : 0 : g_main_loop_run (loop);
387 : :
388 : 0 : g_bus_unown_name (owner_id);
389 : :
390 : 0 : g_dbus_node_info_unref (introspection_data);
391 : :
392 : 0 : return 0;
393 : : }
|