GApplication: port action client to new D-Bus API
[platform/upstream/glib.git] / gio / gapplicationimpl-dbus.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Authors: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "gapplicationimpl.h"
23
24 #include "gactiongroup.h"
25 #include "gactiongroupexporter.h"
26 #include "gapplication.h"
27 #include "gfile.h"
28 #include "gdbusconnection.h"
29 #include "gdbusintrospection.h"
30 #include "gdbuserror.h"
31
32 #include <string.h>
33 #include <stdio.h>
34
35 #include "gapplicationcommandline.h"
36 #include "gdbusmethodinvocation.h"
37
38 /* DBus Interface definition {{{1 */
39 static const GDBusArgInfo platform_data_arg = { -1, (gchar *) "platform_data", (gchar *) "a{sv}" };
40
41 static const GDBusArgInfo open_uris_arg = { -1, (gchar *) "uris", (gchar *) "as" };
42 static const GDBusArgInfo open_hint_arg = { -1, (gchar *) "hint", (gchar *) "s" };
43
44 static const GDBusArgInfo invoke_action_name_arg = { -1, (gchar *) "name", (gchar *) "s" };
45 static const GDBusArgInfo invoke_action_args_arg = { -1, (gchar *) "args", (gchar *) "v" };
46
47 static const GDBusArgInfo cmdline_path_arg = { -1, (gchar *) "path", (gchar *) "o" };
48 static const GDBusArgInfo cmdline_arguments_arg = { -1, (gchar *) "arguments", (gchar *) "aay" };
49 static const GDBusArgInfo cmdline_exit_status_arg = { -1, (gchar *) "exit_status", (gchar *) "i" };
50
51 static const GDBusArgInfo *activate_in[] = { &platform_data_arg, NULL };
52 static const GDBusArgInfo *activate_out[] = { NULL };
53
54 static const GDBusArgInfo *open_in[] = { &open_uris_arg, &open_hint_arg, &platform_data_arg, NULL };
55 static const GDBusArgInfo *open_out[] = { NULL };
56
57 static const GDBusArgInfo *cmdline_in[] = { &cmdline_path_arg, &cmdline_arguments_arg, &platform_data_arg, NULL };
58 static const GDBusArgInfo *cmdline_out[] = { &cmdline_exit_status_arg, NULL };
59
60 static const GDBusMethodInfo activate_method = {
61   -1, (gchar *) "Activate",
62   (GDBusArgInfo **) activate_in,
63   (GDBusArgInfo **) activate_out
64 };
65
66 static const GDBusMethodInfo open_method = {
67   -1, (gchar *) "Open",
68   (GDBusArgInfo **) open_in,
69   (GDBusArgInfo **) open_out
70 };
71
72 static const GDBusMethodInfo command_line_method = {
73   -1, (gchar *) "CommandLine",
74   (GDBusArgInfo **) cmdline_in,
75   (GDBusArgInfo **) cmdline_out
76 };
77
78 static const GDBusMethodInfo *application_methods[] = {
79   &activate_method, &open_method, &command_line_method, NULL
80 };
81
82 const GDBusInterfaceInfo org_gtk_Application = {
83   -1, (gchar *) "org.gtk.Application",
84   (GDBusMethodInfo **) application_methods
85 };
86
87 static const GDBusArgInfo message_arg = { -1, (gchar *) "message", (gchar *) "s" };
88 static const GDBusArgInfo *print_in[] = { &message_arg, NULL };
89 static const GDBusArgInfo *print_out[] = { NULL };
90
91 static const GDBusMethodInfo stdout_method = {
92   -1, (gchar *) "Print",
93   (GDBusArgInfo **) print_in,
94   (GDBusArgInfo **) print_out
95 };
96
97 static const GDBusMethodInfo stderr_method = {
98   -1, (gchar *) "PrintError",
99   (GDBusArgInfo **) print_in,
100   (GDBusArgInfo **) print_out
101 };
102
103 static const GDBusMethodInfo *cmdline_methods[] = {
104   &stdout_method, &stderr_method, NULL
105 };
106
107 const GDBusInterfaceInfo org_gtk_private_Cmdline = {
108   -1, (gchar *) "org.gtk.private.CommandLine",
109   (GDBusMethodInfo **) cmdline_methods
110 };
111
112 /* GApplication implementation {{{1 */
113 struct _GApplicationImpl
114 {
115   GDBusConnection *session_bus;
116   const gchar     *bus_name;
117   gchar           *object_path;
118   guint            object_id;
119   gboolean         actions_exported;
120   gpointer         app;
121
122   GHashTable      *actions;
123   guint            signal_id;
124 };
125
126
127 static GApplicationCommandLine *
128 g_dbus_command_line_new (GDBusMethodInvocation *invocation);
129
130
131 static void
132 g_application_impl_method_call (GDBusConnection       *connection,
133                                 const gchar           *sender,
134                                 const gchar           *object_path,
135                                 const gchar           *interface_name,
136                                 const gchar           *method_name,
137                                 GVariant              *parameters,
138                                 GDBusMethodInvocation *invocation,
139                                 gpointer               user_data)
140 {
141   GApplicationImpl *impl = user_data;
142   GApplicationClass *class;
143
144   class = G_APPLICATION_GET_CLASS (impl->app);
145
146   if (strcmp (method_name, "Activate") == 0)
147     {
148       GVariant *platform_data;
149
150       g_variant_get (parameters, "(@a{sv})", &platform_data);
151       class->before_emit (impl->app, platform_data);
152       g_signal_emit_by_name (impl->app, "activate");
153       class->after_emit (impl->app, platform_data);
154       g_variant_unref (platform_data);
155
156       g_dbus_method_invocation_return_value (invocation, NULL);
157     }
158
159   else if (strcmp (method_name, "Open") == 0)
160     {
161       GVariant *platform_data;
162       const gchar *hint;
163       GVariant *array;
164       GFile **files;
165       gint n, i;
166
167       g_variant_get (parameters, "(@ass@a{sv})",
168                      &array, &hint, &platform_data);
169
170       n = g_variant_n_children (array);
171       files = g_new (GFile *, n + 1);
172
173       for (i = 0; i < n; i++)
174         {
175           const gchar *uri;
176
177           g_variant_get_child (array, i, "&s", &uri);
178           files[i] = g_file_new_for_uri (uri);
179         }
180       g_variant_unref (array);
181       files[n] = NULL;
182
183       class->before_emit (impl->app, platform_data);
184       g_signal_emit_by_name (impl->app, "open", files, n, hint);
185       class->after_emit (impl->app, platform_data);
186
187       g_variant_unref (platform_data);
188
189       for (i = 0; i < n; i++)
190         g_object_unref (files[i]);
191       g_free (files);
192
193       g_dbus_method_invocation_return_value (invocation, NULL);
194     }
195
196   else if (strcmp (method_name, "CommandLine") == 0)
197     {
198       GApplicationCommandLine *cmdline;
199       GVariant *platform_data;
200       int status;
201
202       cmdline = g_dbus_command_line_new (invocation);
203       platform_data = g_variant_get_child_value (parameters, 2);
204       class->before_emit (impl->app, platform_data);
205       g_signal_emit_by_name (impl->app, "command-line", cmdline, &status);
206       g_application_command_line_set_exit_status (cmdline, status);
207       class->after_emit (impl->app, platform_data);
208       g_variant_unref (platform_data);
209       g_object_unref (cmdline);
210     }
211   else
212     g_assert_not_reached ();
213 }
214
215 static gchar *
216 application_path_from_appid (const gchar *appid)
217 {
218   gchar *appid_path, *iter;
219
220   appid_path = g_strconcat ("/", appid, NULL);
221   for (iter = appid_path; *iter; iter++)
222     {
223       if (*iter == '.')
224         *iter = '/';
225
226       if (*iter == '-')
227         *iter = '_';
228     }
229
230   return appid_path;
231 }
232
233 void
234 g_application_impl_destroy (GApplicationImpl *impl)
235 {
236   if (impl->session_bus)
237     {
238       if (impl->object_id)
239         g_dbus_connection_unregister_object (impl->session_bus,
240                                              impl->object_id);
241       if (impl->actions_exported)
242         g_action_group_exporter_stop (impl->app);
243
244       g_dbus_connection_call (impl->session_bus,
245                               "org.freedesktop.DBus",
246                               "/org/freedesktop/DBus",
247                               "org.freedesktop.DBus",
248                               "ReleaseName",
249                               g_variant_new ("(s)",
250                                              impl->bus_name),
251                               NULL,
252                               G_DBUS_CALL_FLAGS_NONE,
253                               -1, NULL, NULL, NULL);
254
255       g_object_unref (impl->session_bus);
256       g_free (impl->object_path);
257     }
258   else
259     {
260       g_assert (impl->object_path == NULL);
261       g_assert (impl->object_id == 0);
262     }
263
264   g_slice_free (GApplicationImpl, impl);
265 }
266
267 static void
268 remote_action_info_free (gpointer user_data)
269 {
270   RemoteActionInfo *info = user_data;
271
272   g_free (info->name);
273
274   if (info->state)
275     g_variant_unref (info->state);
276
277   if (info->parameter_type)
278     g_variant_type_free (info->parameter_type);
279
280   g_slice_free (RemoteActionInfo, info);
281 }
282
283 static RemoteActionInfo *
284 remote_action_info_new_from_iter (GVariantIter *iter)
285 {
286   RemoteActionInfo *info;
287   const gchar *param_str;
288   gboolean enabled;
289   GVariant *state;
290   gchar *name;
291
292   if (!g_variant_iter_next (iter, "{&s(bg@av)}", &name,
293                             &enabled, &param_str, &state))
294     return NULL;
295
296   info = g_slice_new (RemoteActionInfo);
297   info->name = name;
298   info->enabled = enabled;
299
300   if (g_variant_n_children (state))
301     g_variant_get_child (state, 0, "v", &info->state);
302   else
303     info->state = NULL;
304   g_variant_unref (state);
305
306   if (param_str[0])
307     info->parameter_type = g_variant_type_copy ((GVariantType *) param_str);
308   else
309     info->parameter_type = NULL;
310
311   return info;
312 }
313
314 static void
315 g_application_impl_action_signal (GDBusConnection *connection,
316                                   const gchar     *sender_name,
317                                   const gchar     *object_path,
318                                   const gchar     *interface_name,
319                                   const gchar     *signal_name,
320                                   GVariant        *parameters,
321                                   gpointer         user_data)
322 {
323   GApplicationImpl *impl = user_data;
324   GActionGroup *action_group;
325
326   action_group = G_ACTION_GROUP (impl->app);
327
328   if (g_str_equal (signal_name, "Changed") &&
329       g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(asa{sb}a{sv}a{s(bgav)})")))
330     {
331       /* Removes */
332       {
333         GVariantIter *iter;
334         const gchar *name;
335
336         g_variant_get_child (parameters, 0, "as", &iter);
337         while (g_variant_iter_next (iter, "&s", &name))
338           {
339             if (g_hash_table_lookup (impl->actions, name))
340               {
341                 g_hash_table_remove (impl->actions, name);
342                 g_action_group_action_removed (action_group, name);
343               }
344           }
345         g_variant_iter_free (iter);
346       }
347
348       /* Enable changes */
349       {
350         GVariantIter *iter;
351         const gchar *name;
352         gboolean enabled;
353
354         g_variant_get_child (parameters, 1, "a{sb}", &iter);
355         while (g_variant_iter_next (iter, "{&sb}", &name, &enabled))
356           {
357             RemoteActionInfo *info;
358
359             info = g_hash_table_lookup (impl->actions, name);
360
361             if (info && info->enabled != enabled)
362               {
363                 info->enabled = enabled;
364                 g_action_group_action_enabled_changed (action_group,
365                                                        name, enabled);
366               }
367           }
368         g_variant_iter_free (iter);
369       }
370
371       /* State changes */
372       {
373         GVariantIter *iter;
374         const gchar *name;
375         GVariant *state;
376
377         g_variant_get_child (parameters, 2, "a{sv}", &iter);
378         while (g_variant_iter_next (iter, "{&sv}", &name, &state))
379           {
380             RemoteActionInfo *info;
381
382             info = g_hash_table_lookup (impl->actions, name);
383
384             if (info && info->state && !g_variant_equal (state, info->state) &&
385                 g_variant_is_of_type (state, g_variant_get_type (info->state)))
386               {
387                 g_variant_unref (info->state);
388                 info->state = g_variant_ref (state);
389
390                 g_action_group_action_state_changed (action_group, name, state);
391               }
392
393             g_variant_unref (state);
394           }
395         g_variant_iter_free (iter);
396       }
397
398       /* Additions */
399       {
400         RemoteActionInfo *info;
401         GVariantIter *iter;
402
403         g_variant_get_child (parameters, 3, "a{s(bgav)}", &iter);
404         while ((info = remote_action_info_new_from_iter (iter)))
405           {
406             if (!g_hash_table_lookup (impl->actions, info->name))
407               g_hash_table_replace (impl->actions, info->name, info);
408             else
409               remote_action_info_free (info);
410           }
411       }
412     }
413 }
414
415 GApplicationImpl *
416 g_application_impl_register (GApplication       *application,
417                              const gchar        *appid,
418                              GApplicationFlags   flags,
419                              GHashTable        **remote_actions,
420                              GCancellable       *cancellable,
421                              GError            **error)
422 {
423   const static GDBusInterfaceVTable vtable = {
424     g_application_impl_method_call
425   };
426   GApplicationImpl *impl;
427   GVariant *reply;
428   guint32 rval;
429
430   impl = g_slice_new0 (GApplicationImpl);
431
432   impl->app = application;
433   impl->bus_name = appid;
434
435   impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, cancellable, NULL);
436
437   if (impl->session_bus == NULL)
438     {
439       /* If we can't connect to the session bus, proceed as a normal
440        * non-unique application.
441        */
442       *remote_actions = NULL;
443       return impl;
444     }
445
446   impl->object_path = application_path_from_appid (appid);
447
448   /* Only try to be the primary instance if
449    * G_APPLICATION_IS_LAUNCHER was not specified.
450    */
451   if (~flags & G_APPLICATION_IS_LAUNCHER)
452     {
453       /* Attempt to become primary instance. */
454       impl->object_id =
455         g_dbus_connection_register_object (impl->session_bus,
456                                            impl->object_path,
457                                            (GDBusInterfaceInfo *)
458                                              &org_gtk_Application,
459                                            &vtable, impl, NULL, error);
460
461       if (impl->object_id == 0)
462         {
463           g_object_unref (impl->session_bus);
464           g_free (impl->object_path);
465           impl->session_bus = NULL;
466           impl->object_path = NULL;
467
468           g_slice_free (GApplicationImpl, impl);
469           return NULL;
470         }
471
472       if (!g_action_group_exporter_export (impl->session_bus,
473                                            impl->object_path,
474                                            impl->app, error))
475         {
476           g_dbus_connection_unregister_object (impl->session_bus,
477                                                impl->object_id);
478
479           g_object_unref (impl->session_bus);
480           g_free (impl->object_path);
481           impl->session_bus = NULL;
482           impl->object_path = NULL;
483
484           g_slice_free (GApplicationImpl, impl);
485           return NULL;
486         }
487       impl->actions_exported = TRUE;
488
489       /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
490       reply = g_dbus_connection_call_sync (impl->session_bus,
491                                            "org.freedesktop.DBus",
492                                            "/org/freedesktop/DBus",
493                                            "org.freedesktop.DBus",
494                                            "RequestName",
495                                            g_variant_new ("(su)",
496                                                           impl->bus_name,
497                                                           0x4),
498                                            G_VARIANT_TYPE ("(u)"),
499                                            0, -1, cancellable, error);
500
501       if (reply == NULL)
502         {
503           g_dbus_connection_unregister_object (impl->session_bus,
504                                                impl->object_id);
505           impl->object_id = 0;
506
507           g_action_group_exporter_stop (impl->app);
508           impl->actions_exported = FALSE;
509
510           g_object_unref (impl->session_bus);
511           g_free (impl->object_path);
512           impl->session_bus = NULL;
513           impl->object_path = NULL;
514
515           g_slice_free (GApplicationImpl, impl);
516           return NULL;
517         }
518
519       g_variant_get (reply, "(u)", &rval);
520       g_variant_unref (reply);
521
522       /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
523       if (rval != 3)
524         {
525           /* We are the primary instance. */
526           g_dbus_connection_emit_signal (impl->session_bus,
527                                          NULL,
528                                          impl->object_path,
529                                          "org.gtk.Application",
530                                          "Hello",
531                                          g_variant_new ("(s)",
532                                                         impl->bus_name),
533                                          NULL);
534           *remote_actions = NULL;
535           return impl;
536         }
537
538       /* We didn't make it.  Drop our service-side stuff. */
539       g_dbus_connection_unregister_object (impl->session_bus,
540                                            impl->object_id);
541       impl->object_id = 0;
542       g_action_group_exporter_stop (impl->app);
543       impl->actions_exported = FALSE;
544
545       if (flags & G_APPLICATION_IS_SERVICE)
546         {
547           g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
548                        "Unable to acquire bus name `%s'", appid);
549           g_object_unref (impl->session_bus);
550           g_free (impl->object_path);
551
552           g_slice_free (GApplicationImpl, impl);
553           return NULL;
554         }
555     }
556
557   /* We are non-primary.  Try to get the primary's list of actions.
558    * This also serves as a mechanism to ensure that the primary exists
559    * (ie: DBus service files installed correctly, etc).
560    */
561   impl->signal_id =
562     g_dbus_connection_signal_subscribe (impl->session_bus, impl->bus_name,
563                                         "org.gtk.Actions", NULL,
564                                         impl->object_path, NULL,
565                                         G_DBUS_SIGNAL_FLAGS_NONE,
566                                         g_application_impl_action_signal,
567                                         impl, NULL);
568
569   reply = g_dbus_connection_call_sync (impl->session_bus, impl->bus_name,
570                                        impl->object_path, "org.gtk.Actions",
571                                        "DescribeAll", NULL,
572                                        G_VARIANT_TYPE ("(a{s(bgav)})"),
573                                        G_DBUS_CALL_FLAGS_NONE, -1,
574                                        cancellable, error);
575
576   if (reply == NULL)
577     {
578       /* The primary appears not to exist.  Fail the registration. */
579       g_object_unref (impl->session_bus);
580       g_free (impl->object_path);
581       impl->session_bus = NULL;
582       impl->object_path = NULL;
583
584       g_slice_free (GApplicationImpl, impl);
585       return NULL;
586     }
587
588   /* Create and populate the hashtable */
589   {
590     RemoteActionInfo *info;
591     GVariant *descriptions;
592     GVariantIter iter;
593
594     impl->actions = g_hash_table_new_full (g_str_hash, g_str_equal,
595                                            NULL, remote_action_info_free);
596     descriptions = g_variant_get_child_value (reply, 0);
597     g_variant_iter_init (&iter, descriptions);
598
599     while ((info = remote_action_info_new_from_iter (&iter)))
600       g_hash_table_replace (impl->actions, info->name, info);
601
602     g_variant_unref (descriptions);
603
604     *remote_actions = impl->actions;
605   }
606
607   return impl;
608 }
609
610 void
611 g_application_impl_activate (GApplicationImpl *impl,
612                              GVariant         *platform_data)
613 {
614   g_dbus_connection_call (impl->session_bus,
615                           impl->bus_name,
616                           impl->object_path,
617                           "org.gtk.Application",
618                           "Activate",
619                           g_variant_new ("(@a{sv})", platform_data),
620                           NULL, 0, -1, NULL, NULL, NULL);
621 }
622
623 void
624 g_application_impl_open (GApplicationImpl  *impl,
625                          GFile            **files,
626                          gint               n_files,
627                          const gchar       *hint,
628                          GVariant          *platform_data)
629 {
630   GVariantBuilder builder;
631   gint i;
632
633   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})"));
634   g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
635   for (i = 0; i < n_files; i++)
636     {
637       gchar *uri = g_file_get_uri (files[i]);
638       g_variant_builder_add (&builder, "s", uri);
639       g_free (uri);
640     }
641   g_variant_builder_close (&builder);
642   g_variant_builder_add (&builder, "s", hint);
643   g_variant_builder_add_value (&builder, platform_data);
644
645   g_dbus_connection_call (impl->session_bus,
646                           impl->bus_name,
647                           impl->object_path,
648                           "org.gtk.Application",
649                           "Open",
650                           g_variant_builder_end (&builder),
651                           NULL, 0, -1, NULL, NULL, NULL);
652 }
653
654 static void
655 g_application_impl_cmdline_method_call (GDBusConnection       *connection,
656                                         const gchar           *sender,
657                                         const gchar           *object_path,
658                                         const gchar           *interface_name,
659                                         const gchar           *method_name,
660                                         GVariant              *parameters,
661                                         GDBusMethodInvocation *invocation,
662                                         gpointer               user_data)
663 {
664   const gchar *message;
665
666   g_variant_get_child (parameters, 0, "&s", &message);
667
668   if (strcmp (method_name, "Print") == 0)
669     g_print ("%s", message);
670   else if (strcmp (method_name, "PrintError") == 0)
671     g_printerr ("%s", message);
672   else
673     g_assert_not_reached ();
674
675   g_dbus_method_invocation_return_value (invocation, NULL);
676 }
677
678 typedef struct
679 {
680   GMainLoop *loop;
681   int status;
682 } CommandLineData;
683
684 static void
685 g_application_impl_cmdline_done (GObject      *source,
686                                  GAsyncResult *result,
687                                  gpointer      user_data)
688 {
689   CommandLineData *data = user_data;
690   GError *error = NULL;
691   GVariant *reply;
692
693   reply = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source),
694                                          result, &error);
695
696   if (reply != NULL)
697     {
698       g_variant_get (reply, "(i)", &data->status);
699       g_variant_unref (reply);
700     }
701
702   else
703     {
704       g_printerr ("%s\n", error->message);
705       g_error_free (error);
706       data->status = 1;
707     }
708
709   g_main_loop_quit (data->loop);
710 }
711
712 int
713 g_application_impl_command_line (GApplicationImpl  *impl,
714                                  gchar            **arguments,
715                                  GVariant          *platform_data)
716 {
717   const static GDBusInterfaceVTable vtable = {
718     g_application_impl_cmdline_method_call
719   };
720   const gchar *object_path = "/org/gtk/Application/CommandLine";
721   GMainContext *context;
722   CommandLineData data;
723   guint object_id;
724
725   context = g_main_context_new ();
726   data.loop = g_main_loop_new (context, FALSE);
727   g_main_context_push_thread_default (context);
728
729   object_id = g_dbus_connection_register_object (impl->session_bus,
730                                                  object_path,
731                                                  (GDBusInterfaceInfo *)
732                                                    &org_gtk_private_Cmdline,
733                                                  &vtable, &data, NULL, NULL);
734   /* In theory we should try other paths... */
735   g_assert (object_id != 0);
736
737   g_dbus_connection_call (impl->session_bus,
738                           impl->bus_name,
739                           impl->object_path,
740                           "org.gtk.Application",
741                           "CommandLine",
742                           g_variant_new ("(o^aay@a{sv})", object_path,
743                                          arguments, platform_data),
744                           G_VARIANT_TYPE ("(i)"), 0, G_MAXINT, NULL,
745                           g_application_impl_cmdline_done, &data);
746
747   g_main_loop_run (data.loop);
748
749   g_main_context_pop_thread_default (context);
750   g_main_context_unref (context);
751   g_main_loop_unref (data.loop);
752
753   return data.status;
754 }
755
756 void
757 g_application_impl_change_action_state (GApplicationImpl *impl,
758                                         const gchar      *action_name,
759                                         GVariant         *value,
760                                         GVariant         *platform_data)
761 {
762   g_dbus_connection_call (impl->session_bus,
763                           impl->bus_name,
764                           impl->object_path,
765                           "org.gtk.Actions",
766                           "SetState",
767                           g_variant_new ("(sv@a{sv})", action_name,
768                                          value, platform_data),
769                           NULL, 0, -1, NULL, NULL, NULL);
770 }
771
772 void
773 g_application_impl_activate_action (GApplicationImpl *impl,
774                                     const gchar      *action_name,
775                                     GVariant         *parameter,
776                                     GVariant         *platform_data)
777 {
778   GVariant *param;
779
780   if (parameter)
781     parameter = g_variant_new_variant (parameter);
782
783   param = g_variant_new_array (G_VARIANT_TYPE_VARIANT,
784                                &parameter, parameter != NULL);
785
786   g_dbus_connection_call (impl->session_bus,
787                           impl->bus_name,
788                           impl->object_path,
789                           "org.gtk.Actions",
790                           "Activate",
791                           g_variant_new ("(s@av@a{sv})", action_name,
792                                          param, platform_data),
793                           NULL, 0, -1, NULL, NULL, NULL);
794 }
795
796 void
797 g_application_impl_flush (GApplicationImpl *impl)
798 {
799   if (impl->session_bus)
800     g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
801 }
802
803
804 /* GDBusCommandLine implementation {{{1 */
805
806 typedef GApplicationCommandLineClass GDBusCommandLineClass;
807 static GType g_dbus_command_line_get_type (void);
808 typedef struct
809 {
810   GApplicationCommandLine  parent_instance;
811   GDBusMethodInvocation   *invocation;
812
813   GDBusConnection *connection;
814   const gchar     *bus_name;
815   const gchar     *object_path;
816 } GDBusCommandLine;
817
818
819 G_DEFINE_TYPE (GDBusCommandLine,
820                g_dbus_command_line,
821                G_TYPE_APPLICATION_COMMAND_LINE)
822
823 static void
824 g_dbus_command_line_print_literal (GApplicationCommandLine *cmdline,
825                                    const gchar             *message)
826 {
827   GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
828
829   g_dbus_connection_call (gdbcl->connection,
830                           gdbcl->bus_name,
831                           gdbcl->object_path,
832                           "org.gtk.private.CommandLine", "Print",
833                           g_variant_new ("(s)", message),
834                           NULL, 0, -1, NULL, NULL, NULL);
835 }
836
837 static void
838 g_dbus_command_line_printerr_literal (GApplicationCommandLine *cmdline,
839                                       const gchar             *message)
840 {
841   GDBusCommandLine *gdbcl = (GDBusCommandLine *) cmdline;
842
843   g_dbus_connection_call (gdbcl->connection,
844                           gdbcl->bus_name,
845                           gdbcl->object_path,
846                           "org.gtk.private.CommandLine", "PrintError",
847                           g_variant_new ("(s)", message),
848                           NULL, 0, -1, NULL, NULL, NULL);
849 }
850
851 static void
852 g_dbus_command_line_finalize (GObject *object)
853 {
854   GApplicationCommandLine *cmdline = G_APPLICATION_COMMAND_LINE (object);
855   GDBusCommandLine *gdbcl = (GDBusCommandLine *) object;
856   gint status;
857
858   status = g_application_command_line_get_exit_status (cmdline);
859
860   g_dbus_method_invocation_return_value (gdbcl->invocation,
861                                          g_variant_new ("(i)", status));
862   g_object_unref (gdbcl->invocation);
863
864   G_OBJECT_CLASS (g_dbus_command_line_parent_class)
865     ->finalize (object);
866 }
867
868 static void
869 g_dbus_command_line_init (GDBusCommandLine *gdbcl)
870 {
871 }
872
873 static void
874 g_dbus_command_line_class_init (GApplicationCommandLineClass *class)
875 {
876   GObjectClass *object_class = G_OBJECT_CLASS (class);
877
878   object_class->finalize = g_dbus_command_line_finalize;
879   class->printerr_literal = g_dbus_command_line_printerr_literal;
880   class->print_literal = g_dbus_command_line_print_literal;
881 }
882
883 static GApplicationCommandLine *
884 g_dbus_command_line_new (GDBusMethodInvocation *invocation)
885 {
886   GDBusCommandLine *gdbcl;
887   GVariant *args;
888
889   args = g_dbus_method_invocation_get_parameters (invocation);
890
891   gdbcl = g_object_new (g_dbus_command_line_get_type (),
892                         "arguments", g_variant_get_child_value (args, 1),
893                         "platform-data", g_variant_get_child_value (args, 2),
894                         NULL);
895   gdbcl->connection = g_dbus_method_invocation_get_connection (invocation);
896   gdbcl->bus_name = g_dbus_method_invocation_get_sender (invocation);
897   g_variant_get_child (args, 0, "&o", &gdbcl->object_path);
898   gdbcl->invocation = g_object_ref (invocation);
899
900   return G_APPLICATION_COMMAND_LINE (gdbcl);
901 }
902
903 /* Epilogue {{{1 */
904
905 /* vim:set foldmethod=marker: */