new GApplication implementation
[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 "gapplication.h"
25 #include "gfile.h"
26 #include "gdbusconnection.h"
27 #include "gdbusintrospection.h"
28 #include "gdbuserror.h"
29
30 #include <string.h>
31
32
33 #include "gapplicationimpl-dbus-interface.c"
34
35 struct _GApplicationImpl
36 {
37   GDBusConnection *session_bus;
38   const gchar     *bus_name;
39   gchar           *object_path;
40   guint            object_id;
41   gpointer         app;
42
43   GMainLoop       *cmdline_mainloop;
44 };
45
46 static void
47 g_application_impl_method_call (GDBusConnection       *connection,
48                                 const gchar           *sender,
49                                 const gchar           *object_path,
50                                 const gchar           *interface_name,
51                                 const gchar           *method_name,
52                                 GVariant              *parameters,
53                                 GDBusMethodInvocation *invocation,
54                                 gpointer               user_data)
55 {
56   GApplicationImpl *impl = user_data;
57   GApplicationClass *class;
58
59   class = G_APPLICATION_GET_CLASS (impl->app);
60
61   if (strcmp (method_name, "Activate") == 0)
62     {
63       GVariant *platform_data;
64
65       g_variant_get (parameters, "(@a{sv})", &platform_data);
66       class->before_emit (impl->app, platform_data);
67       g_signal_emit_by_name (impl->app, "activate");
68       class->after_emit (impl->app, platform_data);
69       g_variant_unref (platform_data);
70     }
71
72   else if (strcmp (method_name, "Open") == 0)
73     {
74       GVariant *platform_data;
75       const gchar *hint;
76       GVariant *array;
77       GFile **files;
78       gint n, i;
79
80       g_variant_get (parameters, "(@ass@a{sv})",
81                      &array, &hint, &platform_data);
82
83       n = g_variant_n_children (array);
84       files = g_new (GFile *, n + 1);
85
86       for (i = 0; i < n; i++)
87         {
88           const gchar *uri;
89
90           g_variant_get_child (array, i, "&s", &uri);
91           files[i] = g_file_new_for_uri (uri);
92         }
93       g_variant_unref (array);
94       files[n] = NULL;
95
96       class->before_emit (impl->app, platform_data);
97       g_signal_emit_by_name (impl->app, "open", files, n, hint);
98       class->after_emit (impl->app, platform_data);
99
100       g_variant_unref (platform_data);
101
102       for (i = 0; i < n; i++)
103         g_object_unref (files[i]);
104       g_free (files);
105     }
106
107   else
108     g_assert_not_reached ();
109 }
110
111 static gchar *
112 application_path_from_appid (const gchar *appid)
113 {
114   gchar *appid_path, *iter;
115
116   appid_path = g_strconcat ("/", appid, NULL);
117   for (iter = appid_path; *iter; iter++)
118     {
119       if (*iter == '.')
120         *iter = '/';
121     }
122
123   return appid_path;
124 }
125
126 void
127 g_application_impl_destroy (GApplicationImpl *impl)
128 {
129   if (impl->session_bus)
130     {
131       if (impl->object_id)
132         g_dbus_connection_unregister_object (impl->session_bus,
133                                              impl->object_id);
134
135       g_object_unref (impl->session_bus);
136       g_free (impl->object_path);
137     }
138   else
139     {
140       g_assert (impl->object_path == NULL);
141       g_assert (impl->object_id == 0);
142     }
143
144   g_slice_free (GApplicationImpl, impl);
145 }
146
147 GApplicationImpl *
148 g_application_impl_register (GApplication       *application,
149                              const gchar        *appid,
150                              GApplicationFlags   flags,
151                              gboolean           *is_remote,
152                              GCancellable       *cancellable,
153                              GError            **error)
154 {
155   const static GDBusInterfaceVTable vtable = {
156     g_application_impl_method_call
157   };
158   GApplicationImpl *impl;
159   GVariant *reply;
160   guint32 rval;
161
162   impl = g_slice_new (GApplicationImpl);
163
164   impl->app = application;
165   impl->bus_name = appid;
166
167   impl->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION,
168                                       cancellable, error);
169
170   if (impl->session_bus == NULL)
171     {
172       g_slice_free (GApplicationImpl, impl);
173       return NULL;
174     }
175
176   impl->object_path = application_path_from_appid (appid);
177
178   if (flags & G_APPLICATION_FLAGS_IS_LAUNCHER)
179     {
180       impl->object_id = 0;
181       *is_remote = TRUE;
182
183       return impl;
184     }
185
186   impl->object_id = g_dbus_connection_register_object (impl->session_bus,
187                                                        impl->object_path,
188                                                        (GDBusInterfaceInfo *)
189                                                          &org_gtk_Application,
190                                                        &vtable,
191                                                        impl, NULL,
192                                                        error);
193
194   if (impl->object_id == 0)
195     {
196       g_object_unref (impl->session_bus);
197       g_free (impl->object_path);
198       impl->session_bus = NULL;
199       impl->object_path = NULL;
200
201       g_slice_free (GApplicationImpl, impl);
202       return NULL;
203     }
204
205   reply = g_dbus_connection_call_sync (impl->session_bus,
206                                        "org.freedesktop.DBus",
207                                        "/org/freedesktop/DBus",
208                                        "org.freedesktop.DBus",
209                                        "RequestName",
210                                        g_variant_new ("(su)",
211                                        /* DBUS_NAME_FLAG_DO_NOT_QUEUE: 0x4 */
212                                                       impl->bus_name, 0x4),
213                                        G_VARIANT_TYPE ("(u)"),
214                                        0, -1, cancellable, error);
215
216   if (reply == NULL)
217     {
218       g_dbus_connection_unregister_object (impl->session_bus,
219                                            impl->object_id);
220       impl->object_id = 0;
221
222       g_object_unref (impl->session_bus);
223       g_free (impl->object_path);
224       impl->session_bus = NULL;
225       impl->object_path = NULL;
226
227       g_slice_free (GApplicationImpl, impl);
228       return NULL;
229     }
230
231   g_variant_get (reply, "(u)", &rval);
232   g_variant_unref (reply);
233
234   /* DBUS_REQUEST_NAME_REPLY_EXISTS: 3 */
235   if ((*is_remote = (rval == 3)))
236     {
237       g_dbus_connection_unregister_object (impl->session_bus,
238                                            impl->object_id);
239       impl->object_id = 0;
240
241       if (flags & G_APPLICATION_FLAGS_IS_SERVICE)
242         {
243           g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
244                        "Unable to acquire bus name `%s'", appid);
245           g_object_unref (impl->session_bus);
246           g_free (impl->object_path);
247
248           g_slice_free (GApplicationImpl, impl);
249           impl = NULL;
250         }
251     }
252
253   return impl;
254 }
255
256 void
257 g_application_impl_activate (GApplicationImpl *impl,
258                              GVariant         *platform_data)
259 {
260   g_dbus_connection_call (impl->session_bus,
261                           impl->bus_name,
262                           impl->object_path,
263                           "org.gtk.Application",
264                           "Activate",
265                           g_variant_new ("(@a{sv})", platform_data),
266                           NULL, 0, -1, NULL, NULL, NULL);
267 }
268
269 void
270 g_application_impl_open (GApplicationImpl  *impl,
271                          GFile            **files,
272                          gint               n_files,
273                          const gchar       *hint,
274                          GVariant          *platform_data)
275 {
276   GVariantBuilder builder;
277   gint i;
278
279   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(assa{sv})"));
280   g_variant_builder_open (&builder, G_VARIANT_TYPE_STRING_ARRAY);
281   for (i = 0; i < n_files; i++)
282     {
283       gchar *uri = g_file_get_uri (files[i]);
284       g_variant_builder_add (&builder, "s", uri);
285       g_free (uri);
286     }
287   g_variant_builder_close (&builder);
288   g_variant_builder_add (&builder, "s", hint);
289   g_variant_builder_add_value (&builder, platform_data);
290
291   g_dbus_connection_call (impl->session_bus,
292                           impl->bus_name,
293                           impl->object_path,
294                           "org.gtk.Application",
295                           "Open",
296                           g_variant_builder_end (&builder),
297                           NULL, 0, -1, NULL, NULL, NULL);
298 }
299
300 void
301 g_application_impl_flush (GApplicationImpl *impl)
302 {
303   g_dbus_connection_flush_sync (impl->session_bus, NULL, NULL);
304 }