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