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 "gdbusactiongroup.h"
27 #include "gapplication.h"
29 #include "gdbusconnection.h"
30 #include "gdbusintrospection.h"
31 #include "gdbuserror.h"
32 #include "gmenuexporter.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 */
47 static const gchar org_gtk_Application_xml
[] =
49 " <interface name='org.gtk.Application'>"
50 " <method name='Activate'>"
51 " <arg type='a{sv}' name='platform-data' direction='in'/>"
53 " <method name='Open'>"
54 " <arg type='as' name='uris' direction='in'/>"
55 " <arg type='s' name='hint' direction='in'/>"
56 " <arg type='a{sv}' name='platform-data' direction='in'/>"
58 " <method name='CommandLine'>"
59 " <arg type='o' name='path' direction='in'/>"
60 " <arg type='aay' name='arguments' direction='in'/>"
61 " <arg type='a{sv}' name='platform-data' direction='in'/>"
62 " <arg type='i' name='exit-status' direction='out'/>"
64 " <property name='AppMenu' type='ao' access='read'/>"
65 " <property name='MenuBar' type='ao' access='read'/>"
69 static GDBusInterfaceInfo
*org_gtk_Application
;
71 static const gchar org_gtk_private_CommandLine_xml
[] =
73 " <interface name='org.gtk.private.CommandLine'>"
74 " <method name='Print'>"
75 " <arg type='s' name='message' direction='in'/>"
77 " <method name='PrintError'>"
78 " <arg type='s' name='message' direction='in'/>"
83 static GDBusInterfaceInfo
*org_gtk_private_CommandLine
;
85 /* GApplication implementation {{{1 */
86 struct _GApplicationImpl
88 GDBusConnection
*session_bus
;
89 const gchar
*bus_name
;
101 gboolean properties_live
;
107 static GApplicationCommandLine
*
108 g_dbus_command_line_new (GDBusMethodInvocation
*invocation
);
112 g_application_impl_method_call (GDBusConnection
*connection
,
114 const gchar
*object_path
,
115 const gchar
*interface_name
,
116 const gchar
*method_name
,
117 GVariant
*parameters
,
118 GDBusMethodInvocation
*invocation
,
121 GApplicationImpl
*impl
= user_data
;
122 GApplicationClass
*class;
124 class = G_APPLICATION_GET_CLASS (impl
->app
);
126 if (strcmp (method_name
, "Activate") == 0)
128 GVariant
*platform_data
;
130 g_variant_get (parameters
, "(@a{sv})", &platform_data
);
131 class->before_emit (impl
->app
, platform_data
);
132 g_signal_emit_by_name (impl
->app
, "activate");
133 class->after_emit (impl
->app
, platform_data
);
134 g_variant_unref (platform_data
);
136 g_dbus_method_invocation_return_value (invocation
, NULL
);
139 else if (strcmp (method_name
, "Open") == 0)
141 GVariant
*platform_data
;
147 g_variant_get (parameters
, "(@ass@a{sv})",
148 &array
, &hint
, &platform_data
);
150 n
= g_variant_n_children (array
);
151 files
= g_new (GFile
*, n
+ 1);
153 for (i
= 0; i
< n
; i
++)
157 g_variant_get_child (array
, i
, "&s", &uri
);
158 files
[i
] = g_file_new_for_uri (uri
);
160 g_variant_unref (array
);
163 class->before_emit (impl
->app
, platform_data
);
164 g_signal_emit_by_name (impl
->app
, "open", files
, n
, hint
);
165 class->after_emit (impl
->app
, platform_data
);
167 g_variant_unref (platform_data
);
169 for (i
= 0; i
< n
; i
++)
170 g_object_unref (files
[i
]);
173 g_dbus_method_invocation_return_value (invocation
, NULL
);
176 else if (strcmp (method_name
, "CommandLine") == 0)
178 GApplicationCommandLine
*cmdline
;
179 GVariant
*platform_data
;
182 cmdline
= g_dbus_command_line_new (invocation
);
183 platform_data
= g_variant_get_child_value (parameters
, 2);
184 class->before_emit (impl
->app
, platform_data
);
185 g_signal_emit_by_name (impl
->app
, "command-line", cmdline
, &status
);
186 g_application_command_line_set_exit_status (cmdline
, status
);
187 class->after_emit (impl
->app
, platform_data
);
188 g_variant_unref (platform_data
);
189 g_object_unref (cmdline
);
192 g_assert_not_reached ();
196 g_application_impl_get_property (GDBusConnection
*connection
,
198 const gchar
*object_path
,
199 const gchar
*interface_name
,
200 const gchar
*property_name
,
204 GApplicationImpl
*impl
= user_data
;
205 GVariantBuilder builder
;
207 /* We use this boolean to detect if the properties have ever been
208 * queried before. If they have not been queried, then there is no
209 * point emitting change signals since nobody is watching anyway.
211 impl
->properties_live
= TRUE
;
213 g_variant_builder_init (&builder
, G_VARIANT_TYPE_OBJECT_PATH_ARRAY
);
215 if (g_str_equal (property_name
, "AppMenu"))
217 if (impl
->app_menu_path
!= NULL
)
218 g_variant_builder_add (&builder
, "o", impl
->app_menu_path
);
221 else if (g_str_equal (property_name
, "MenuBar"))
223 if (impl
->menubar_path
!= NULL
)
224 g_variant_builder_add (&builder
, "o", impl
->menubar_path
);
228 g_assert_not_reached ();
230 return g_variant_builder_end (&builder
);
234 application_path_from_appid (const gchar
*appid
)
236 gchar
*appid_path
, *iter
;
238 appid_path
= g_strconcat ("/", appid
, NULL
);
239 for (iter
= appid_path
; *iter
; iter
++)
252 g_application_impl_publish_menu (GApplicationImpl
*impl
,
260 /* unexport any existing menu */
263 g_dbus_connection_unexport_menu_model (impl
->session_bus
, *id
);
269 /* export the new menu, if there is one */
272 /* try getting the preferred name */
273 *path
= g_strconcat (impl
->object_path
, "/menus/", type
, NULL
);
274 *id
= g_dbus_connection_export_menu_model (impl
->session_bus
, *path
, model
, NULL
);
276 /* keep trying until we get a working name... */
277 for (i
= 0; *id
== 0; i
++)
280 *path
= g_strdup_printf ("%s/menus/%s%d", impl
->object_path
, type
, i
);
281 *id
= g_dbus_connection_export_menu_model (impl
->session_bus
, *path
, model
, NULL
);
285 /* notify for changes, if needed */
286 if (impl
->properties_live
)
288 GVariantBuilder builder
;
290 g_variant_builder_init (&builder
, G_VARIANT_TYPE_VARDICT
);
291 g_variant_builder_add (&builder
, "{sv}", type
, g_variant_new_objv ((const gchar
**) path
, *path
!= NULL
));
292 g_dbus_connection_emit_signal (impl
->session_bus
, NULL
, impl
->object_path
,
293 "org.freedesktop.DBus.Properties", "PropertiesChanged",
294 g_variant_new ("(sa{sv}as)", "org.gtk.Actions", &builder
, NULL
), NULL
);
299 g_application_impl_app_menu_changed (GObject
*source
,
303 GApplicationImpl
*impl
= user_data
;
305 g_assert (source
== impl
->app
);
307 g_application_impl_publish_menu (impl
, "AppMenu", g_application_get_app_menu (impl
->app
),
308 &impl
->app_menu_id
, &impl
->app_menu_path
);
312 g_application_impl_menubar_changed (GObject
*source
,
316 GApplicationImpl
*impl
= user_data
;
318 g_assert (source
== impl
->app
);
320 g_application_impl_publish_menu (impl
, "MenuBar", g_application_get_menubar (impl
->app
),
321 &impl
->menubar_id
, &impl
->menubar_path
);
324 /* Attempt to become the primary instance.
326 * Returns %TRUE if everything went OK, regardless of if we became the
327 * primary instance or not. %FALSE is reserved for when something went
328 * seriously wrong (and @error will be set too, in that case).
330 * After a %TRUE return, impl->primary will be TRUE if we were
334 g_application_impl_attempt_primary (GApplicationImpl
*impl
,
335 GCancellable
*cancellable
,
338 const static GDBusInterfaceVTable vtable
= {
339 g_application_impl_method_call
,
340 g_application_impl_get_property
345 if (org_gtk_Application
== NULL
)
347 GError
*error
= NULL
;
350 info
= g_dbus_node_info_new_for_xml (org_gtk_Application_xml
, &error
);
351 if G_UNLIKELY (info
== NULL
)
352 g_error ("%s", error
->message
);
353 org_gtk_Application
= g_dbus_node_info_lookup_interface (info
, "org.gtk.Application");
354 g_assert (org_gtk_Application
!= NULL
);
355 g_dbus_interface_info_ref (org_gtk_Application
);
356 g_dbus_node_info_unref (info
);
359 /* We could possibly have been D-Bus activated as a result of incoming
360 * requests on either the application or actiongroup interfaces.
361 * Because of how GDBus dispatches messages, we need to ensure that
362 * both of those things are registered before we attempt to request
365 * The action group need not be populated yet, as long as it happens
366 * before we return to the mainloop. The reason for that is because
367 * GDBus does the check to make sure the object exists from the worker
368 * thread but doesn't actually dispatch the action invocation until we
369 * hit the mainloop in this thread. There is also no danger of
370 * receiving 'activate' or 'open' signals until after 'startup' runs,
371 * for the same reason.
373 impl
->object_id
= g_dbus_connection_register_object (impl
->session_bus
, impl
->object_path
,
374 org_gtk_Application
, &vtable
, impl
, NULL
, error
);
376 if (impl
->object_id
== 0)
379 impl
->actions_id
= g_dbus_connection_export_action_group (impl
->session_bus
, impl
->object_path
, impl
->app
, error
);
381 if (impl
->actions_id
== 0)
384 /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
385 reply
= g_dbus_connection_call_sync (impl
->session_bus
, "org.freedesktop.DBus", "/org/freedesktop/DBus",
386 "org.freedesktop.DBus", "RequestName",
387 g_variant_new ("(su)", impl
->bus_name
, 0x4), G_VARIANT_TYPE ("(u)"),
388 0, -1, cancellable
, error
);
393 g_variant_get (reply
, "(u)", &rval
);
394 g_variant_unref (reply
);
396 /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
397 impl
->primary
= (rval
!= 3);
401 g_signal_connect (impl
->app
, "notify::app-menu", G_CALLBACK (g_application_impl_app_menu_changed
), impl
);
402 g_signal_connect (impl
->app
, "notify::menubar", G_CALLBACK (g_application_impl_menubar_changed
), impl
);
403 g_application_impl_app_menu_changed (impl
->app
, NULL
, impl
);
404 g_application_impl_menubar_changed (impl
->app
, NULL
, impl
);
410 /* Stop doing the things that the primary instance does.
412 * This should be called if attempting to become the primary instance
413 * failed (in order to clean up any partial success) and should also
414 * be called when freeing the GApplication.
416 * It is safe to call this multiple times.
419 g_application_impl_stop_primary (GApplicationImpl
*impl
)
423 g_dbus_connection_unregister_object (impl
->session_bus
, impl
->object_id
);
427 if (impl
->actions_id
)
429 g_dbus_connection_unexport_action_group (impl
->session_bus
, impl
->actions_id
);
430 impl
->actions_id
= 0;
435 g_signal_handlers_disconnect_by_func (impl
->app
, g_application_impl_app_menu_changed
, impl
);
436 g_signal_handlers_disconnect_by_func (impl
->app
, g_application_impl_menubar_changed
, impl
);
438 g_dbus_connection_call (impl
->session_bus
, "org.freedesktop.DBus",
439 "/org/freedesktop/DBus", "org.freedesktop.DBus",
440 "ReleaseName", g_variant_new ("(s)", impl
->bus_name
),
441 NULL
, G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, NULL
, NULL
);
442 impl
->primary
= FALSE
;
445 if (impl
->app_menu_id
)
447 g_dbus_connection_unexport_menu_model (impl
->session_bus
, impl
->app_menu_id
);
448 g_free (impl
->app_menu_path
);
449 impl
->app_menu_path
= NULL
;
450 impl
->app_menu_id
= 0;
453 if (impl
->menubar_id
)
455 g_dbus_connection_unexport_menu_model (impl
->session_bus
, impl
->menubar_id
);
456 g_free (impl
->menubar_path
);
457 impl
->menubar_path
= NULL
;
458 impl
->menubar_id
= 0;
463 g_application_impl_destroy (GApplicationImpl
*impl
)
465 g_application_impl_stop_primary (impl
);
467 if (impl
->session_bus
)
468 g_object_unref (impl
->session_bus
);
470 g_free (impl
->object_path
);
472 g_slice_free (GApplicationImpl
, impl
);
476 g_application_impl_register (GApplication
*application
,
478 GApplicationFlags flags
,
479 GActionGroup
**remote_actions
,
480 GCancellable
*cancellable
,
483 GDBusActionGroup
*actions
;
484 GApplicationImpl
*impl
;
486 impl
= g_slice_new0 (GApplicationImpl
);
488 impl
->app
= application
;
489 impl
->bus_name
= appid
;
491 impl
->session_bus
= g_bus_get_sync (G_BUS_TYPE_SESSION
, cancellable
, NULL
);
493 if (impl
->session_bus
== NULL
)
495 /* If we can't connect to the session bus, proceed as a normal
496 * non-unique application.
498 *remote_actions
= NULL
;
502 impl
->object_path
= application_path_from_appid (appid
);
504 /* Only try to be the primary instance if
505 * G_APPLICATION_IS_LAUNCHER was not specified.
507 if (~flags
& G_APPLICATION_IS_LAUNCHER
)
509 if (!g_application_impl_attempt_primary (impl
, cancellable
, error
))
511 g_application_impl_destroy (impl
);
518 /* We didn't make it. Drop our service-side stuff. */
519 g_application_impl_stop_primary (impl
);
521 if (flags
& G_APPLICATION_IS_SERVICE
)
523 g_set_error (error
, G_DBUS_ERROR
, G_DBUS_ERROR_FAILED
,
524 "Unable to acquire bus name `%s'", appid
);
525 g_application_impl_destroy (impl
);
531 /* We are non-primary. Try to get the primary's list of actions.
532 * This also serves as a mechanism to ensure that the primary exists
533 * (ie: DBus service files installed correctly, etc).
535 actions
= g_dbus_action_group_get (impl
->session_bus
, impl
->bus_name
, impl
->object_path
);
536 if (!g_dbus_action_group_sync (actions
, cancellable
, error
))
538 /* The primary appears not to exist. Fail the registration. */
539 g_application_impl_destroy (impl
);
540 g_object_unref (actions
);
545 *remote_actions
= G_ACTION_GROUP (actions
);
551 g_application_impl_activate (GApplicationImpl
*impl
,
552 GVariant
*platform_data
)
554 g_dbus_connection_call (impl
->session_bus
,
557 "org.gtk.Application",
559 g_variant_new ("(@a{sv})", platform_data
),
560 NULL
, 0, -1, NULL
, NULL
, NULL
);
564 g_application_impl_open (GApplicationImpl
*impl
,
568 GVariant
*platform_data
)
570 GVariantBuilder builder
;
573 g_variant_builder_init (&builder
, G_VARIANT_TYPE ("(assa{sv})"));
574 g_variant_builder_open (&builder
, G_VARIANT_TYPE_STRING_ARRAY
);
575 for (i
= 0; i
< n_files
; i
++)
577 gchar
*uri
= g_file_get_uri (files
[i
]);
578 g_variant_builder_add (&builder
, "s", uri
);
581 g_variant_builder_close (&builder
);
582 g_variant_builder_add (&builder
, "s", hint
);
583 g_variant_builder_add_value (&builder
, platform_data
);
585 g_dbus_connection_call (impl
->session_bus
,
588 "org.gtk.Application",
590 g_variant_builder_end (&builder
),
591 NULL
, 0, -1, NULL
, NULL
, NULL
);
595 g_application_impl_cmdline_method_call (GDBusConnection
*connection
,
597 const gchar
*object_path
,
598 const gchar
*interface_name
,
599 const gchar
*method_name
,
600 GVariant
*parameters
,
601 GDBusMethodInvocation
*invocation
,
604 const gchar
*message
;
606 g_variant_get_child (parameters
, 0, "&s", &message
);
608 if (strcmp (method_name
, "Print") == 0)
609 g_print ("%s", message
);
610 else if (strcmp (method_name
, "PrintError") == 0)
611 g_printerr ("%s", message
);
613 g_assert_not_reached ();
615 g_dbus_method_invocation_return_value (invocation
, NULL
);
625 g_application_impl_cmdline_done (GObject
*source
,
626 GAsyncResult
*result
,
629 CommandLineData
*data
= user_data
;
630 GError
*error
= NULL
;
633 reply
= g_dbus_connection_call_finish (G_DBUS_CONNECTION (source
),
638 g_variant_get (reply
, "(i)", &data
->status
);
639 g_variant_unref (reply
);
644 g_printerr ("%s\n", error
->message
);
645 g_error_free (error
);
649 g_main_loop_quit (data
->loop
);
653 g_application_impl_command_line (GApplicationImpl
*impl
,
655 GVariant
*platform_data
)
657 const static GDBusInterfaceVTable vtable
= {
658 g_application_impl_cmdline_method_call
660 const gchar
*object_path
= "/org/gtk/Application/CommandLine";
661 GMainContext
*context
;
662 CommandLineData data
;
665 context
= g_main_context_new ();
666 data
.loop
= g_main_loop_new (context
, FALSE
);
667 g_main_context_push_thread_default (context
);
669 if (org_gtk_private_CommandLine
== NULL
)
671 GError
*error
= NULL
;
674 info
= g_dbus_node_info_new_for_xml (org_gtk_private_CommandLine_xml
, &error
);
675 if G_UNLIKELY (info
== NULL
)
676 g_error ("%s", error
->message
);
677 org_gtk_private_CommandLine
= g_dbus_node_info_lookup_interface (info
, "org.gtk.private.CommandLine");
678 g_assert (org_gtk_private_CommandLine
!= NULL
);
679 g_dbus_interface_info_ref (org_gtk_private_CommandLine
);
680 g_dbus_node_info_unref (info
);
683 object_id
= g_dbus_connection_register_object (impl
->session_bus
, object_path
,
684 org_gtk_private_CommandLine
,
685 &vtable
, &data
, NULL
, NULL
);
686 /* In theory we should try other paths... */
687 g_assert (object_id
!= 0);
689 g_dbus_connection_call (impl
->session_bus
,
692 "org.gtk.Application",
694 g_variant_new ("(o^aay@a{sv})", object_path
,
695 arguments
, platform_data
),
696 G_VARIANT_TYPE ("(i)"), 0, G_MAXINT
, NULL
,
697 g_application_impl_cmdline_done
, &data
);
699 g_main_loop_run (data
.loop
);
701 g_main_context_pop_thread_default (context
);
702 g_main_context_unref (context
);
703 g_main_loop_unref (data
.loop
);
709 g_application_impl_flush (GApplicationImpl
*impl
)
711 if (impl
->session_bus
)
712 g_dbus_connection_flush_sync (impl
->session_bus
, NULL
, NULL
);
716 /* GDBusCommandLine implementation {{{1 */
718 typedef GApplicationCommandLineClass GDBusCommandLineClass
;
719 static GType
g_dbus_command_line_get_type (void);
722 GApplicationCommandLine parent_instance
;
723 GDBusMethodInvocation
*invocation
;
725 GDBusConnection
*connection
;
726 const gchar
*bus_name
;
727 const gchar
*object_path
;
731 G_DEFINE_TYPE (GDBusCommandLine
,
733 G_TYPE_APPLICATION_COMMAND_LINE
)
736 g_dbus_command_line_print_literal (GApplicationCommandLine
*cmdline
,
737 const gchar
*message
)
739 GDBusCommandLine
*gdbcl
= (GDBusCommandLine
*) cmdline
;
741 g_dbus_connection_call (gdbcl
->connection
,
744 "org.gtk.private.CommandLine", "Print",
745 g_variant_new ("(s)", message
),
746 NULL
, 0, -1, NULL
, NULL
, NULL
);
750 g_dbus_command_line_printerr_literal (GApplicationCommandLine
*cmdline
,
751 const gchar
*message
)
753 GDBusCommandLine
*gdbcl
= (GDBusCommandLine
*) cmdline
;
755 g_dbus_connection_call (gdbcl
->connection
,
758 "org.gtk.private.CommandLine", "PrintError",
759 g_variant_new ("(s)", message
),
760 NULL
, 0, -1, NULL
, NULL
, NULL
);
764 g_dbus_command_line_finalize (GObject
*object
)
766 GApplicationCommandLine
*cmdline
= G_APPLICATION_COMMAND_LINE (object
);
767 GDBusCommandLine
*gdbcl
= (GDBusCommandLine
*) object
;
770 status
= g_application_command_line_get_exit_status (cmdline
);
772 g_dbus_method_invocation_return_value (gdbcl
->invocation
,
773 g_variant_new ("(i)", status
));
774 g_object_unref (gdbcl
->invocation
);
776 G_OBJECT_CLASS (g_dbus_command_line_parent_class
)
781 g_dbus_command_line_init (GDBusCommandLine
*gdbcl
)
786 g_dbus_command_line_class_init (GApplicationCommandLineClass
*class)
788 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
790 object_class
->finalize
= g_dbus_command_line_finalize
;
791 class->printerr_literal
= g_dbus_command_line_printerr_literal
;
792 class->print_literal
= g_dbus_command_line_print_literal
;
795 static GApplicationCommandLine
*
796 g_dbus_command_line_new (GDBusMethodInvocation
*invocation
)
798 GDBusCommandLine
*gdbcl
;
801 args
= g_dbus_method_invocation_get_parameters (invocation
);
803 gdbcl
= g_object_new (g_dbus_command_line_get_type (),
804 "arguments", g_variant_get_child_value (args
, 1),
805 "platform-data", g_variant_get_child_value (args
, 2),
807 gdbcl
->connection
= g_dbus_method_invocation_get_connection (invocation
);
808 gdbcl
->bus_name
= g_dbus_method_invocation_get_sender (invocation
);
809 g_variant_get_child (args
, 0, "&o", &gdbcl
->object_path
);
810 gdbcl
->invocation
= g_object_ref (invocation
);
812 return G_APPLICATION_COMMAND_LINE (gdbcl
);
817 /* vim:set foldmethod=marker: */