nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Copyright © 2010 Codethink Limited |
||
3 | * |
||
4 | * This library is free software; you can redistribute it and/or |
||
5 | * modify it under the terms of the GNU Lesser General Public |
||
6 | * License as published by the Free Software Foundation; either |
||
7 | * version 2 of the License, or (at your option) any later version. |
||
8 | * |
||
9 | * This library is distributed in the hope that it will be useful, |
||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
12 | * Lesser General Public License for more details. |
||
13 | * |
||
14 | * You should have received a copy of the GNU Lesser General |
||
15 | * Public License along with this library; if not, see <http://www.gnu.org/licenses/>. |
||
16 | * |
||
17 | * Authors: Ryan Lortie <desrt@desrt.ca> |
||
18 | */ |
||
19 | |||
20 | #include "config.h" |
||
21 | |||
22 | #include "gapplicationimpl.h" |
||
23 | |||
24 | #include "gactiongroup.h" |
||
25 | #include "gactiongroupexporter.h" |
||
26 | #include "gremoteactiongroup.h" |
||
27 | #include "gdbusactiongroup-private.h" |
||
28 | #include "gapplication.h" |
||
29 | #include "gfile.h" |
||
30 | #include "gdbusconnection.h" |
||
31 | #include "gdbusintrospection.h" |
||
32 | #include "gdbuserror.h" |
||
33 | #include "glib/gstdio.h" |
||
34 | |||
35 | #include <string.h> |
||
36 | #include <stdio.h> |
||
37 | |||
38 | #include "gapplicationcommandline.h" |
||
39 | #include "gdbusmethodinvocation.h" |
||
40 | |||
41 | #ifdef G_OS_UNIX |
||
42 | #include "gunixinputstream.h" |
||
43 | #include "gunixfdlist.h" |
||
44 | #endif |
||
45 | |||
46 | /* DBus Interface definition {{{1 */ |
||
47 | |||
48 | /* For documentation of these interfaces, see |
||
49 | * https://wiki.gnome.org/Projects/GLib/GApplication/DBusAPI |
||
50 | */ |
||
51 | static const gchar org_gtk_Application_xml[] = |
||
52 | "<node>" |
||
53 | "<interface name='org.gtk.Application'>" |
||
54 | "<method name='Activate'>" |
||
55 | "<arg type='a{sv}' name='platform-data' direction='in'/>" |
||
56 | "</method>" |
||
57 | "<method name='Open'>" |
||
58 | "<arg type='as' name='uris' direction='in'/>" |
||
59 | "<arg type='s' name='hint' direction='in'/>" |
||
60 | "<arg type='a{sv}' name='platform-data' direction='in'/>" |
||
61 | "</method>" |
||
62 | "<method name='CommandLine'>" |
||
63 | "<arg type='o' name='path' direction='in'/>" |
||
64 | "<arg type='aay' name='arguments' direction='in'/>" |
||
65 | "<arg type='a{sv}' name='platform-data' direction='in'/>" |
||
66 | "<arg type='i' name='exit-status' direction='out'/>" |
||
67 | "</method>" |
||
68 | "<property name='Busy' type='b' access='read'/>" |
||
69 | "</interface>" |
||
70 | "</node>"; |
||
71 | |||
72 | static GDBusInterfaceInfo *org_gtk_Application; |
||
73 | |||
74 | static const gchar org_freedesktop_Application_xml[] = |
||
75 | "<node>" |
||
76 | "<interface name='org.freedesktop.Application'>" |
||
77 | "<method name='Activate'>" |
||
78 | "<arg type='a{sv}' name='platform-data' direction='in'/>" |
||
79 | "</method>" |
||
80 | "<method name='Open'>" |
||
81 | "<arg type='as' name='uris' direction='in'/>" |
||
82 | "<arg type='a{sv}' name='platform-data' direction='in'/>" |
||
83 | "</method>" |
||
84 | "<method name='ActivateAction'>" |
||
85 | "<arg type='s' name='action-name' direction='in'/>" |
||
86 | "<arg type='av' name='parameter' direction='in'/>" |
||
87 | "<arg type='a{sv}' name='platform-data' direction='in'/>" |
||
88 | "</method>" |
||
89 | "</interface>" |
||
90 | "</node>"; |
||
91 | |||
92 | static GDBusInterfaceInfo *org_freedesktop_Application; |
||
93 | |||
94 | static const gchar org_gtk_private_CommandLine_xml[] = |
||
95 | "<node>" |
||
96 | "<interface name='org.gtk.private.CommandLine'>" |
||
97 | "<method name='Print'>" |
||
98 | "<arg type='s' name='message' direction='in'/>" |
||
99 | "</method>" |
||
100 | "<method name='PrintError'>" |
||
101 | "<arg type='s' name='message' direction='in'/>" |
||
102 | "</method>" |
||
103 | "</interface>" |
||
104 | "</node>"; |
||
105 | |||
106 | static GDBusInterfaceInfo *org_gtk_private_CommandLine; |
||
107 | |||
108 | /* GApplication implementation {{{1 */ |
||
109 | struct _GApplicationImpl |
||
110 | { |
||
111 | GDBusConnection *session_bus; |
||
112 | GActionGroup *exported_actions; |
||
113 | const gchar *bus_name; |
||
114 | |||
115 | gchar *object_path; |
||
116 | guint object_id; |
||
117 | guint fdo_object_id; |
||
118 | guint actions_id; |
||
119 | |||
120 | gboolean properties_live; |
||
121 | gboolean primary; |
||
122 | gboolean busy; |
||
123 | GApplication *app; |
||
124 | }; |
||
125 | |||
126 | |||
127 | static GApplicationCommandLine * |
||
128 | g_dbus_command_line_new (GDBusMethodInvocation *invocation); |
||
129 | |||
130 | static GVariant * |
||
131 | g_application_impl_get_property (GDBusConnection *connection, |
||
132 | const gchar *sender, |
||
133 | const gchar *object_path, |
||
134 | const gchar *interface_name, |
||
135 | const gchar *property_name, |
||
136 | GError **error, |
||
137 | gpointer user_data) |
||
138 | { |
||
139 | GApplicationImpl *impl = user_data; |
||
140 | |||
141 | if (strcmp (property_name, "Busy") == 0) |
||
142 | return g_variant_new_boolean (impl->busy); |
||
143 | |||
144 | g_assert_not_reached (); |
||
145 | |||
146 | return NULL; |
||
147 | } |
||
148 | |||
149 | static void |
||
150 | send_property_change (GApplicationImpl *impl) |
||
151 | { |
||
152 | GVariantBuilder builder; |
||
153 | |||
154 | g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); |
||
155 | g_variant_builder_add (&builder, |
||
156 | "{sv}", |
||
157 | "Busy", g_variant_new_boolean (impl->busy)); |
||
158 | |||
159 | g_dbus_connection_emit_signal (impl->session_bus, |
||
160 | NULL, |
||
161 | impl->object_path, |
||
162 | "org.freedesktop.DBus.Properties", |
||
163 | "PropertiesChanged", |
||
164 | g_variant_new ("(sa{sv}as)", |
||
165 | "org.gtk.Application", |
||
166 | &builder, |
||
167 | NULL), |
||
168 | NULL); |
||
169 | } |
||
170 | |||
171 | static void |
||
172 | g_application_impl_method_call (GDBusConnection *connection, |
||
173 | const gchar *sender, |
||
174 | const gchar *object_path, |
||
175 | const gchar *interface_name, |
||
176 | const gchar *method_name, |
||
177 | GVariant *parameters, |
||
178 | GDBusMethodInvocation *invocation, |
||
179 | gpointer user_data) |
||
180 | { |
||
181 | GApplicationImpl *impl = user_data; |
||
182 | GApplicationClass *class; |
||
183 | |||
184 | class = G_APPLICATION_GET_CLASS (impl->app); |
||
185 | |||
186 | if (strcmp (method_name, "Activate") == 0) |
||
187 | { |
||
188 | GVariant *platform_data; |
||
189 | |||
190 | /* Completely the same for both freedesktop and gtk interfaces */ |
||
191 | |||
192 | g_variant_get (parameters, "(@a{sv})", &platform_data); |
||
193 | |||
194 | class->before_emit (impl->app, platform_data); |
||
195 | g_signal_emit_by_name (impl->app, "activate"); |
||
196 | class->after_emit (impl->app, platform_data); |
||
197 | g_variant_unref (platform_data); |
||
198 | |||
199 | g_dbus_method_invocation_return_value (invocation, NULL); |
||
200 | } |
||
201 | |||
202 | else if (strcmp (method_name, "Open") == 0) |
||
203 | { |
||
204 | GApplicationFlags flags; |
||
205 | GVariant *platform_data; |
||
206 | const gchar *hint; |
||
207 | GVariant *array; |
||
208 | GFile **files; |
||
209 | gint n, i; |
||
210 | |||
211 | flags = g_application_get_flags (impl->app); |
||
212 | if ((flags & G_APPLICATION_HANDLES_OPEN) == 0) |
||
213 | { |
||
214 | g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "Application does not open files"); |
||
215 | return; |
||
216 | } |
||
217 | |||
218 | /* freedesktop interface has no hint parameter */ |
||
219 | if (g_str_equal (interface_name, "org.freedesktop.Application")) |
||
220 | { |
||
221 | g_variant_get (parameters, "(@as@a{sv})", &array, &platform_data); |
||
222 | hint = ""; |
||
223 | } |
||
224 | else |
||
225 | g_variant_get (parameters, "(@as&s@a{sv})", &array, &hint, &platform_data); |
||
226 | |||
227 | n = g_variant_n_children (array); |
||
228 | files = g_new (GFile *, n + 1); |
||
229 | |||
230 | for (i = 0; i < n; i++) |
||
231 | { |
||
232 | const gchar *uri; |
||
233 | |||
234 | g_variant_get_child (array, i, "&s", &uri); |
||
235 | files[i] = g_file_new_for_uri (uri); |
||
236 | } |
||
237 | g_variant_unref (array); |
||
238 | files[n] = NULL; |
||
239 | |||
240 | class->before_emit (impl->app, platform_data); |
||
241 | g_signal_emit_by_name (impl->app, "open", files, n, hint); |
||
242 | class->after_emit (impl->app, platform_data); |
||
243 | |||
244 | g_variant_unref (platform_data); |
||
245 | |||
246 | for (i = 0; i < n; i++) |
||
247 | g_object_unref (files[i]); |
||
248 | g_free (files); |
||
249 | |||
250 | g_dbus_method_invocation_return_value (invocation, NULL); |
||
251 | } |
||
252 | |||
253 | else if (strcmp (method_name, "CommandLine") == 0) |
||
254 | { |
||
255 | GApplicationFlags flags; |
||
256 | GApplicationCommandLine *cmdline; |
||
257 | GVariant *platform_data; |
||
258 | int status; |
||
259 | |||
260 | flags = g_application_get_flags (impl->app); |
||
261 | if ((flags & G_APPLICATION_HANDLES_COMMAND_LINE) == 0) |
||
262 | { |
||
263 | g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, |
||
264 | "Application does not handle command line arguments"); |
||
265 | return; |
||
266 | } |
||
267 | |||
268 | /* Only on the GtkApplication interface */ |
||
269 | |||
270 | cmdline = g_dbus_command_line_new (invocation); |
||
271 | platform_data = g_variant_get_child_value (parameters, 2); |
||
272 | class->before_emit (impl->app, platform_data); |
||
273 | g_signal_emit_by_name (impl->app, "command-line", cmdline, &status); |
||
274 | g_application_command_line_set_exit_status (cmdline, status); |
||
275 | class->after_emit (impl->app, platform_data); |
||
276 | g_variant_unref (platform_data); |
||
277 | g_object_unref (cmdline); |
||
278 | } |
||
279 | else if (g_str_equal (method_name, "ActivateAction")) |
||
280 | { |
||
281 | GVariant *parameter = NULL; |
||
282 | GVariant *platform_data; |
||
283 | GVariantIter *iter; |
||
284 | const gchar *name; |
||
285 | |||
286 | /* Only on the freedesktop interface */ |
||
287 | |||
288 | g_variant_get (parameters, "(&sav@a{sv})", &name, &iter, &platform_data); |
||
289 | g_variant_iter_next (iter, "v", ¶meter); |
||
290 | g_variant_iter_free (iter); |
||
291 | |||
292 | class->before_emit (impl->app, platform_data); |
||
293 | g_action_group_activate_action (impl->exported_actions, name, parameter); |
||
294 | class->after_emit (impl->app, platform_data); |
||
295 | |||
296 | if (parameter) |
||
297 | g_variant_unref (parameter); |
||
298 | |||
299 | g_variant_unref (platform_data); |
||
300 | |||
301 | g_dbus_method_invocation_return_value (invocation, NULL); |
||
302 | } |
||
303 | else |
||
304 | g_assert_not_reached (); |
||
305 | } |
||
306 | |||
307 | static gchar * |
||
308 | application_path_from_appid (const gchar *appid) |
||
309 | { |
||
310 | gchar *appid_path, *iter; |
||
311 | |||
312 | if (appid == NULL) |
||
313 | /* this is a private implementation detail */ |
||
314 | return g_strdup ("/org/gtk/Application/anonymous"); |
||
315 | |||
316 | appid_path = g_strconcat ("/", appid, NULL); |
||
317 | for (iter = appid_path; *iter; iter++) |
||
318 | { |
||
319 | if (*iter == '.') |
||
320 | *iter = '/'; |
||
321 | |||
322 | if (*iter == '-') |
||
323 | *iter = '_'; |
||
324 | } |
||
325 | |||
326 | return appid_path; |
||
327 | } |
||
328 | |||
329 | /* Attempt to become the primary instance. |
||
330 | * |
||
331 | * Returns %TRUE if everything went OK, regardless of if we became the |
||
332 | * primary instance or not. %FALSE is reserved for when something went |
||
333 | * seriously wrong (and @error will be set too, in that case). |
||
334 | * |
||
335 | * After a %TRUE return, impl->primary will be TRUE if we were |
||
336 | * successful. |
||
337 | */ |
||
338 | static gboolean |
||
339 | g_application_impl_attempt_primary (GApplicationImpl *impl, |
||
340 | GCancellable *cancellable, |
||
341 | GError **error) |
||
342 | { |
||
343 | const static GDBusInterfaceVTable vtable = { |
||
344 | g_application_impl_method_call, |
||
345 | g_application_impl_get_property, |
||
346 | NULL /* set_property */ |
||
347 | }; |
||
348 | GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app); |
||
349 | GVariant *reply; |
||
350 | guint32 rval; |
||
351 | |||
352 | if (org_gtk_Application == NULL) |
||
353 | { |
||
354 | GError *error = NULL; |
||
355 | GDBusNodeInfo *info; |
||
356 | |||
357 | info = g_dbus_node_info_new_for_xml (org_gtk_Application_xml, &error); |
||
358 | if G_UNLIKELY (info == NULL) |
||
359 | g_error ("%s", error->message); |
||
360 | org_gtk_Application = g_dbus_node_info_lookup_interface (info, "org.gtk.Application"); |
||
361 | g_assert (org_gtk_Application != NULL); |
||
362 | g_dbus_interface_info_ref (org_gtk_Application); |
||
363 | g_dbus_node_info_unref (info); |
||
364 | |||
365 | info = g_dbus_node_info_new_for_xml (org_freedesktop_Application_xml, &error); |
||
366 | if G_UNLIKELY (info == NULL) |
||
367 | g_error ("%s", error->message); |
||
368 | org_freedesktop_Application = g_dbus_node_info_lookup_interface (info, "org.freedesktop.Application"); |
||
369 | g_assert (org_freedesktop_Application != NULL); |
||
370 | g_dbus_interface_info_ref (org_freedesktop_Application); |
||
371 | g_dbus_node_info_unref (info); |
||
372 | } |
||
373 | |||
374 | /* We could possibly have been D-Bus activated as a result of incoming |
||
375 | * requests on either the application or actiongroup interfaces. |
||
376 | * Because of how GDBus dispatches messages, we need to ensure that |
||
377 | * both of those things are registered before we attempt to request |
||
378 | * our name. |
||
379 | * |
||
380 | * The action group need not be populated yet, as long as it happens |
||
381 | * before we return to the mainloop. The reason for that is because |
||
382 | * GDBus does the check to make sure the object exists from the worker |
||
383 | * thread but doesn't actually dispatch the action invocation until we |
||
384 | * hit the mainloop in this thread. There is also no danger of |
||
385 | * receiving 'activate' or 'open' signals until after 'startup' runs, |
||
386 | * for the same reason. |
||
387 | */ |
||
388 | impl->object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path, |
||
389 | org_gtk_Application, &vtable, impl, NULL, error); |
||
390 | |||
391 | if (impl->object_id == 0) |
||
392 | return FALSE; |
||
393 | |||
394 | impl->fdo_object_id = g_dbus_connection_register_object (impl->session_bus, impl->object_path, |
||
395 | org_freedesktop_Application, &vtable, impl, NULL, error); |
||
396 | |||
397 | if (impl->fdo_object_id == 0) |
||
398 | return FALSE; |
||
399 | |||
400 | impl->actions_id = g_dbus_connection_export_action_group (impl->session_bus, impl->object_path, |
||
401 | impl->exported_actions, error); |
||
402 | |||
403 | if (impl->actions_id == 0) |
||
404 | return FALSE; |
||
405 | |||
406 | if (!app_class->dbus_register (impl->app, |
||
407 | impl->session_bus, |
||
408 | impl->object_path, |
||
409 | error)) |
||
410 | return FALSE; |
||
411 | |||
412 | if (impl->bus_name == NULL) |
||
413 | { |
||
414 | /* If this is a non-unique application then it is sufficient to |
||
415 | * have our object paths registered. We can return now. |
||
416 | * |
||
417 | * Note: non-unique applications always act as primary-instance. |
||
418 | */ |
||
419 | impl->primary = TRUE; |
||
420 | return TRUE; |
||
421 | } |
||
422 | |||
423 | /* If this is a unique application then we need to attempt to own |
||
424 | * the well-known name and fall back to remote mode (!is_primary) |
||
425 | * in the case that we can't do that. |
||
426 | */ |
||
427 | /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */ |
||
428 | reply = g_dbus_connection_call_sync (impl->session_bus, "org.freedesktop.DBus", "/org/freedesktop/DBus", |
||
429 | "org.freedesktop.DBus", "RequestName", |
||
430 | g_variant_new ("(su)", impl->bus_name, 0x4), G_VARIANT_TYPE ("(u)"), |
||
431 | 0, -1, cancellable, error); |
||
432 | |||
433 | if (reply == NULL) |
||
434 | return FALSE; |
||
435 | |||
436 | g_variant_get (reply, "(u)", &rval); |
||
437 | g_variant_unref (reply); |
||
438 | |||
439 | /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */ |
||
440 | impl->primary = (rval != 3); |
||
441 | |||
442 | return TRUE; |
||
443 | } |
||
444 | |||
445 | /* Stop doing the things that the primary instance does. |
||
446 | * |
||
447 | * This should be called if attempting to become the primary instance |
||
448 | * failed (in order to clean up any partial success) and should also |
||
449 | * be called when freeing the GApplication. |
||
450 | * |
||
451 | * It is safe to call this multiple times. |
||
452 | */ |
||
453 | static void |
||
454 | g_application_impl_stop_primary (GApplicationImpl *impl) |
||
455 | { |
||
456 | GApplicationClass *app_class = G_APPLICATION_GET_CLASS (impl->app); |
||
457 | |||
458 | app_class->dbus_unregister (impl->app, |
||
459 | impl->session_bus, |
||
460 | impl->object_path); |
||
461 | |||
462 | if (impl->object_id) |
||
463 | { |
||
464 | g_dbus_connection_unregister_object (impl->session_bus, impl->object_id); |
||
465 | impl->object_id = 0; |
||
466 | } |
||
467 | |||
468 | if (impl->fdo_object_id) |
||
469 | { |
||
470 | g_dbus_connection_unregister_object (impl->session_bus, impl->fdo_object_id); |
||
471 | impl->fdo_object_id = 0; |
||
472 | } |
||
473 | |||
474 | if (impl->actions_id) |
||
475 | { |
||
476 | g_dbus_connection_unexport_action_group (impl->session_bus, impl->actions_id); |
||
477 | impl->actions_id = 0; |
||
478 | } |
||
479 | |||
480 | if (impl->primary && impl->bus_name) |
||
481 | { |
||
482 | g_dbus_connection_call (impl->session_bus, "org.freedesktop.DBus", |
||
483 | "/org/freedesktop/DBus", "org.freedesktop.DBus", |
||
484 | "ReleaseName", g_variant_new ("(s)", impl->bus_name), |
||
485 | NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, NULL, NULL); |
||
486 | impl->primary = FALSE; |
||
487 | } |
||
488 | } |
||
489 | |||
490 | void |
||
491 | g_application_impl_set_busy_state (GApplicationImpl *impl, |
||
492 | gboolean busy) |
||
493 | { |
||
494 | if (impl->busy != busy) |
||
495 | { |
||
496 | impl->busy = busy; |
||
497 | send_property_change (impl); |
||
498 | } |
||
499 | } |
||
500 | |||
501 | void |
||
502 | g_application_impl_destroy (GApplicationImpl *impl) |
||
503 | { |
||
504 | g_application_impl_stop_primary (impl); |
||
505 | |||
506 | if (impl->session_bus) |
||
507 | g_object_unref (impl->session_bus); |
||
508 | |||
509 | g_free (impl->object_path); |
||
510 | |||
511 | g_slice_free (GApplicationImpl, impl); |
||
512 | } |
||
513 | |||
514 | GApplicationImpl * |
||
515 | g_application_impl_register (GApplication *application, |
||
516 | const gchar *appid, |
||
517 | GApplicationFlags flags, |
||
518 | GActionGroup *exported_actions, |
||
519 | GRemoteActionGroup **remote_actions, |
||
520 | GCancellable *cancellable, |
||
521 | GError **error) |
||
522 | { |
||
523 | GDBusActionGroup *actions; |
||
524 | GApplicationImpl *impl; |
||
525 | |||
526 | g_assert ((flags & G_APPLICATION_NON_UNIQUE) || appid != NULL); |
||
527 | |||
528 | impl = g_slice_new0 (GApplicationImpl); |
||
529 | |||
530 | impl->app = application; |
||
531 | impl->exported_actions = exported_actions; |
||
532 | |||
533 | /* non-unique applications do not attempt to acquire a bus name */ |
||
534 | if (~flags & G_APPLICATION_NON_UNIQUE) |
||
535 | impl->bus_name = appid; |
||
536 | |||
537 | impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL); |
||
538 | |||
539 | if (impl->session_bus == NULL) |
||
540 | { |
||
541 | /* If we can't connect to the session bus, proceed as a normal |
||
542 | * non-unique application. |
||
543 | */ |
||
544 | *remote_actions = NULL; |
||
545 | return impl; |
||
546 | } |
||
547 | |||
548 | impl->object_path = application_path_from_appid (appid); |
||
549 | |||
550 | /* Only try to be the primary instance if |
||
551 | * G_APPLICATION_IS_LAUNCHER was not specified. |
||
552 | */ |
||
553 | if (~flags & G_APPLICATION_IS_LAUNCHER) |
||
554 | { |
||
555 | if (!g_application_impl_attempt_primary (impl, cancellable, error)) |
||
556 | { |
||
557 | g_application_impl_destroy (impl); |
||
558 | return NULL; |
||
559 | } |
||
560 | |||
561 | if (impl->primary) |
||
562 | return impl; |
||
563 | |||
564 | /* We didn't make it. Drop our service-side stuff. */ |
||
565 | g_application_impl_stop_primary (impl); |
||
566 | |||
567 | if (flags & G_APPLICATION_IS_SERVICE) |
||
568 | { |
||
569 | g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, |
||
570 | "Unable to acquire bus name '%s'", appid); |
||
571 | g_application_impl_destroy (impl); |
||
572 | |||
573 | return NULL; |
||
574 | } |
||
575 | } |
||
576 | |||
577 | /* We are non-primary. Try to get the primary's list of actions. |
||
578 | * This also serves as a mechanism to ensure that the primary exists |
||
579 | * (ie: DBus service files installed correctly, etc). |
||
580 | */ |
||
581 | actions = g_dbus_action_group_get (impl->session_bus, impl->bus_name, impl->object_path); |
||
582 | if (!g_dbus_action_group_sync (actions, cancellable, error)) |
||
583 | { |
||
584 | /* The primary appears not to exist. Fail the registration. */ |
||
585 | g_application_impl_destroy (impl); |
||
586 | g_object_unref (actions); |
||
587 | |||
588 | return NULL; |
||
589 | } |
||
590 | |||
591 | *remote_actions = G_REMOTE_ACTION_GROUP (actions); |
||
592 | |||
593 | return impl; |
||
594 | } |
||
595 | |||
596 | void |
||
597 | g_application_impl_activate (GApplicationImpl *impl, |
||
598 | GVariant *platform_data) |
||
599 | { |
||
600 | g_dbus_connection_call (impl->session_bus, |
||
601 | impl->bus_name, |
||
602 | impl->object_path, |
||
603 | "org.gtk.Application", |
||
604 | "Activate", |
||
605 | g_variant_new ("(@a{sv})", platform_data), |
||
606 | NULL, 0, -1, NULL, NULL, NULL); |
||
607 | } |
||
608 | |||
609 | void |
||
610 | g_application_impl_open (GApplicationImpl *impl, |
||
611 | GFile **files, |
||
612 | gint n_files, |
||
613 | const gchar *hint, |
||
614 | GVariant *platform_data) |
||
615 | { |
||
616 | GVariantBuilder builder; |
||
617 | gint i; |
||
618 | |||
619 | g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})")); |
||
620 | g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY); |
||
621 | for (i = 0; i < n_files; i++) |
||
622 | { |
||
623 | gchar *uri = g_file_get_uri (files[i]); |
||
624 | g_variant_builder_add (&builder, "s", uri); |
||
625 | g_free (uri); |
||
626 | } |
||
627 | g_variant_builder_close (&builder); |
||
628 | g_variant_builder_add (&builder, "s", hint); |
||
629 | g_variant_builder_add_value (&builder, platform_data); |
||
630 | |||
631 | g_dbus_connection_call (impl->session_bus, |
||
632 | impl->bus_name, |
||
633 | impl->object_path, |
||
634 | "org.gtk.Application", |
||
635 | "Open", |
||
636 | g_variant_builder_end (&builder), |
||
637 | NULL, 0, -1, NULL, NULL, NULL); |
||
638 | } |
||
639 | |||
640 | static void |
||
641 | g_application_impl_cmdline_method_call (GDBusConnection *connection, |
||
642 | const gchar *sender, |
||
643 | const gchar *object_path, |
||
644 | const gchar *interface_name, |
||
645 | const gchar *method_name, |
||
646 | GVariant *parameters, |
||
647 | GDBusMethodInvocation *invocation, |
||
648 | gpointer user_data) |
||
649 | { |
||
650 | const gchar *message; |
||
651 | |||
652 | g_variant_get_child (parameters, 0, "&s", &message); |
||
653 | |||
654 | if (strcmp (method_name, "Print") == 0) |
||
655 | g_print ("%s", message); |
||
656 | else if (strcmp (method_name, "PrintError") == 0) |
||
657 | g_printerr ("%s", message); |
||
658 | else |
||
659 | g_assert_not_reached (); |
||
660 | |||
661 | g_dbus_method_invocation_return_value (invocation, NULL); |
||
662 | } |
||
663 | |||
664 | typedef struct |
||
665 | { |
||
666 | GMainLoop *loop; |
||
667 | int status; |
||
668 | } CommandLineData; |
||
669 | |||
670 | static void |
||
671 | g_application_impl_cmdline_done (GObject *source, |
||
672 | GAsyncResult *result, |
||
673 | gpointer user_data) |
||
674 | { |
||
675 | CommandLineData *data = user_data; |
||
676 | GError *error = NULL; |
||
677 | GVariant *reply; |
||
678 | |||
679 | #ifdef G_OS_UNIX |
||
680 | reply = g_dbus_connection_call_with_unix_fd_list_finish (G_DBUS_CONNECTION (source), NULL, result, &error); |
||
681 | #else |
||
682 | reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source), result, &error); |
||
683 | #endif |
||
684 | |||
685 | |||
686 | if (reply != NULL) |
||
687 | { |
||
688 | g_variant_get (reply, "(i)", &data->status); |
||
689 | g_variant_unref (reply); |
||
690 | } |
||
691 | |||
692 | else |
||
693 | { |
||
694 | g_printerr ("%s\n", error->message); |
||
695 | g_error_free (error); |
||
696 | data->status = 1; |
||
697 | } |
||
698 | |||
699 | g_main_loop_quit (data->loop); |
||
700 | } |
||
701 | |||
702 | int |
||
703 | g_application_impl_command_line (GApplicationImpl *impl, |
||
704 | const gchar * const *arguments, |
||
705 | GVariant *platform_data) |
||
706 | { |
||
707 | const static GDBusInterfaceVTable vtable = { |
||
708 | g_application_impl_cmdline_method_call |
||
709 | }; |
||
710 | const gchar *object_path = "/org/gtk/Application/CommandLine"; |
||
711 | GMainContext *context; |
||
712 | CommandLineData data; |
||
713 | guint object_id; |
||
714 | |||
715 | context = g_main_context_new (); |
||
716 | data.loop = g_main_loop_new (context, FALSE); |
||
717 | g_main_context_push_thread_default (context); |
||
718 | |||
719 | if (org_gtk_private_CommandLine == NULL) |
||
720 | { |
||
721 | GError *error = NULL; |
||
722 | GDBusNodeInfo *info; |
||
723 | |||
724 | info = g_dbus_node_info_new_for_xml (org_gtk_private_CommandLine_xml, &error); |
||
725 | if G_UNLIKELY (info == NULL) |
||
726 | g_error ("%s", error->message); |
||
727 | org_gtk_private_CommandLine = g_dbus_node_info_lookup_interface (info, "org.gtk.private.CommandLine"); |
||
728 | g_assert (org_gtk_private_CommandLine != NULL); |
||
729 | g_dbus_interface_info_ref (org_gtk_private_CommandLine); |
||
730 | g_dbus_node_info_unref (info); |
||
731 | } |
||
732 | |||
733 | object_id = g_dbus_connection_register_object (impl->session_bus, object_path, |
||
734 | org_gtk_private_CommandLine, |
||
735 | &vtable, &data, NULL, NULL); |
||
736 | /* In theory we should try other paths... */ |
||
737 | g_assert (object_id != 0); |
||
738 | |||
739 | #ifdef G_OS_UNIX |
||
740 | { |
||
741 | GError *error = NULL; |
||
742 | GUnixFDList *fd_list; |
||
743 | |||
744 | /* send along the stdin in case |
||
745 | * g_application_command_line_get_stdin_data() is called |
||
746 | */ |
||
747 | fd_list = g_unix_fd_list_new (); |
||
748 | g_unix_fd_list_append (fd_list, 0, &error); |
||
749 | g_assert_no_error (error); |
||
750 | |||
751 | g_dbus_connection_call_with_unix_fd_list (impl->session_bus, impl->bus_name, impl->object_path, |
||
752 | "org.gtk.Application", "CommandLine", |
||
753 | g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data), |
||
754 | G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, fd_list, NULL, |
||
755 | g_application_impl_cmdline_done, &data); |
||
756 | g_object_unref (fd_list); |
||
757 | } |
||
758 | #else |
||
759 | g_dbus_connection_call (impl->session_bus, impl->bus_name, impl->object_path, |
||
760 | "org.gtk.Application", "CommandLine", |
||
761 | g_variant_new ("(o^aay@a{sv})", object_path, arguments, platform_data), |
||
762 | G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, NULL, |
||
763 | g_application_impl_cmdline_done, &data); |
||
764 | #endif |
||
765 | |||
766 | g_main_loop_run (data.loop); |
||
767 | |||
768 | g_main_context_pop_thread_default (context); |
||
769 | g_main_context_unref (context); |
||
770 | g_main_loop_unref (data.loop); |
||
771 | |||
772 | return data.status; |
||
773 | } |
||
774 | |||
775 | void |
||
776 | g_application_impl_flush (GApplicationImpl *impl) |
||
777 | { |
||
778 | if (impl->session_bus) |
||
779 | g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL); |
||
780 | } |
||
781 | |||
782 | GDBusConnection * |
||
783 | g_application_impl_get_dbus_connection (GApplicationImpl *impl) |
||
784 | { |
||
785 | return impl->session_bus; |
||
786 | } |
||
787 | |||
788 | const gchar * |
||
789 | g_application_impl_get_dbus_object_path (GApplicationImpl *impl) |
||
790 | { |
||
791 | return impl->object_path; |
||
792 | } |
||
793 | |||
794 | /* GDBusCommandLine implementation {{{1 */ |
||
795 | |||
796 | typedef GApplicationCommandLineClass GDBusCommandLineClass; |
||
797 | static GType g_dbus_command_line_get_type (void); |
||
798 | typedef struct |
||
799 | { |
||
800 | GApplicationCommandLine parent_instance; |
||
801 | GDBusMethodInvocation *invocation; |
||
802 | |||
803 | GDBusConnection *connection; |
||
804 | const gchar *bus_name; |
||
805 | const gchar *object_path; |
||
806 | } GDBusCommandLine; |
||
807 | |||
808 | |||
809 | G_DEFINE_TYPE (GDBusCommandLine, |
||
810 | g_dbus_command_line, |
||
811 | G_TYPE_APPLICATION_COMMAND_LINE) |
||
812 | |||
813 | static void |
||
814 | g_dbus_command_line_print_literal (GApplicationCommandLine *cmdline, |
||
815 | const gchar *message) |
||
816 | { |
||
817 | GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline; |
||
818 | |||
819 | g_dbus_connection_call (gdbcl->connection, |
||
820 | gdbcl->bus_name, |
||
821 | gdbcl->object_path, |
||
822 | "org.gtk.private.CommandLine", "Print", |
||
823 | g_variant_new ("(s)", message), |
||
824 | NULL, 0, -1, NULL, NULL, NULL); |
||
825 | } |
||
826 | |||
827 | static void |
||
828 | g_dbus_command_line_printerr_literal (GApplicationCommandLine *cmdline, |
||
829 | const gchar *message) |
||
830 | { |
||
831 | GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline; |
||
832 | |||
833 | g_dbus_connection_call (gdbcl->connection, |
||
834 | gdbcl->bus_name, |
||
835 | gdbcl->object_path, |
||
836 | "org.gtk.private.CommandLine", "PrintError", |
||
837 | g_variant_new ("(s)", message), |
||
838 | NULL, 0, -1, NULL, NULL, NULL); |
||
839 | } |
||
840 | |||
841 | static GInputStream * |
||
842 | g_dbus_command_line_get_stdin (GApplicationCommandLine *cmdline) |
||
843 | { |
||
844 | #ifdef G_OS_UNIX |
||
845 | GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline; |
||
846 | GInputStream *result = NULL; |
||
847 | GDBusMessage *message; |
||
848 | GUnixFDList *fd_list; |
||
849 | |||
850 | message = g_dbus_method_invocation_get_message (gdbcl->invocation); |
||
851 | fd_list = g_dbus_message_get_unix_fd_list (message); |
||
852 | |||
853 | if (fd_list && g_unix_fd_list_get_length (fd_list)) |
||
854 | { |
||
855 | gint *fds, n_fds, i; |
||
856 | |||
857 | fds = g_unix_fd_list_steal_fds (fd_list, &n_fds); |
||
858 | result = g_unix_input_stream_new (fds[0], TRUE); |
||
859 | for (i = 1; i < n_fds; i++) |
||
860 | (void) g_close (fds[i], NULL); |
||
861 | g_free (fds); |
||
862 | } |
||
863 | |||
864 | return result; |
||
865 | #else |
||
866 | return NULL; |
||
867 | #endif |
||
868 | } |
||
869 | |||
870 | static void |
||
871 | g_dbus_command_line_finalize (GObject *object) |
||
872 | { |
||
873 | GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object); |
||
874 | GDBusCommandLine *gdbcl = (GDBusCommandLine *) object; |
||
875 | gint status; |
||
876 | |||
877 | status = g_application_command_line_get_exit_status (cmdline); |
||
878 | |||
879 | g_dbus_method_invocation_return_value (gdbcl->invocation, |
||
880 | g_variant_new ("(i)", status)); |
||
881 | g_object_unref (gdbcl->invocation); |
||
882 | |||
883 | G_OBJECT_CLASS (g_dbus_command_line_parent_class) |
||
884 | ->finalize (object); |
||
885 | } |
||
886 | |||
887 | static void |
||
888 | g_dbus_command_line_init (GDBusCommandLine *gdbcl) |
||
889 | { |
||
890 | } |
||
891 | |||
892 | static void |
||
893 | g_dbus_command_line_class_init (GApplicationCommandLineClass *class) |
||
894 | { |
||
895 | GObjectClass *object_class = G_OBJECT_CLASS (class); |
||
896 | |||
897 | object_class->finalize = g_dbus_command_line_finalize; |
||
898 | class->printerr_literal = g_dbus_command_line_printerr_literal; |
||
899 | class->print_literal = g_dbus_command_line_print_literal; |
||
900 | class->get_stdin = g_dbus_command_line_get_stdin; |
||
901 | } |
||
902 | |||
903 | static GApplicationCommandLine * |
||
904 | g_dbus_command_line_new (GDBusMethodInvocation *invocation) |
||
905 | { |
||
906 | GDBusCommandLine *gdbcl; |
||
907 | GVariant *args; |
||
908 | GVariant *arguments, *platform_data; |
||
909 | |||
910 | args = g_dbus_method_invocation_get_parameters (invocation); |
||
911 | |||
912 | arguments = g_variant_get_child_value (args, 1); |
||
913 | platform_data = g_variant_get_child_value (args, 2); |
||
914 | gdbcl = g_object_new (g_dbus_command_line_get_type (), |
||
915 | "arguments", arguments, |
||
916 | "platform-data", platform_data, |
||
917 | NULL); |
||
918 | g_variant_unref (arguments); |
||
919 | g_variant_unref (platform_data); |
||
920 | |||
921 | gdbcl->connection = g_dbus_method_invocation_get_connection (invocation); |
||
922 | gdbcl->bus_name = g_dbus_method_invocation_get_sender (invocation); |
||
923 | g_variant_get_child (args, 0, "&o", &gdbcl->object_path); |
||
924 | gdbcl->invocation = g_object_ref (invocation); |
||
925 | |||
926 | return G_APPLICATION_COMMAND_LINE (gdbcl); |
||
927 | } |
||
928 | |||
929 | /* Epilogue {{{1 */ |
||
930 | |||
931 | /* vim:set foldmethod=marker: */ |