~mhr3/libzeitgeist/fix-reconnect

« back to all changes in this revision

Viewing changes to module/gapplaunchhandlerzeitgeist.c

  • Committer: Mikkel Kamstrup Erlandsen
  • Date: 2011-02-09 12:19:51 UTC
  • mfrom: (190.1.1 libzeitgeist-gnome3)
  • Revision ID: mikkel.kamstrup@gmail.com-20110209121951-x3ab4xm5y3hfyk7k
Merge Michal's branch, lp:~mhr3/libzeitgeist/various-fixes, removing the giomodule. The same functionality has been implemented in lp:zeitgeist-datahub trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2010 Canonical, Ltd.
3
 
 *
4
 
 * This library is free software; you can redistribute it and/or modify
5
 
 * it under the terms of the GNU Lesser General Public License
6
 
 * version 3.0 as published by the Free Software Foundation.
7
 
 *
8
 
 * This library is distributed in the hope that it will be useful,
9
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
 * GNU Lesser General Public License version 3.0 for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU Lesser General Public
14
 
 * License along with this library. If not, see
15
 
 * <http://www.gnu.org/licenses/>.
16
 
 *
17
 
 * Authored by Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com>
18
 
 */
19
 
 
20
 
#include <config.h>
21
 
 
22
 
#include <glib.h>
23
 
#include <gio/gio.h>
24
 
#include <gio/gdesktopappinfo.h>
25
 
#include <zeitgeist.h>
26
 
 
27
 
#include "gapplaunchhandlerzeitgeist.h"
28
 
 
29
 
static ZeitgeistLog *log = NULL;
30
 
static gpointer      log_w = NULL;
31
 
 
32
 
struct _GAppLaunchHandlerZeitgeist {
33
 
  GObject parent;
34
 
 
35
 
};
36
 
 
37
 
static void launch_handler_iface_init (GDesktopAppInfoLaunchHandlerIface *iface);
38
 
static void g_app_launch_handler_zeitgeist_finalize (GObject *object);
39
 
static void ensure_log (void);
40
 
 
41
 
#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
42
 
  const GInterfaceInfo g_implement_interface_info = { \
43
 
    (GInterfaceInitFunc) iface_init, NULL, NULL \
44
 
  }; \
45
 
  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
46
 
}
47
 
 
48
 
G_DEFINE_DYNAMIC_TYPE_EXTENDED (GAppLaunchHandlerZeitgeist, g_app_launch_handler_zeitgeist, G_TYPE_OBJECT, 0,
49
 
                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_DESKTOP_APP_INFO_LAUNCH_HANDLER,
50
 
                                                                launch_handler_iface_init))
51
 
 
52
 
/* We lazily create the ZeitgeistLog lazily because it creates a dbus
53
 
 * connection, which if set up during the GIOModule registration phase
54
 
 * (ie inside g_app_launch_handler_zeitgeist_register()) might mess up the
55
 
 * gio module initialization process.
56
 
 *
57
 
 * Spurious deadlocks and segfaults have been observed without
58
 
 * lazy setup of the log.
59
 
 */
60
 
static void
61
 
ensure_log (void)
62
 
{
63
 
  if (log_w == NULL)
64
 
    {
65
 
      log = zeitgeist_log_new ();
66
 
      log_w = log;
67
 
      g_object_add_weak_pointer (G_OBJECT (log), &log_w);
68
 
    }
69
 
}
70
 
 
71
 
static void
72
 
g_app_launch_handler_zeitgeist_finalize (GObject *object)
73
 
{
74
 
  if (G_OBJECT_CLASS (g_app_launch_handler_zeitgeist_parent_class)->finalize)
75
 
    (*G_OBJECT_CLASS (g_app_launch_handler_zeitgeist_parent_class)->finalize) (object);
76
 
}
77
 
 
78
 
static GObject *
79
 
g_app_launch_handler_zeitgeist_constructor (GType                  type,
80
 
                                            guint                  n_construct_properties,
81
 
                                            GObjectConstructParam *construct_properties)
82
 
{
83
 
  GObject *object;
84
 
  GAppLaunchHandlerZeitgeistClass *klass;
85
 
  GObjectClass *parent_class;
86
 
 
87
 
  object = NULL;
88
 
 
89
 
  /* Invoke parent constructor. */
90
 
  klass = G_APP_LAUNCH_HANDLER_ZEITGEIST_CLASS (g_type_class_peek (G_TYPE_APP_LAUNCH_HANDLER_ZEITGEIST));
91
 
  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
92
 
  object = parent_class->constructor (type,
93
 
                                      n_construct_properties,
94
 
                                      construct_properties);
95
 
 
96
 
  return object;
97
 
}
98
 
 
99
 
