]> www.fi.muni.cz Git - evince.git/blob - shell/ev-daemon.c
[dualscreen] fix crash on ctrl+w and fix control window closing
[evince.git] / shell / ev-daemon.c
1 /* ev-daemon.c
2  *  this file is part of evince, a gnome document viewer
3  *
4  * Copyright (C) 2009 Carlos Garcia Campos  <carlosgc@gnome.org>
5  * Copyright © 2010 Christian Persch
6  *
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.
11  *
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.
16  *
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.
20  */
21
22 #include "config.h"
23
24 #include <glib.h>
25 #include <glib/gstdio.h>
26 #include <gio/gio.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <sys/wait.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33
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"
37
38 #define EV_DBUS_WINDOW_INTERFACE_NAME   "org.gnome.evince.Window"
39
40 #define DAEMON_TIMEOUT (30) /* seconds */
41
42 #define LOG g_printerr
43
44 static GList *ev_daemon_docs = NULL;
45 static guint kill_timer_id;
46 static GHashTable *pending_invocations = NULL;
47
48 typedef struct {
49         gchar *dbus_name;
50         gchar *uri;
51         guint  watch_id;
52         guint  loaded_id;
53 } EvDoc;
54
55 static void
56 ev_doc_free (EvDoc *doc)
57 {
58         if (!doc)
59                 return;
60
61         g_free (doc->dbus_name);
62         g_free (doc->uri);
63
64         g_bus_unwatch_name (doc->watch_id);
65
66         g_free (doc);
67 }
68
69 static EvDoc *
70 ev_daemon_find_doc (const gchar *uri)
71 {
72         GList *l;
73
74         for (l = ev_daemon_docs; l != NULL; l = l->next) {
75                 EvDoc *doc = (EvDoc *)l->data;
76
77                 if (strcmp (doc->uri, uri) == 0)
78                         return doc;
79         }
80
81         return NULL;
82 }
83
84 static void
85 ev_daemon_stop_killtimer (void)
86 {
87         if (kill_timer_id != 0)
88                 g_source_remove (kill_timer_id);
89         kill_timer_id = 0;
90 }
91
92 static gboolean
93 ev_daemon_shutdown (gpointer user_data)
94 {
95         GMainLoop *loop = (GMainLoop *) user_data;
96
97         LOG ("Timeout; exiting daemon.\n");
98
99         if (g_main_loop_is_running (loop))
100                 g_main_loop_quit (loop);
101
102         return FALSE;
103 }
104
105 static void
106 ev_daemon_maybe_start_killtimer (gpointer data)
107 {
108         ev_daemon_stop_killtimer ();
109         if (ev_daemon_docs != NULL)
110                 return;
111
112         kill_timer_id = g_timeout_add_seconds (DAEMON_TIMEOUT,
113                                                (GSourceFunc) ev_daemon_shutdown,
114                                                data);
115 }
116
117 static gboolean
118 spawn_evince (const gchar *uri)
119 {
120         gchar   *argv[3];
121         gboolean retval;
122         GError  *error = NULL;
123
124         /* TODO Check that the uri exists */
125         argv[0] = g_build_filename (BINDIR, "evince", NULL);
126         argv[1] = (gchar *) uri;
127         argv[2] = NULL;
128
129         retval = g_spawn_async (NULL /* wd */, argv, NULL /* env */,
130                                 0, NULL, NULL, NULL, &error);
131         if (!retval) {
132                 g_printerr ("Error spawning evince for uri %s: %s\n", uri, error->message);
133                 g_error_free (error);
134         }
135         g_free (argv[0]);
136
137         return retval;
138 }
139
140 static void
141 name_appeared_cb (GDBusConnection *connection,
142                   const gchar     *name,
143                   const gchar     *name_owner,
144                   gpointer         user_data)
145 {
146         LOG ("Watch name'%s' appeared with owner '%s'\n", name, name_owner);
147 }
148
149 static void
150 name_vanished_cb (GDBusConnection *connection,
151                   const gchar     *name,
152                   gpointer         user_data)
153 {
154         GList *l;
155
156         LOG ("Watch name'%s' disappeared\n", name);
157
158         for (l = ev_daemon_docs; l != NULL; l = l->next) {
159                 EvDoc *doc = (EvDoc *) l->data;
160
161                 if (strcmp (doc->dbus_name, name) != 0)
162                         continue;
163
164                 LOG ("Watch found URI '%s' for name; removing\n", doc->uri);
165
166                 ev_daemon_docs = g_list_delete_link (ev_daemon_docs, l);
167                 ev_doc_free (doc);
168                 
169                 ev_daemon_maybe_start_killtimer (user_data);
170                 return;
171         }
172 }
173
174 static void
175 process_pending_invocations (const gchar *uri,
176                              const gchar *dbus_name)
177 {
178         GList *l;
179         GList *uri_invocations;
180
181         LOG ("RegisterDocument process pending invocations for URI %s\n", uri);
182         uri_invocations = g_hash_table_lookup (pending_invocations, uri);
183
184         for (l = uri_invocations; l != NULL; l = l->next) {
185                 GDBusMethodInvocation *invocation;
186
187                 invocation = (GDBusMethodInvocation *)l->data;
188                 g_dbus_method_invocation_return_value (invocation,
189                                                        g_variant_new ("(s)", dbus_name));
190         }
191
192         g_list_free (uri_invocations);
193         g_hash_table_remove (pending_invocations, uri);
194 }
195
196 static void
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,
203                     EvDoc           *doc)
204 {
205         const gchar *uri;
206
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);
211 }
212
213 static void
214 method_call_cb (GDBusConnection       *connection,
215                 const gchar           *sender,
216                 const gchar           *object_path,
217                 const gchar           *interface_name,
218                 const gchar           *method_name,
219                 GVariant              *parameters,
220                 GDBusMethodInvocation *invocation,
221                 gpointer               user_data)
222 {
223         if (g_strcmp0 (interface_name, EV_DBUS_DAEMON_INTERFACE_NAME) != 0)
224                 return;
225
226         if (g_strcmp0 (method_name, "RegisterDocument") == 0) {
227                 EvDoc       *doc;
228                 const gchar *uri;
229
230                 g_variant_get (parameters, "(&s)", &uri);
231
232                 doc = ev_daemon_find_doc (uri);
233                 if (doc != NULL) {
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));
237                         return;
238                 }
239
240                 ev_daemon_stop_killtimer ();
241
242                 doc = g_new (EvDoc, 1);
243                 doc->dbus_name = g_strdup (sender);
244                 doc->uri = g_strdup (uri);
245
246                 doc->loaded_id = g_dbus_connection_signal_subscribe (connection,
247                                                                      doc->dbus_name,
248                                                                      EV_DBUS_WINDOW_INTERFACE_NAME,
249                                                                      "DocumentLoaded",
250                                                                      NULL,
251                                                                      NULL,
252                                                                      0,
253                                                                      (GDBusSignalCallback) document_loaded_cb,
254                                                                      doc,
255                                                                      NULL);
256                 doc->watch_id = g_bus_watch_name_on_connection (connection,
257                                                                 sender,
258                                                                 G_BUS_NAME_WATCHER_FLAGS_NONE,
259                                                                 name_appeared_cb,
260                                                                 name_vanished_cb,
261                                                                 user_data, NULL);
262
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);
265
266                 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", ""));
267         } else if (g_strcmp0 (method_name, "UnregisterDocument") == 0) {
268                 EvDoc *doc;
269                 const gchar *uri;
270
271                 g_variant_get (parameters, "(&s)", &uri);
272
273                 LOG ("UnregisterDocument URI '%s'\n", uri);
274
275                 doc = ev_daemon_find_doc (uri);
276                 if (doc == NULL) {
277                         LOG ("UnregisterDocument URI was not registered!\n");
278                         g_dbus_method_invocation_return_error_literal (invocation,
279                                                                        G_DBUS_ERROR,
280                                                                        G_DBUS_ERROR_INVALID_ARGS,
281                                                                        "URI not registered");
282                         return;
283                 }
284
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);
288
289                         g_dbus_method_invocation_return_error_literal (invocation,
290                                                                        G_DBUS_ERROR,
291                                                                        G_DBUS_ERROR_BAD_ADDRESS,
292                                                                        "Only owner can call this method");
293                         return;
294                 }
295
296                 ev_daemon_docs = g_list_remove (ev_daemon_docs, doc);
297                 ev_doc_free (doc);
298                 ev_daemon_maybe_start_killtimer (user_data);
299
300                 g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
301         } else if (g_strcmp0 (method_name, "FindDocument") == 0) {
302                 EvDoc *doc;
303                 const gchar *uri;
304                 gboolean spawn;
305
306                 g_variant_get (parameters, "(&sb)",  &uri, &spawn);
307
308                 LOG ("FindDocument URI '%s' \n", uri);
309
310                 doc = ev_daemon_find_doc (uri);
311                 if (doc != NULL) {
312                         g_dbus_method_invocation_return_value (invocation,
313                                                                g_variant_new ("(s)", doc->dbus_name));
314                         return;
315                 }
316
317                 if (spawn) {
318                         GList *uri_invocations;
319                         gboolean ret_val = TRUE;
320
321                         uri_invocations = g_hash_table_lookup (pending_invocations, uri);
322
323                         if (uri_invocations == NULL) {
324                                 /* Only spawn once. */
325                                 ret_val = spawn_evince (uri);
326                         }
327
328                         if (ret_val) {
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,
332                                                      g_strdup (uri),
333                                                      uri_invocations);
334                                 return;
335                         }
336                 }
337
338                 LOG ("FindDocument URI '%s' was not registered!\n", uri);
339                 g_dbus_method_invocation_return_value (invocation,
340                                                        g_variant_new ("(s)",""));
341         }
342 }
343
344 static const char introspection_xml[] =
345   "<node>"
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'/>"
350       "</method>"
351       "<method name='UnregisterDocument'>"
352         "<arg type='s' name='uri' direction='in'/>"
353       "</method>"
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'/>"
358       "</method>"
359     "</interface>"
360   "</node>";
361
362 static const GDBusInterfaceVTable interface_vtable = {
363   method_call_cb,
364   NULL,
365   NULL
366 };
367
368 static GDBusNodeInfo *introspection_data;
369
370 static void
371 bus_acquired_cb (GDBusConnection *connection,
372                  const gchar     *name,
373                  gpointer         user_data)
374 {
375         GMainLoop *loop = (GMainLoop *) user_data;
376         guint      registration_id;
377         GError    *error = NULL;
378
379         if (!introspection_data)
380                 introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
381
382         registration_id = g_dbus_connection_register_object (connection,
383                                                              EV_DBUS_DAEMON_OBJECT_PATH,
384                                                              introspection_data->interfaces[0],
385                                                              &interface_vtable,
386                                                              g_main_loop_ref (loop),
387                                                              (GDestroyNotify) g_main_loop_unref,
388                                                              &error);
389         if (registration_id == 0) {
390                 g_printerr ("Failed to register object: %s\n", error->message);
391                 g_error_free (error);
392
393                 if (g_main_loop_is_running (loop))
394                         g_main_loop_quit (loop);
395         }
396 }
397
398 static void
399 name_acquired_cb (GDBusConnection *connection,
400                   const gchar     *name,
401                   gpointer         user_data)
402 {
403         ev_daemon_maybe_start_killtimer (user_data);
404 }
405
406 static void
407 name_lost_cb (GDBusConnection *connection,
408               const gchar     *name,
409               gpointer         user_data)
410 {
411           GMainLoop *loop = (GMainLoop *) user_data;
412
413           /* Failed to acquire the name; exit daemon */
414           if (g_main_loop_is_running (loop))
415                   g_main_loop_quit (loop);
416 }
417
418 gint
419 main (gint argc, gchar **argv)
420 {
421         GMainLoop *loop;
422         guint owner_id;
423
424         g_set_prgname ("evince-daemon");
425
426         g_type_init ();
427
428         loop = g_main_loop_new (NULL, FALSE);
429
430         pending_invocations = g_hash_table_new_full (g_str_hash,
431                                                      g_str_equal,
432                                                      (GDestroyNotify)g_free,
433                                                      NULL);
434
435         owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
436                                    EV_DBUS_DAEMON_NAME,
437                                    G_BUS_NAME_OWNER_FLAGS_NONE,
438                                    bus_acquired_cb,
439                                    name_acquired_cb,
440                                    name_lost_cb,
441                                    g_main_loop_ref (loop),
442                                    (GDestroyNotify) g_main_loop_unref);
443
444         g_main_loop_run (loop);
445
446         g_bus_unown_name (owner_id);
447
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);
454
455         return 0;
456 }