Simplify variant builder
[platform/upstream/glib.git] / gio / gdbusapplication.c
1 /* GIO - GLib Input, Output and Streaming Library
2  *
3  * Copyright © 2010 Red Hat, Inc
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General
16  * Public License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
18  * Boston, MA 02111-1307, USA.
19  *
20  * Authors: Colin Walters <walters@verbum.org>
21  */
22
23 #define G_APPLICATION_IFACE "org.gtk.Application"
24
25 static void
26 application_dbus_method_call (GDBusConnection       *connection,
27                               const gchar           *sender,
28                               const gchar           *object_path,
29                               const gchar           *interface_name,
30                               const gchar           *method_name,
31                               GVariant              *parameters,
32                               GDBusMethodInvocation *invocation,
33                               gpointer               user_data)
34 {
35   GApplication *app = G_APPLICATION (user_data);
36
37   if (method_name == NULL && *method_name == '\0')
38     return;
39
40   if (strcmp (method_name, "Quit") == 0)
41     {
42       guint32 timestamp;
43       g_variant_get (parameters, "(u)", &timestamp);
44
45       g_dbus_method_invocation_return_value (invocation, NULL);
46
47       g_application_quit (app, timestamp);
48     }
49   else if (strcmp (method_name, "ListActions") == 0)
50     {
51       GHashTableIter iter;
52       GApplicationAction *value;
53       GVariantBuilder builder;
54
55       g_variant_builder_init (&builder, G_VARIANT_TYPE ("(a{s(sb)})"));
56       g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{s(sb)}"));
57       g_hash_table_iter_init (&iter, app->priv->actions);
58       while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&value))
59         g_variant_builder_add (&builder, "{s(sb)}",
60                                value->name,
61                                value->description ? value->description : "",
62                                value->enabled);
63       g_variant_builder_close (&builder);
64
65       g_dbus_method_invocation_return_value (invocation,
66                                              g_variant_builder_end (&builder));
67     }
68   else if (strcmp (method_name, "InvokeAction") == 0)
69     {
70       const char *action_name;
71       guint32 timestamp;
72       GApplicationAction *action;
73
74       g_variant_get (parameters, "(&su)", &action_name, &timestamp);
75
76       action = g_hash_table_lookup (app->priv->actions, action_name);
77
78       if (!action)
79         {
80           char *errmsg  = g_strdup_printf ("Invalid action: %s", action_name);
81           g_dbus_method_invocation_return_dbus_error (invocation, G_APPLICATION_IFACE ".InvalidAction", errmsg);
82           g_free (errmsg);
83           return;
84         }
85
86       g_signal_emit (app, application_signals[ACTION], g_quark_from_string (action_name), action_name, (guint)timestamp);
87
88       g_dbus_method_invocation_return_value (invocation, NULL);
89     }
90   else if (strcmp (method_name, "Activate") == 0)
91     {
92       GVariant *args;
93       GVariant *platform_data;
94
95       g_variant_get (parameters, "(@aay@a{sv})", &args, &platform_data);
96
97       g_signal_emit (app, application_signals[PREPARE_ACTIVATION], 0, args, platform_data);
98
99       g_variant_unref (args);
100       g_variant_unref (platform_data);
101
102       g_dbus_method_invocation_return_value (invocation, NULL);
103     }
104 }
105
106 static const GDBusArgInfo application_quit_in_args[] =
107 {
108   {
109     -1,
110     "timestamp",
111     "u",
112     NULL
113   }
114 };
115
116 static const GDBusArgInfo * const application_quit_in_args_p[] = {
117   &application_quit_in_args[0],
118   NULL
119 };
120
121 static const GDBusArgInfo application_list_actions_out_args[] =
122 {
123   {
124     -1,
125     "actions",
126     "a{s(sb)}",
127     NULL
128   }
129 };
130
131 static const GDBusArgInfo * const application_list_actions_out_args_p[] = {
132   &application_list_actions_out_args[0],
133   NULL
134 };
135
136 static const GDBusArgInfo application_invoke_action_in_args[] =
137 {
138   {
139     -1,
140     "action",
141     "s",
142     NULL
143   },
144   {
145     -1,
146     "timestamp",
147     "u",
148     NULL
149   }
150 };
151
152 static const GDBusArgInfo * const application_invoke_action_in_args_p[] = {
153   &application_invoke_action_in_args[0],
154   &application_invoke_action_in_args[1],
155   NULL
156 };
157
158 static const GDBusMethodInfo application_quit_method_info =
159 {
160   -1,
161   "Quit",
162   (GDBusArgInfo **) &application_quit_in_args_p,
163   NULL,
164   NULL
165 };
166
167 static const GDBusMethodInfo application_list_actions_method_info =
168 {
169   -1,
170   "ListActions",
171   NULL,
172   (GDBusArgInfo **) &application_list_actions_out_args_p,
173   NULL
174 };
175
176 static const GDBusMethodInfo application_invoke_action_method_info =
177 {
178   -1,
179   "InvokeAction",
180   (GDBusArgInfo **) &application_invoke_action_in_args_p,
181   NULL,
182   NULL
183 };
184
185 static const GDBusArgInfo application_activate_in_args[] =
186 {
187   {
188     -1,
189     "arguments",
190     "aay",
191     NULL
192   },
193   {
194     -1,
195     "data",
196     "a{sv}",
197     NULL
198   }
199 };
200
201 static const GDBusArgInfo * const application_activate_in_args_p[] = {
202   &application_activate_in_args[0],
203   &application_activate_in_args[1],
204   NULL
205 };
206
207 static const GDBusMethodInfo application_activate_method_info =
208 {
209   -1,
210   "Activate",
211   (GDBusArgInfo **) &application_activate_in_args_p,
212   NULL,
213   NULL
214 };
215
216 static const GDBusMethodInfo * const application_dbus_method_info_p[] =
217 {
218   &application_quit_method_info,
219   &application_list_actions_method_info,
220   &application_invoke_action_method_info,
221   &application_activate_method_info,
222   NULL
223 };
224
225 static const GDBusSignalInfo application_dbus_signal_info[] =
226 {
227   {
228     -1,
229     "ActionsChanged",
230     NULL,
231     NULL
232   }
233 };
234
235 static const GDBusSignalInfo * const application_dbus_signal_info_p[] = {
236   &application_dbus_signal_info[0],
237   NULL
238 };
239
240 static const GDBusInterfaceInfo application_dbus_interface_info =
241 {
242   -1,
243   G_APPLICATION_IFACE,
244   (GDBusMethodInfo **) application_dbus_method_info_p,
245   (GDBusSignalInfo **) application_dbus_signal_info_p,
246   NULL,
247 };
248
249 static GDBusInterfaceVTable application_dbus_vtable =
250 {
251   application_dbus_method_call,
252   NULL,
253   NULL
254 };
255
256 static gchar *
257 application_path_from_appid (const gchar *appid)
258 {
259   gchar *appid_path, *iter;
260
261
262   appid_path = g_strconcat ("/", appid, NULL);
263   for (iter = appid_path; *iter; iter++)
264     {
265       if (*iter == '.')
266         *iter = '/';
267     }
268
269   return appid_path;
270 }
271
272 static void
273 ensure_bus (GApplication *app)
274 {
275   GError *error = NULL;
276
277   if (app->priv->session_bus == NULL)
278     app->priv->session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
279   if (app->priv->session_bus == NULL)
280     {
281       g_error ("%s", error->message);
282       g_error_free (error);
283     }
284
285   if (app->priv->dbus_path == NULL)
286     app->priv->dbus_path = application_path_from_appid (app->priv->appid);
287 }
288
289 static void
290 _g_application_platform_init (GApplication *app)
291 {
292   GError *error = NULL;
293   guint registration_id;
294
295   ensure_bus (app);
296
297   registration_id = g_dbus_connection_register_object (app->priv->session_bus,
298                                                        app->priv->dbus_path,
299                                                        &application_dbus_interface_info,
300                                                        &application_dbus_vtable,
301                                                        app, NULL,
302                                                        &error);
303   if (registration_id == 0)
304     {
305       g_error ("%s", error->message);
306       g_error_free (error);
307     }
308 }
309
310 static gboolean
311 _g_application_platform_acquire_single_instance (GApplication  *app,
312                                                  GError       **error)
313 {
314   GVariant *request_result;
315   guint32 request_status;
316
317   ensure_bus (app);
318   if (app->priv->session_bus == NULL)
319     return FALSE;
320
321   request_result = g_dbus_connection_call_sync (app->priv->session_bus,
322                                                 "org.freedesktop.DBus",
323                                                 "/org/freedesktop/DBus",
324                                                 "org.freedesktop.DBus",
325                                                 "RequestName",
326                                                 g_variant_new ("(su)", app->priv->appid, 0x4),
327                                                 NULL, 0, -1, NULL, error);
328
329   if (request_result == NULL)
330     return FALSE;
331
332   if (g_variant_is_of_type (request_result, G_VARIANT_TYPE ("(u)")))
333     g_variant_get (request_result, "(u)", &request_status);
334   else
335     request_status = 0;
336
337   g_variant_unref (request_result);
338
339   if (request_status != 1 && request_status != 4)
340     {
341       if (request_status == 3)
342         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Another process has name \"%s\"", app->priv->appid);
343       else
344         g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown error");
345
346       return FALSE;
347     }
348
349   return TRUE;
350 }
351
352 static void
353 _g_application_platform_on_actions_changed (GApplication *app)
354 {
355   g_dbus_connection_emit_signal (app->priv->session_bus, NULL,
356                                  app->priv->dbus_path,
357                                  G_APPLICATION_IFACE,
358                                  "ActionsChanged", NULL, NULL);
359 }
360
361 static void
362 _g_application_platform_remote_invoke_action (GApplication  *app,
363                                               const gchar   *action,
364                                               guint          timestamp)
365 {
366   GVariant *result;
367
368   ensure_bus (app);
369
370   result = g_dbus_connection_call_sync (app->priv->session_bus,
371                                         app->priv->appid,
372                                         app->priv->dbus_path,
373                                         G_APPLICATION_IFACE,
374                                         "InvokeAction",
375                                         g_variant_new ("(su)",
376                                                        action,
377                                                        timestamp),
378                                         NULL, 0, -1, NULL, NULL);
379   if (result)
380     g_variant_unref (result);
381 }
382
383 static void
384 _g_application_platform_remote_quit (GApplication *app,
385                                      guint         timestamp)
386 {
387   GVariant *result;
388
389   ensure_bus (app);
390
391   result = g_dbus_connection_call_sync (app->priv->session_bus,
392                                         app->priv->appid,
393                                         app->priv->dbus_path,
394                                         G_APPLICATION_IFACE,
395                                         "Quit",
396                                         g_variant_new ("(u)",
397                                                        timestamp),
398                                         NULL, 0, -1, NULL, NULL);
399   if (result)
400     g_variant_unref (result);
401 }
402
403 static void
404 _g_application_platform_activate (GApplication *app,
405                                   GVariant     *data)
406 {
407   GVariant *result;
408
409   ensure_bus (app);
410
411   result = g_dbus_connection_call_sync (app->priv->session_bus,
412                                         app->priv->appid,
413                                         app->priv->dbus_path,
414                                         G_APPLICATION_IFACE,
415                                         "Activate",
416                                         data,
417                                         NULL, 0, -1, NULL, NULL);
418
419   if (result)
420     g_variant_unref (result);
421
422   exit (0);
423 }