2 * Copyright © 2010 Codethink Limited
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.
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.
14 * You should have received a copy of the GNU Lesser General
15 * Public License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17 * Boston, MA 02111-1307, USA.
19 * Authors: Ryan Lortie <desrt@desrt.ca>
22 #include "gapplicationimpl.h"
24 #include "gactiongroup.h"
25 #include "gactiongroupexporter.h"
26 #include "gremoteactiongroup.h"
27 #include "gdbusactiongroup.h"
28 #include "gapplication.h"
30 #include "gdbusconnection.h"
31 #include "gdbusintrospection.h"
32 #include "gdbuserror.h"
37 #include "gapplicationcommandline.h"
38 #include "gdbusmethodinvocation.h"
40 G_GNUC_INTERNAL gboolean
41 g_dbus_action_group_sync (GDBusActionGroup
*group
,
42 GCancellable
*cancellable
,
46 /* DBus Interface definition {{{1 */
48 /* For documentation of these interfaces, see
49 * http://live.gnome.org/GTK+/GApplication-dbus-apis
51 static const gchar org_gtk_Application_xml
[] =
53 "<interface name='org.gtk.Application'>"
54 "<method name='Activate'>"
55 "<arg type='a{sv}' name='platform-data' direction='in'/>"
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'/>"
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'/>"
71 static GDBusInterfaceInfo
*org_gtk_Application
;
73 static const gchar org_gtk_private_CommandLine_xml
[] =
75 "<interface name='org.gtk.private.CommandLine'>"
76 "<method name='Print'>"
77 "<arg type='s' name='message' direction='in'/>"
79 "<method name='PrintError'>"
80 "<arg type='s' name='message' direction='in'/>"
85 static GDBusInterfaceInfo
*org_gtk_private_CommandLine
;
87 /* GApplication implementation {{{1 */
88 struct _GApplicationImpl
90 GDBusConnection
*session_bus
;
91 GActionGroup
*exported_actions
;
92 const gchar
*bus_name
;
98 gboolean properties_live
;
104 static GApplicationCommandLine
*
105 g_dbus_command_line_new (GDBusMethodInvocation
*invocation
);
109 g_application_impl_method_call (GDBusConnection
*connection
,
111 const gchar
*object_path
,
112 const gchar
*interface_name
,
113 const gchar
*method_name
,
114 GVariant
*parameters
,
115 GDBusMethodInvocation
*invocation
,
118 GApplicationImpl
*impl
= user_data
;
119 GApplicationClass
*class;
121 class = G_APPLICATION_GET_CLASS (impl
->app
);
123 if (strcmp (method_name
, "Activate") == 0)
125 GVariant
*platform_data
;
127 g_variant_get (parameters
, "(@a{sv})", &platform_data
);
128 class->before_emit (impl
->app
, platform_data
);
129 g_signal_emit_by_name (impl
->app
, "activate");
130 class->after_emit (impl
->app
, platform_data
);
131 g_variant_unref (platform_data
);
133 g_dbus_method_invocation_return_value (invocation
, NULL
);
136 else if (strcmp (method_name
, "Open") == 0)
138 GVariant
*platform_data
;
144 g_variant_get (parameters
, "(@ass@a{sv})",
145 &array
, &hint
, &platform_data
);
147 n
= g_variant_n_children (array
);
148 files
= g_new (GFile
*, n
+ 1);
150 for (i
= 0; i
< n
; i
++)
154 g_variant_get_child (array
, i
, "&s", &uri
);
155 files
[i
] = g_file_new_for_uri (uri
);
157 g_variant_unref (array
);
160 class->before_emit (impl
->app
, platform_data
);
161 g_signal_emit_by_name (impl
->app
, "open", files
, n
, hint
);
162 class->after_emit (impl
->app
, platform_data
);
164 g_variant_unref (platform_data
);
166 for (i
= 0; i
< n
; i
++)
167 g_object_unref (files
[i
]);
170 g_dbus_method_invocation_return_value (invocation
, NULL
);
173 else if (strcmp (method_name
, "CommandLine") == 0)
175 GApplicationCommandLine
*cmdline
;
176 GVariant
*platform_data
;
179 cmdline
= g_dbus_command_line_new (invocation
);
180 platform_data
= g_variant_get_child_value (parameters
, 2);
181 class->before_emit (impl
->app
, platform_data
);
182 g_signal_emit_by_name (impl
->app
, "command-line", cmdline
, &status
);
183 g_application_command_line_set_exit_status (cmdline
, status
);
184 class->after_emit (impl
->app
, platform_data
);
185 g_variant_unref (platform_data
);
186 g_object_unref (cmdline
);
189 g_assert_not_reached ();
193 application_path_from_appid (const gchar
*appid
)
195 gchar
*appid_path
, *iter
;
198 /* this is a private implementation detail */
199 return g_strdup ("/org/gtk/Application/anonymous");
201 appid_path
= g_strconcat ("/", appid
, NULL
);
202 for (iter
= appid_path
; *iter
; iter
++)
214 /* Attempt to become the primary instance.
216 * Returns %TRUE if everything went OK, regardless of if we became the
217 * primary instance or not. %FALSE is reserved for when something went
218 * seriously wrong (and @error will be set too, in that case).
220 * After a %TRUE return, impl->primary will be TRUE if we were
224 g_application_impl_attempt_primary (GApplicationImpl
*impl
,
225 GCancellable
*cancellable
,
228 const static GDBusInterfaceVTable vtable
= {
229 g_application_impl_method_call
,
231 GApplicationClass
*app_class
= G_APPLICATION_GET_CLASS (impl
->app
);
235 if (org_gtk_Application
== NULL
)
237 GError
*error
= NULL
;
240 info
= g_dbus_node_info_new_for_xml (org_gtk_Application_xml
, &error
);
241 if G_UNLIKELY (info
== NULL
)
242 g_error ("%s", error
->message
);
243 org_gtk_Application
= g_dbus_node_info_lookup_interface (info
, "org.gtk.Application");
244 g_assert (org_gtk_Application
!= NULL
);
245 g_dbus_interface_info_ref (org_gtk_Application
);
246 g_dbus_node_info_unref (info
);
249 /* We could possibly have been D-Bus activated as a result of incoming
250 * requests on either the application or actiongroup interfaces.
251 * Because of how GDBus dispatches messages, we need to ensure that
252 * both of those things are registered before we attempt to request
255 * The action group need not be populated yet, as long as it happens
256 * before we return to the mainloop. The reason for that is because
257 * GDBus does the check to make sure the object exists from the worker
258 * thread but doesn't actually dispatch the action invocation until we
259 * hit the mainloop in this thread. There is also no danger of
260 * receiving 'activate' or 'open' signals until after 'startup' runs,
261 * for the same reason.
263 impl
->object_id
= g_dbus_connection_register_object (impl
->session_bus
, impl
->object_path
,
264 org_gtk_Application
, &vtable
, impl
, NULL
, error
);
266 if (impl
->object_id
== 0)
269 impl
->actions_id
= g_dbus_connection_export_action_group (impl
->session_bus
, impl
->object_path
,
270 impl
->exported_actions
, error
);
272 if (impl
->actions_id
== 0)
275 if (!app_class
->dbus_register (impl
->app
,
281 if (impl
->bus_name
== NULL
)
283 /* If this is a non-unique application then it is sufficient to
284 * have our object paths registered. We can return now.
286 * Note: non-unique applications always act as primary-instance.
288 impl
->primary
= TRUE
;
292 /* If this is a unique application then we need to attempt to own
293 * the well-known name and fall back to remote mode (!is_primary)
294 * in the case that we can't do that.
296 /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
297 reply
= g_dbus_connection_call_sync (impl
->session_bus
, "org.freedesktop.DBus", "/org/freedesktop/DBus",
298 "org.freedesktop.DBus", "RequestName",
299 g_variant_new ("(su)", impl
->bus_name
, 0x4), G_VARIANT_TYPE ("(u)"),
300 0, -1, cancellable
, error
);
305 g_variant_get (reply
, "(u)", &rval
);
306 g_variant_unref (reply
);
308 /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
309 impl
->primary
= (rval
!= 3);
314 /* Stop doing the things that the primary instance does.
316 * This should be called if attempting to become the primary instance
317 * failed (in order to clean up any partial success) and should also
318 * be called when freeing the GApplication.
320 * It is safe to call this multiple times.
323 g_application_impl_stop_primary (GApplicationImpl
*impl
)
325 GApplicationClass
*app_class
= G_APPLICATION_GET_CLASS (impl
->app
);
327 app_class
->dbus_unregister (impl
->app
,
333 g_dbus_connection_unregister_object (impl
->session_bus
, impl
->object_id
);
337 if (impl
->actions_id
)
339 g_dbus_connection_unexport_action_group (impl
->session_bus
, impl
->actions_id
);
340 impl
->actions_id
= 0;
343 if (impl
->primary
&& impl
->bus_name
)
345 g_dbus_connection_call (impl
->session_bus
, "org.freedesktop.DBus",
346 "/org/freedesktop/DBus", "org.freedesktop.DBus",
347 "ReleaseName", g_variant_new ("(s)", impl
->bus_name
),
348 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
349 impl
->primary
= FALSE
;
354 g_application_impl_destroy (GApplicationImpl
*impl
)
356 g_application_impl_stop_primary (impl
);
358 if (impl
->session_bus
)
359 g_object_unref (impl
->session_bus
);
361 g_free (impl
->object_path
);
363 g_slice_free (GApplicationImpl
, impl
);
367 g_application_impl_register (GApplication
*application
,
369 GApplicationFlags flags
,
370 GActionGroup
*exported_actions
,
371 GRemoteActionGroup
**remote_actions
,
372 GCancellable
*cancellable
,
375 GDBusActionGroup
*actions
;
376 GApplicationImpl
*impl
;
378 g_assert ((flags
& G_APPLICATION_NON_UNIQUE
) || appid
!= NULL
);
380 impl
= g_slice_new0 (GApplicationImpl
);
382 impl
->app
= application
;
383 impl
->exported_actions
= exported_actions
;
385 /* non-unique applications do not attempt to acquire a bus name */
386 if (~flags
& G_APPLICATION_NON_UNIQUE
)
387 impl
->bus_name
= appid
;
389 impl
->session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, cancellable
, NULL
);
391 if (impl
->session_bus
== NULL
)
393 /* If we can't connect to the session bus, proceed as a normal
394 * non-unique application.
396 *remote_actions
= NULL
;
400 impl
->object_path
= application_path_from_appid (appid
);
402 /* Only try to be the primary instance if
403 * G_APPLICATION_IS_LAUNCHER was not specified.
405 if (~flags
& G_APPLICATION_IS_LAUNCHER
)
407 if (!g_application_impl_attempt_primary (impl
, cancellable
, error
))
409 g_application_impl_destroy (impl
);
416 /* We didn't make it. Drop our service-side stuff. */
417 g_application_impl_stop_primary (impl
);
419 if (flags
& G_APPLICATION_IS_SERVICE
)
421 g_set_error (error
, G_DBUS_ERROR
, G_DBUS_ERROR_FAILED
,
422 "Unable to acquire bus name `%s'", appid
);
423 g_application_impl_destroy (impl
);
429 /* We are non-primary. Try to get the primary's list of actions.
430 * This also serves as a mechanism to ensure that the primary exists
431 * (ie: DBus service files installed correctly, etc).
433 actions
= g_dbus_action_group_get (impl
->session_bus
, impl
->bus_name
, impl
->object_path
);
434 if (!g_dbus_action_group_sync (actions
, cancellable
, error
))
436 /* The primary appears not to exist. Fail the registration. */
437 g_application_impl_destroy (impl
);
438 g_object_unref (actions
);
443 *remote_actions
= G_REMOTE_ACTION_GROUP (actions
);
449 g_application_impl_activate (GApplicationImpl
*impl
,
450 GVariant
*platform_data
)
452 g_dbus_connection_call (impl
->session_bus
,
455 "org.gtk.Application",
457 g_variant_new ("(@a{sv})", platform_data
),
458 NULL
, 0, -1, NULL
, NULL
, NULL
);
462 g_application_impl_open (GApplicationImpl
*impl
,
466 GVariant
*platform_data
)
468 GVariantBuilder builder
;
471 g_variant_builder_init (&builder
, G_VARIANT_TYPE ("(assa{sv})"));
472 g_variant_builder_open (&builder
, G_VARIANT_TYPE_STRING_ARRAY
);
473 for (i
= 0; i
< n_files
; i
++)
475 gchar
*uri
= g_file_get_uri (files
[i
]);
476 g_variant_builder_add (&builder
, "s", uri
);
479 g_variant_builder_close (&builder
);
480 g_variant_builder_add (&builder
, "s", hint
);
481 g_variant_builder_add_value (&builder
, platform_data
);
483 g_dbus_connection_call (impl
->session_bus
,
486 "org.gtk.Application",
488 g_variant_builder_end (&builder
),
489 NULL
, 0, -1, NULL
, NULL
, NULL
);
493 g_application_impl_cmdline_method_call (GDBusConnection
*connection
,
495 const gchar
*object_path
,
496 const gchar
*interface_name
,
497 const gchar
*method_name
,
498 GVariant
*parameters
,
499 GDBusMethodInvocation
*invocation
,
502 const gchar
*message
;
504 g_variant_get_child (parameters
, 0, "&s", &message
);
506 if (strcmp (method_name
, "Print") == 0)
507 g_print ("%s", message
);
508 else if (strcmp (method_name
, "PrintError") == 0)
509 g_printerr ("%s", message
);
511 g_assert_not_reached ();
513 g_dbus_method_invocation_return_value (invocation
, NULL
);
523 g_application_impl_cmdline_done (GObject
*source
,
524 GAsyncResult
*result
,
527 CommandLineData
*data
= user_data
;
528 GError
*error
= NULL
;
531 reply
= g_dbus_connection_call_finish (G_DBUS_CONNECTION (source
),
536 g_variant_get (reply
, "(i)", &data
->status
);
537 g_variant_unref (reply
);
542 g_printerr ("%s\n", error
->message
);
543 g_error_free (error
);
547 g_main_loop_quit (data
->loop
);
551 g_application_impl_command_line (GApplicationImpl
*impl
,
553 GVariant
*platform_data
)
555 const static GDBusInterfaceVTable vtable
= {
556 g_application_impl_cmdline_method_call
558 const gchar
*object_path
= "/org/gtk/Application/CommandLine";
559 GMainContext
*context
;
560 CommandLineData data
;
563 context
= g_main_context_new ();
564 data
.loop
= g_main_loop_new (context
, FALSE
);
565 g_main_context_push_thread_default (context
);
567 if (org_gtk_private_CommandLine
== NULL
)
569 GError
*error
= NULL
;
572 info
= g_dbus_node_info_new_for_xml (org_gtk_private_CommandLine_xml
, &error
);
573 if G_UNLIKELY (info
== NULL
)
574 g_error ("%s", error
->message
);
575 org_gtk_private_CommandLine
= g_dbus_node_info_lookup_interface (info
, "org.gtk.private.CommandLine");
576 g_assert (org_gtk_private_CommandLine
!= NULL
);
577 g_dbus_interface_info_ref (org_gtk_private_CommandLine
);
578 g_dbus_node_info_unref (info
);
581 object_id
= g_dbus_connection_register_object (impl
->session_bus
, object_path
,
582 org_gtk_private_CommandLine
,
583 &vtable
, &data
, NULL
, NULL
);
584 /* In theory we should try other paths... */
585 g_assert (object_id
!= 0);
587 g_dbus_connection_call (impl
->session_bus
,
590 "org.gtk.Application",
592 g_variant_new ("(o^aay@a{sv})", object_path
,
593 arguments
, platform_data
),
594 G_VARIANT_TYPE ("(i)"), 0, G_MAXINT
, NULL
,
595 g_application_impl_cmdline_done
, &data
);
597 g_main_loop_run (data
.loop
);
599 g_main_context_pop_thread_default (context
);
600 g_main_context_unref (context
);
601 g_main_loop_unref (data
.loop
);
607 g_application_impl_flush (GApplicationImpl
*impl
)
609 if (impl
->session_bus
)
610 g_dbus_connection_flush_sync (impl
->session_bus
, NULL
, NULL
);
614 g_application_impl_get_dbus_connection (GApplicationImpl
*impl
)
616 return impl
->session_bus
;
620 g_application_impl_get_dbus_object_path (GApplicationImpl
*impl
)
622 return impl
->object_path
;
625 /* GDBusCommandLine implementation {{{1 */
627 typedef GApplicationCommandLineClass GDBusCommandLineClass
;
628 static GType
g_dbus_command_line_get_type (void);
631 GApplicationCommandLine parent_instance
;
632 GDBusMethodInvocation
*invocation
;
634 GDBusConnection
*connection
;
635 const gchar
*bus_name
;
636 const gchar
*object_path
;
640 G_DEFINE_TYPE (GDBusCommandLine
,
642 G_TYPE_APPLICATION_COMMAND_LINE
)
645 g_dbus_command_line_print_literal (GApplicationCommandLine
*cmdline
,
646 const gchar
*message
)
648 GDBusCommandLine
*gdbcl
= (GDBusCommandLine
*) cmdline
;
650 g_dbus_connection_call (gdbcl
->connection
,
653 "org.gtk.private.CommandLine", "Print",
654 g_variant_new ("(s)", message
),
655 NULL
, 0, -1, NULL
, NULL
, NULL
);
659 g_dbus_command_line_printerr_literal (GApplicationCommandLine
*cmdline
,
660 const gchar
*message
)
662 GDBusCommandLine
*gdbcl
= (GDBusCommandLine
*) cmdline
;
664 g_dbus_connection_call (gdbcl
->connection
,
667 "org.gtk.private.CommandLine", "PrintError",
668 g_variant_new ("(s)", message
),
669 NULL
, 0, -1, NULL
, NULL
, NULL
);
673 g_dbus_command_line_finalize (GObject
*object
)
675 GApplicationCommandLine
*cmdline
= G_APPLICATION_COMMAND_LINE (object
);
676 GDBusCommandLine
*gdbcl
= (GDBusCommandLine
*) object
;
679 status
= g_application_command_line_get_exit_status (cmdline
);
681 g_dbus_method_invocation_return_value (gdbcl
->invocation
,
682 g_variant_new ("(i)", status
));
683 g_object_unref (gdbcl
->invocation
);
685 G_OBJECT_CLASS (g_dbus_command_line_parent_class
)
690 g_dbus_command_line_init (GDBusCommandLine
*gdbcl
)
695 g_dbus_command_line_class_init (GApplicationCommandLineClass
*class)
697 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
699 object_class
->finalize
= g_dbus_command_line_finalize
;
700 class->printerr_literal
= g_dbus_command_line_printerr_literal
;
701 class->print_literal
= g_dbus_command_line_print_literal
;
704 static GApplicationCommandLine
*
705 g_dbus_command_line_new (GDBusMethodInvocation
*invocation
)
707 GDBusCommandLine
*gdbcl
;
710 args
= g_dbus_method_invocation_get_parameters (invocation
);
712 gdbcl
= g_object_new (g_dbus_command_line_get_type (),
713 "arguments", g_variant_get_child_value (args
, 1),
714 "platform-data", g_variant_get_child_value (args
, 2),
716 gdbcl
->connection
= g_dbus_method_invocation_get_connection (invocation
);
717 gdbcl
->bus_name
= g_dbus_method_invocation_get_sender (invocation
);
718 g_variant_get_child (args
, 0, "&o", &gdbcl
->object_path
);
719 gdbcl
->invocation
= g_object_ref (invocation
);
721 return G_APPLICATION_COMMAND_LINE (gdbcl
);
726 /* vim:set foldmethod=marker: */