static void
100
 
g_app_launch_handler_zeitgeist_init (GAppLaunchHandlerZeitgeist *lookup)
101
 
{
102
 
}
103
 
 
104
 
static void
105
 
g_app_launch_handler_zeitgeist_class_finalize (GAppLaunchHandlerZeitgeistClass *klass)
106
 
{
107
 
}
108
 
 
109
 
 
110
 
static void
111
 
g_app_launch_handler_zeitgeist_class_init (GAppLaunchHandlerZeitgeistClass *klass)
112
 
{
113
 
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
114
 
 
115
 
  gobject_class->constructor = g_app_launch_handler_zeitgeist_constructor;
116
 
  gobject_class->finalize = g_app_launch_handler_zeitgeist_finalize;
117
 
}
118
 
 
119
 
/*
120
 
static void
121
 
on_subject_info_ready (GFile *file,
122
 
                       GAsyncResult *res,
123
 
                       ZeitgeistEvent *ev)
124
 
{
125
 
  ZeitgeistSubject *su;
126
 
  GError           *error;
127
 
  GFileInfo        *info;
128
 
  GFile            *parent_file;
129
 
  gchar            *uri, *parent_uri;
130
 
  const gchar      *display_name, *filesystem, *mimetype;
131
 
 
132
 
  uri = g_file_get_uri (file);
133
 
  
134
 
  error = NULL;
135
 
  info = g_file_query_info_finish (file, res, &error);
136
 
  if (error != NULL)
137
 
    {
138
 
      // On anything but a file-not-found we complain in the log
139
 
      if (error->code != G_IO_ERROR_NOT_FOUND)
140
 
        g_warning ("Error querying file info for '%s': %s",
141
 
                   uri, error->message);
142
 
 
143
 
      g_free (uri);
144
 
      g_object_unref (ev);
145
 
      g_error_free (error);
146
 
      return;
147
 
    }
148
 
 
149
 
  display_name = g_file_info_get_attribute_string (info,
150
 
                                                   G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME);
151
 
  filesystem = g_file_info_get_attribute_string (info,
152
 
                                                 G_FILE_ATTRIBUTE_ID_FILESYSTEM);
153
 
  mimetype = g_file_info_get_attribute_string (info,
154
 
                                               G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
155
 
 
156
 
  parent_file = g_file_get_parent (file);
157
 
  parent_uri = g_file_get_uri (parent_file);
158
 
  
159
 
  // If we couldn't query the filesystem for the subject assume it was a
160
 
  // residing online somwhere
161
 
  filesystem = filesystem != NULL ? filesystem : "net";
162
 
 
163
 
  su = zeitgeist_subject_new_full (uri,
164
 
                                   zeitgeist_interpretation_for_mimetype (mimetype),
165
 
                                   zeitgeist_manifestation_for_uri (uri),
166
 
                                   mimetype,
167
 
                                   parent_uri,
168
 
                                   display_name,
169
 
                                   filesystem);
170
 
  zeitgeist_event_add_subject (ev, su);
171
 
 
172
 
  // FIXME: Right now we actually insert one event for each subject.
173
 
  //        We should rather collect all subject data (async) and
174
 
  //        insert one event with all the subjects
175
 
  zeitgeist_log_insert_events_no_reply (log, ev, NULL);
176
 
 
177
 
  g_free (uri);
178
 
  g_free (parent_uri);
179
 
  g_object_unref (parent_file);
180
 
  g_object_unref (info);
181
 
}
182
 
*/
183
 
 
184
 
/* Create an application://app.desktop URI. Caller must g_free() the result
185
 
 * if it's != NULL */
186
 
static gchar*
187
 
get_application_uri (GDesktopAppInfo              *appinfo)
188
 
{
189
 
  gchar *app_id = NULL;
190
 
 
191
 
  app_id = g_strdup (g_app_info_get_id (G_APP_INFO (appinfo)));
192
 
 
193
 
  if (app_id != NULL)
194
 
  {
195
 
    app_id = g_strconcat ("application://", app_id, NULL);
196
 
  }
197
 
  else if (g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (appinfo)))
198
 
  {
199
 
    const gchar *path = g_desktop_app_info_get_filename (
200
 
                                                G_DESKTOP_APP_INFO (appinfo));
201
 
    gchar *basename = g_path_get_basename (path);
202
 
    app_id = g_strconcat ("application://", basename, NULL);
203
 
    g_free (basename);
204
 
  }
205
 
 
206
 
  return app_id;
