2 * this file is part of evince, a gnome document viewer
4 * Copyright (C) 2009 Carlos Garcia Campos <carlosgc@gnome.org>
5 * Copyright © 2010 Christian Persch
7 * Evince is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * Evince is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include <glib/gstdio.h>
28 #include <sys/types.h>
34 #define EV_DBUS_DAEMON_NAME "org.gnome.evince.Daemon"
35 #define EV_DBUS_DAEMON_INTERFACE_NAME "org.gnome.evince.Daemon"
36 #define EV_DBUS_DAEMON_OBJECT_PATH "/org/gnome/evince/Daemon"
38 #define EV_DBUS_WINDOW_INTERFACE_NAME "org.gnome.evince.Window"
40 #define DAEMON_TIMEOUT (30) /* seconds */
42 #define LOG g_printerr
44 static GList *ev_daemon_docs = NULL;
45 static guint kill_timer_id;
46 static GHashTable *pending_invocations = NULL;
56 ev_doc_free (EvDoc *doc)
61 g_free (doc->dbus_name);
64 g_bus_unwatch_name (doc->watch_id);
70 ev_daemon_find_doc (const gchar *uri)
74 for (l = ev_daemon_docs; l != NULL; l = l->next) {
75 EvDoc *doc = (EvDoc *)l->data;
77 if (strcmp (doc->uri, uri) == 0)
85 ev_daemon_stop_killtimer (void)
87 if (kill_timer_id != 0)
88 g_source_remove (kill_timer_id);
93 ev_daemon_shutdown (gpointer user_data)
95 GMainLoop *loop = (GMainLoop *) user_data;
97 LOG ("Timeout; exiting daemon.\n");
99 if (g_main_loop_is_running (loop))
100 g_main_loop_quit (loop);
106 ev_daemon_maybe_start_killtimer (gpointer data)
108 ev_daemon_stop_killtimer ();
109 if (ev_daemon_docs != NULL)
112 kill_timer_id = g_timeout_add_seconds (DAEMON_TIMEOUT,
113 (GSourceFunc) ev_daemon_shutdown,
118 spawn_evince (const gchar *uri)
122 GError *error = NULL;
124 /* TODO Check that the uri exists */
125 argv[0] = g_build_filename (BINDIR, "evince", NULL);
126 argv[1] = (gchar *) uri;
129 retval = g_spawn_async (NULL /* wd */, argv, NULL /* env */,
130 0, NULL, NULL, NULL, &error);
132 g_printerr ("Error spawning evince for uri %s: %s\n", uri, error->message);
133 g_error_free (error);
141 name_appeared_cb (GDBusConnection *connection,
143 const gchar *name_owner,
146 LOG ("Watch name'%s' appeared with owner '%s'\n", name, name_owner);
150 name_vanished_cb (GDBusConnection *connection,
156 LOG ("Watch name'%s' disappeared\n", name);
158 for (l = ev_daemon_docs; l != NULL; l = l->next) {
159 EvDoc *doc = (EvDoc *) l->data;
161 if (strcmp (doc->dbus_name, name) != 0)
164 LOG ("Watch found URI '%s' for name; removing\n", doc->uri);
166 ev_daemon_docs = g_list_delete_link (ev_daemon_docs, l);
169 ev_daemon_maybe_start_killtimer (user_data);
175 process_pending_invocations (const gchar *uri,
176 const gchar *dbus_name)
179 GList *uri_invocations;
181 LOG ("RegisterDocument process pending invocations for URI %s\n", uri);
182 uri_invocations = g_hash_table_lookup (pending_invocations, uri);
184 for (l = uri_invocations; l != NULL; l = l->next) {
185 GDBusMethodInvocation *invocation;
187 invocation = (GDBusMethodInvocation *)l->data;
188 g_dbus_method_invocation_return_value (invocation,
189 g_variant_new ("(s)", dbus_name));
192 g_list_free (uri_invocations);
193 g_hash_table_remove (pending_invocations, uri);
197 document_loaded_cb (GDBusConnection *connection,
198 const gchar *sender_name,
199 const gchar *object_path,
200 const gchar *interface_name,
201 const gchar *signal_name,
202 GVariant *parameters,
207 g_variant_get (parameters, "(&s)", &uri);
208 if (strcmp (uri, doc->uri) == 0)
209 process_pending_invocations (uri, sender_name);
210 g_dbus_connection_signal_unsubscribe (connection, doc->loaded_id);
214 method_call_cb (GDBusConnection *connection,
216 const gchar *object_path,
217 const gchar *interface_name,
218 const gchar *method_name,
219 GVariant *parameters,
220 GDBusMethodInvocation *invocation,
223 if (g_strcmp0 (interface_name, EV_DBUS_DAEMON_INTERFACE_NAME) != 0)
226 if (g_strcmp0 (method_name, "RegisterDocument") == 0) {
230 g_variant_get (parameters, "(&s)", &uri);
232 doc = ev_daemon_find_doc (uri);
234 LOG ("RegisterDocument found owner '%s' for URI '%s'\n", doc->dbus_name, uri);
235 g_dbus_method_invocation_return_value (invocation,
236 g_variant_new ("(s)", doc->dbus_name));
240 ev_daemon_stop_killtimer ();
242 doc = g_new (EvDoc, 1);
243 doc->dbus_name = g_strdup (sender);
244 doc->uri = g_strdup (uri);
246 doc->loaded_id = g_dbus_connection_signal_subscribe (connection,
248 EV_DBUS_WINDOW_INTERFACE_NAME,
253 (GDBusSignalCallback) document_loaded_cb,
256 doc->watch_id = g_bus_watch_name_on_connection (connection,
258 G_BUS_NAME_WATCHER_FLAGS_NONE,
263 LOG ("RegisterDocument registered owner '%s' for URI '%s'\n", doc->dbus_name, uri);
264 ev_daemon_docs = g_list_prepend (ev_daemon_docs, doc);
266 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ""));
267 } else if (g_strcmp0 (method_name, "UnregisterDocument") == 0) {
271 g_variant_get (parameters, "(&s)", &uri);
273 LOG ("UnregisterDocument URI '%s'\n", uri);
275 doc = ev_daemon_find_doc (uri);
277 LOG ("UnregisterDocument URI was not registered!\n");
278 g_dbus_method_invocation_return_error_literal (invocation,
280 G_DBUS_ERROR_INVALID_ARGS,
281 "URI not registered");
285 if (strcmp (doc->dbus_name, sender) != 0) {
286 LOG ("UnregisterDocument called by non-owner (owner '%s' sender '%s')\n",
287 doc->dbus_name, sender);
289 g_dbus_method_invocation_return_error_literal (invocation,
291 G_DBUS_ERROR_BAD_ADDRESS,
292 "Only owner can call this method");
296 ev_daemon_docs = g_list_remove (ev_daemon_docs, doc);
298 ev_daemon_maybe_start_killtimer (user_data);
300 g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
301 } else if (g_strcmp0 (method_name, "FindDocument") == 0) {
306 g_variant_get (parameters, "(&sb)", &uri, &spawn);
308 LOG ("FindDocument URI '%s' \n", uri);
310 doc = ev_daemon_find_doc (uri);
312 g_dbus_method_invocation_return_value (invocation,
313 g_variant_new ("(s)", doc->dbus_name));
318 GList *uri_invocations;
319 gboolean ret_val = TRUE;
321 uri_invocations = g_hash_table_lookup (pending_invocations, uri);
323 if (uri_invocations == NULL) {
324 /* Only spawn once. */
325 ret_val = spawn_evince (uri);
329 /* Only defer DBUS answer if evince was succesfully spawned */
330 uri_invocations = g_list_prepend (uri_invocations, invocation);
331 g_hash_table_insert (pending_invocations,
338 LOG ("FindDocument URI '%s' was not registered!\n", uri);
339 g_dbus_method_invocation_return_value (invocation,
340 g_variant_new ("(s)",""));
344 static const char introspection_xml[] =
346 "<interface name='org.gnome.evince.Daemon'>"
347 "<method name='RegisterDocument'>"
348 "<arg type='s' name='uri' direction='in'/>"
349 "<arg type='s' name='owner' direction='out'/>"
351 "<method name='UnregisterDocument'>"
352 "<arg type='s' name='uri' direction='in'/>"
354 "<method name='FindDocument'>"
355 "<arg type='s' name='uri' direction='in'/>"
356 "<arg type='b' name='spawn' direction='in'/>"
357 "<arg type='s' name='owner' direction='out'/>"
362 static const GDBusInterfaceVTable interface_vtable = {
368 static GDBusNodeInfo *introspection_data;
371 bus_acquired_cb (GDBusConnection *connection,
375 GMainLoop *loop = (GMainLoop *) user_data;
376 guint registration_id;
377 GError *error = NULL;
379 if (!introspection_data)
380 introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
382 registration_id = g_dbus_connection_register_object (connection,
383 EV_DBUS_DAEMON_OBJECT_PATH,
384 introspection_data->interfaces[0],
386 g_main_loop_ref (loop),
387 (GDestroyNotify) g_main_loop_unref,
389 if (registration_id == 0) {
390 g_printerr ("Failed to register object: %s\n", error->message);
391 g_error_free (error);
393 if (g_main_loop_is_running (loop))
394 g_main_loop_quit (loop);
399 name_acquired_cb (GDBusConnection *connection,
403 ev_daemon_maybe_start_killtimer (user_data);
407 name_lost_cb (GDBusConnection *connection,
411 GMainLoop *loop = (GMainLoop *) user_data;
413 /* Failed to acquire the name; exit daemon */
414 if (g_main_loop_is_running (loop))
415 g_main_loop_quit (loop);
419 main (gint argc, gchar **argv)
424 g_set_prgname ("evince-daemon");
428 loop = g_main_loop_new (NULL, FALSE);
430 pending_invocations = g_hash_table_new_full (g_str_hash,
432 (GDestroyNotify)g_free,
435 owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
437 G_BUS_NAME_OWNER_FLAGS_NONE,
441 g_main_loop_ref (loop),
442 (GDestroyNotify) g_main_loop_unref);
444 g_main_loop_run (loop);
446 g_bus_unown_name (owner_id);
448 g_main_loop_unref (loop);
449 if (introspection_data)
450 g_dbus_node_info_unref (introspection_data);
451 g_list_foreach (ev_daemon_docs, (GFunc)ev_doc_free, NULL);
452 g_list_free (ev_daemon_docs);
453 g_hash_table_destroy (pending_invocations);