2
* Copyright (C) 2010 Canonical, Ltd.
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.
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.
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/>.
17
* Authored by Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com>
24
#include <gio/gdesktopappinfo.h>
25
#include <zeitgeist.h>
27
#include "gapplaunchhandlerzeitgeist.h"
29
static ZeitgeistLog *log = NULL;
30
static gpointer log_w = NULL;
32
struct _GAppLaunchHandlerZeitgeist {
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);
41
#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init) { \
42
const GInterfaceInfo g_implement_interface_info = { \
43
(GInterfaceInitFunc) iface_init, NULL, NULL \
45
g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
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))
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.
57
* Spurious deadlocks and segfaults have been observed without
58
* lazy setup of the log.
65
log = zeitgeist_log_new ();
67
g_object_add_weak_pointer (G_OBJECT (log), &log_w);
72
g_app_launch_handler_zeitgeist_finalize (GObject *object)
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);
79
g_app_launch_handler_zeitgeist_constructor (GType type,
80
guint n_construct_properties,
81
GObjectConstructParam *construct_properties)
84
GAppLaunchHandlerZeitgeistClass *klass;
85
GObjectClass *parent_class;
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);
100
g_app_launch_handler_zeitgeist_init (GAppLaunchHandlerZeitgeist *lookup)
105
g_app_launch_handler_zeitgeist_class_finalize (GAppLaunchHandlerZeitgeistClass *klass)
111
g_app_launch_handler_zeitgeist_class_init (GAppLaunchHandlerZeitgeistClass *klass)
113
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
115
gobject_class->constructor = g_app_launch_handler_zeitgeist_constructor;
116
gobject_class->finalize = g_app_launch_handler_zeitgeist_finalize;
121
on_subject_info_ready (GFile *file,
125
ZeitgeistSubject *su;
129
gchar *uri, *parent_uri;
130
const gchar *display_name, *filesystem, *mimetype;
132
uri = g_file_get_uri (file);
135
info = g_file_query_info_finish (file, res, &error);
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);
145
g_error_free (error);
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);
156
parent_file = g_file_get_parent (file);
157
parent_uri = g_file_get_uri (parent_file);
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";
163
su = zeitgeist_subject_new_full (uri,
164
zeitgeist_interpretation_for_mimetype (mimetype),
165
zeitgeist_manifestation_for_uri (uri),
170
zeitgeist_event_add_subject (ev, su);
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);
179
g_object_unref (parent_file);
180
g_object_unref (info);
184
/* Create an application://app.desktop URI. Caller must g_free() the result
187
get_application_uri (GDesktopAppInfo *appinfo)
189
gchar *app_id = NULL;
191
app_id = g_strdup (g_app_info_get_id (G_APP_INFO (appinfo)));
195
app_id = g_strconcat ("application://", app_id, NULL);
197
else if (g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (appinfo)))
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);
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
213
log_event (ZeitgeistEvent *event)
217
g_return_val_if_fail (ZEITGEIST_IS_EVENT (event), FALSE);
218
g_return_val_if_fail (ZEITGEIST_IS_LOG (log), FALSE);
220
/* Send the event to Zeitgeist */
221
zeitgeist_log_insert_events_no_reply (log, event, NULL);
227
on_launched (GDesktopAppInfoLaunchHandler *launch_handler,
228
GDesktopAppInfo *app_info,
230
GAppLaunchContext *launch_ctx,
234
ZeitgeistSubject *su;
236
gchar *subject_uri, *actor, *prgname;
238
g_return_if_fail (G_IS_DESKTOP_APP_INFO (app_info));
240
/* Don't log stuff about apps not shown in user menus */
241
if (! g_app_info_should_show (G_APP_INFO (app_info)))
244
ev = zeitgeist_event_new ();
245
su = zeitgeist_subject_new ();
247
subject_uri = get_application_uri (app_info);
249
if (subject_uri == NULL)
251
/* We where unable to figure out the launched app... */
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) :
263
zeitgeist_event_set_actor (ev, actor);
266
zeitgeist_event_add_subject (ev, su);
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?
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);
284
launch_handler_iface_init (GDesktopAppInfoLaunchHandlerIface *iface)
286
iface->on_launched = on_launched;
290
g_app_launch_handler_zeitgeist_register (GIOModule *module)
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,
300
g_app_launch_handler_zeitgeist_unregister (GIOModule *module)
303
g_object_unref (log);