207
 
}
208
 
 
209
 
/* Idle handler for logging the app launch event. We do this in an
210
 
 * idle call to make sure it's done in the mainloop in order to be
211
 
 * thread safe */
212
 
static gboolean
213
 
log_event (ZeitgeistEvent *event)
214
 
{
215
 
  ensure_log ();
216
 
 
217
 
  g_return_val_if_fail (ZEITGEIST_IS_EVENT (event), FALSE);
218
 
  g_return_val_if_fail (ZEITGEIST_IS_LOG (log), FALSE);
219
 
 
220
 
  /* Send the event to Zeitgeist */
221
 
  zeitgeist_log_insert_events_no_reply (log, event, NULL);
222
 
  
223
 
  return FALSE;
224
 
}
225
 
 
226
 
static void
227
 
on_launched (GDesktopAppInfoLaunchHandler *launch_handler,
228
 
             GDesktopAppInfo              *app_info,
229
 
             GList                        *uris,
230
 
             GAppLaunchContext            *launch_ctx,
231
 
             gint                          pid)
232
 
{
233
 
  ZeitgeistEvent   *ev;
234
 
  ZeitgeistSubject *su;
235
 
  GFile            *file;
236
 
  gchar            *subject_uri, *actor, *prgname;
237
 
  
238
 
  g_return_if_fail (G_IS_DESKTOP_APP_INFO (app_info));
239
 
 
240
 
  /* Don't log stuff about apps not shown in user menus */
241
 
  if (! g_app_info_should_show (G_APP_INFO (app_info)))
242
 
    return;
243
 
 
244
 
  ev = zeitgeist_event_new ();
245
 
  su = zeitgeist_subject_new ();
246
 
 
247
 
  subject_uri = get_application_uri (app_info);
248
 
 
249
 
  if (subject_uri == NULL)
250
 
  {
251
 
    /* We where unable to figure out the launched app... */
252
 
    g_object_unref (ev);
253
 
    g_object_unref (su);
254
 
    return;
255
 
  }
256
 
 
257
 
  /* Set event metadata */
258
 
  prgname = g_get_prgname ();
259
 
  zeitgeist_event_set_interpretation (ev, ZEITGEIST_ZG_ACCESS_EVENT);
260
 
  zeitgeist_event_set_manifestation (ev, ZEITGEIST_ZG_USER_ACTIVITY);
261
 
  actor = prgname ? g_strdup_printf ("application://%s.desktop", prgname) :
262
 
                    g_strdup ("");
263
 
  zeitgeist_event_set_actor (ev, actor);
264
 
  g_free (actor);
265
 
  
266
 
  zeitgeist_event_add_subject (ev, su);
267
 
  
268
 
  zeitgeist_subject_set_uri (su, subject_uri);
269
 
  zeitgeist_subject_set_interpretation (su, ZEITGEIST_NFO_SOFTWARE);
270
 
  zeitgeist_subject_set_manifestation (su, ZEITGEIST_NFO_SOFTWARE_ITEM);
271
 
  zeitgeist_subject_set_mimetype (su, "application/x-desktop");
272
 
  zeitgeist_subject_set_text (su, g_app_info_get_display_name (
273
 
                                                       G_APP_INFO (app_info)));
274
 
  g_free (subject_uri);
275
 
  // FIXME: subject origin and storage?
276
 
 
277
 
  /* Do all DBus interactions in an idle handler to make sure
278
 
   * we are thread safe. The floating ref on ev swallowed by idle
279
 
   * handler log_event() */
280
 
  g_idle_add ((GSourceFunc) log_event, ev);
281
 
}
282
 
 
283
 
static void
284
 
launch_handler_iface_init (GDesktopAppInfoLaunchHandlerIface *iface)
285
 
{
286
 
  iface->on_launched = on_launched;
287
 
}
288
 
 
289
 
void
290
 
g_app_launch_handler_zeitgeist_register (GIOModule *module)
291
 
{
292
 
  g_app_launch_handler_zeitgeist_register_type (G_TYPE_MODULE (module));
293
 
  g_io_extension_point_implement (G_DESKTOP_APP_INFO_LAUNCH_HANDLER_EXTENSION_POINT_NAME,
294
 
                                  G_TYPE_APP_LAUNCH_HANDLER_ZEITGEIST,
295
 
                                  "zeitgeist",
296
 
                                  10);
297
 
}
298
 
 
299
 
void
300
 
g_app_launch_handler_zeitgeist_unregister (GIOModule *module)
301
 
{
302
 
  if (log_w != NULL)
303
 
    g_object_unref (log);
304
 
}