← Back to team overview

zeitgeist team mailing list archive

GIO And the Magical Patch: A Zeitgeist Story

 

Zeitgeist Developers,

There is an amazing amount of activity surrounding the happenings of
Zeitgeist. Lots of excitement going on and obviously Zeitgeist will
continue to be an important part of the linux experience in the coming
years. Zeitgeist has not been without issues, there have been two major
ones I can think of.

- The API is too complex
- The information quality is fairly low without lots of item providers

The API issue has been largely addressed with the 0.3 release and I hope
the API can be stabilized almost completely at this point. The
information quality issue has also received considerable attention,
however I believe more can be done.

In short I believe the problem with Zeitgeist is it works largely at the
application level. Getting individual applications to provide
information to Zeitgeist, or watching applications through wnck.
Instead, I believe a lot more can be achieved at the toolkit level with
a relatively small patch (or perhaps set of patches) to GIO and
libgnome-desktop.

By patching these parts of the toolkit, we can be notified easily of
every file the user intentionally opens, every applications they launch,
every process they use, quickly and efficiently with very little code. I
have attached a GIO patch that adds a new extension point into GIO that
allows a loadable GIO Module to track all launches of .desktop files.
This patch is early and should be considered beta quality, however it
provides the needed interface to begin writing a GIO module that will be
extremely useful to Zeitgeist. I have also included a skeleton (and very
quickly written) GIO module that essentially does nothing using this
extension point.

I believe there to be good reason to look into getting this patch
upstreamed, and I believe it can be done in a reasonable time frame. The
benefit to Zeitgeist really could be amazing.

Jason Smith <jason.smith@xxxxxxxxxxxxx>
*** a/gio/gdesktopappinfo.c	2009-10-16 23:57:29.436561111 -0400
--- b/gio/gdesktopappinfo.c	2009-10-17 00:06:28.716984254 -0400
***************
*** 69,74 ****
--- 69,78 ----
  static void     mime_info_cache_reload                (const char       *dir);
  static gboolean g_desktop_app_info_ensure_saved       (GDesktopAppInfo  *info,
  						       GError          **error);
+ static void 
+ g_desktop_app_info_launch_handler_on_launched (GDesktopAppInfoLaunchHandler *launch_handler,
+                                                const char                   *desktop_file_path,
+                                                gint                         pid);
  
  /**
   * GDesktopAppInfo:
***************
*** 859,864 ****
--- 863,907 ----
      g_setenv ("DESKTOP_STARTUP_ID", data->sn_id, TRUE);
  }
  
+ static void
+ g_desktop_app_info_on_launched (const char *desktop_file_path, gint pid)
+ {
+   static gsize lookup = 0;
+   
+   if (g_once_init_enter (&lookup))
+     {
+       gsize setup_value = 1;
+       GDesktopAppInfoLaunchHandler *lookup_instance;
+       GIOExtensionPoint *ep;
+       GIOExtension *extension;
+       GList *l;
+ 
+       /* Ensure vfs in modules loaded */
+       _g_io_modules_ensure_loaded ();
+       
+       ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LAUNCH_HANDLER_EXTENSION_POINT_NAME);
+       
+       lookup_instance = NULL;
+       for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
+         {
+           extension = l->data;
+           lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
+           if (lookup_instance != NULL)
+             break;
+ 	}
+ 
+       if (lookup_instance != NULL)
+ 	setup_value = (gsize)lookup_instance;
+      
+       g_once_init_leave (&lookup, setup_value);
+     }
+ 
+   if (lookup == 1)
+     return;
+ 
+   g_desktop_app_info_launch_handler_on_launched (G_DESKTOP_APP_INFO_LAUNCH_HANDLER (lookup), desktop_file_path, pid);
+ }
+ 
  static gboolean
  g_desktop_app_info_launch_uris (GAppInfo           *appinfo,
  				GList              *uris,
***************
*** 910,922 ****
  	  g_list_free (launched_files);
  	}
  
        if (!g_spawn_async (info->path,
  			  argv,
  			  NULL,
  			  G_SPAWN_SEARCH_PATH,
  			  child_setup,
  			  &data,
! 			  NULL,
  			  error))
  	{
  	  if (data.sn_id)
--- 953,966 ----
  	  g_list_free (launched_files);
  	}
  
+       gint pid;
        if (!g_spawn_async (info->path,
  			  argv,
  			  NULL,
  			  G_SPAWN_SEARCH_PATH,
  			  child_setup,
  			  &data,
! 			  &pid,
  			  error))
  	{
  	  if (data.sn_id)
***************
*** 927,932 ****
--- 971,980 ----
  
  	  goto out;
  	}
+       else
+         {
+           g_desktop_app_info_on_launched (info->filename, pid);
+         }
  
        g_free (data.sn_id);
        g_free (data.display);
***************
*** 2692,2696 ****
--- 2740,2806 ----
    return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
  }
  
+ /* GDesktopAppInfoLaunchHandler interface: */
+ 
+ static void g_desktop_app_info_launch_handler_base_init (gpointer g_class);
+ static void g_desktop_app_info_launch_handler_class_init (gpointer g_class,
+ 						  gpointer class_data);
+ 
+ GType
+ g_desktop_app_info_launch_handler_get_type (void)
+ {
+   static volatile gsize g_define_type_id__volatile = 0;
+ 
+   if (g_once_init_enter (&g_define_type_id__volatile))
+     {
+       const GTypeInfo desktop_app_info_launch_handler_info =
+       {
+         sizeof (GDesktopAppInfoLaunchHandlerIface), /* class_size */
+ 	g_desktop_app_info_launch_handler_base_init,   /* base_init */
+ 	NULL,		/* base_finalize */
+ 	g_desktop_app_info_launch_handler_class_init,
+ 	NULL,		/* class_finalize */
+ 	NULL,		/* class_data */
+ 	0,
+ 	0,              /* n_preallocs */
+ 	NULL
+       };
+       GType g_define_type_id =
+ 	g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLaunchHandler"),
+ 			&desktop_app_info_launch_handler_info, 0);
+ 
+       g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
+ 
+       g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
+     }
+ 
+   return g_define_type_id__volatile;
+ }
+ 
+ static void
+ g_desktop_app_info_launch_handler_class_init (gpointer g_class,
+ 				      gpointer class_data)
+ {
+ }
+ 
+ static void
+ g_desktop_app_info_launch_handler_base_init (gpointer g_class)
+ {
+ }
+ 
+ static void 
+ g_desktop_app_info_launch_handler_on_launched (GDesktopAppInfoLaunchHandler *launch_handler,
+                                                const char                   *desktop_file_path,
+                                                gint                         pid)
+ {
+   GDesktopAppInfoLaunchHandlerIface *iface;
+   
+   g_return_if_fail (G_IS_DESKTOP_APP_INFO_LAUNCH_HANDLER (launch_handler));
+ 
+   iface = G_DESKTOP_APP_INFO_LAUNCH_HANDLER_GET_IFACE (launch_handler);
+ 
+   (* iface->on_launched) (launch_handler, desktop_file_path, pid);
+ }
+ 
  #define __G_DESKTOP_APP_INFO_C__
  #include "gioaliasdef.c"

*** a/gio/gdesktopappinfo.h	2009-03-31 19:04:20.000000000 -0400
--- b/gio/gdesktopappinfo.h	2009-07-21 20:34:35.565657314 -0400
***************
*** 89,94 ****
--- 89,123 ----
  GAppInfo *g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
                                                                  const char            *uri_scheme);
  
+ 
+ 
+ #define G_TYPE_DESKTOP_APP_INFO_LAUNCH_HANDLER           (g_desktop_app_info_launch_handler_get_type ())
+ #define G_DESKTOP_APP_INFO_LAUNCH_HANDLER(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_DESKTOP_APP_INFO_LAUNCH_HANDLER, GDesktopAppInfoLaunchHandler))
+ #define G_IS_DESKTOP_APP_INFO_LAUNCH_HANDLER(obj)	 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_DESKTOP_APP_INFO_LAUNCH_HANDLER))
+ #define G_DESKTOP_APP_INFO_LAUNCH_HANDLER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_DESKTOP_APP_INFO_LAUNCH_HANDLER, GDesktopAppInfoLaunchHandlerIface))
+ 
+ /**
+  * G_DESKTOP_APP_INFO_LAUNCH_HANDLER_EXTENSION_POINT_NAME:
+  *
+  * Extension point for default handler to launching. See
+  * <link linkend="extending-gio">Extending GIO</link>.
+  */
+ #define G_DESKTOP_APP_INFO_LAUNCH_HANDLER_EXTENSION_POINT_NAME "gio-desktop-app-info-launch-handler"
+ 
+ typedef struct _GDesktopAppInfoLaunchHandler GDesktopAppInfoLaunchHandler;
+ typedef struct _GDesktopAppInfoLaunchHandlerIface GDesktopAppInfoLaunchHandlerIface;
+ 
+ struct _GDesktopAppInfoLaunchHandlerIface
+ {
+   GTypeInterface g_iface;
+ 
+   void (* on_launched) (GDesktopAppInfoLaunchHandler *launch_handler,
+                         const char                   *desktop_file_path,
+                         gint                         pid);
+ };
+ 
+ GType     g_desktop_app_info_launch_handler_get_type                   (void) G_GNUC_CONST;
+ 
  G_END_DECLS
  
  #endif /* __G_DESKTOP_APP_INFO_H__ */

*** a/gio/giomodule.c	2009-03-31 19:04:20.000000000 -0400
--- b/gio/giomodule.c	2009-07-21 20:40:32.738157079 -0400
***************
*** 312,317 ****
--- 312,320 ----
  #ifdef G_OS_UNIX
        ep = g_io_extension_point_register (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
        g_io_extension_point_set_required_type (ep, G_TYPE_DESKTOP_APP_INFO_LOOKUP);
+       
+       ep = g_io_extension_point_register (G_DESKTOP_APP_INFO_LAUNCH_HANDLER_EXTENSION_POINT_NAME);
+       g_io_extension_point_set_required_type (ep, G_TYPE_DESKTOP_APP_INFO_LAUNCH_HANDLER);
  #endif
        
        ep = g_io_extension_point_register (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME);
//  
//  Copyright (C) 2009 Canonical Ltd.
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
// 
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 

#include <config.h>

#include <glib.h>
#include <gio/gio.h>

#include "gapplaunchhandlerexample.h"


struct _GAppLaunchHandlerExample {
  GObject parent;

};

static void launch_handler_iface_init (GDesktopAppInfoLaunchHandlerIface *iface);
static void g_app_launch_handler_example_finalize (GObject *object);

#define _G_IMPLEMENT_INTERFACE_DYNAMIC(TYPE_IFACE, iface_init)       { \
  const GInterfaceInfo g_implement_interface_info = { \
    (GInterfaceInitFunc) iface_init, NULL, NULL \
  }; \
  g_type_module_add_interface (type_module, g_define_type_id, TYPE_IFACE, &g_implement_interface_info); \
}

G_DEFINE_DYNAMIC_TYPE_EXTENDED (GAppLaunchHandlerExample, g_app_launch_handler_example, G_TYPE_OBJECT, 0,
                                _G_IMPLEMENT_INTERFACE_DYNAMIC (G_TYPE_DESKTOP_APP_INFO_LAUNCH_HANDLER,
                                                                launch_handler_iface_init))

static void
g_app_launch_handler_example_finalize (GObject *object)
{
  if (G_OBJECT_CLASS (g_app_launch_handler_example_parent_class)->finalize)
    (*G_OBJECT_CLASS (g_app_launch_handler_example_parent_class)->finalize) (object);
}

static GObject *
g_app_launch_handler_example_constructor (GType                  type,
                         	       guint                  n_construct_properties,
                         	       GObjectConstructParam *construct_properties)
{
  GObject *object;
  GAppLaunchHandlerExampleClass *klass;
  GObjectClass *parent_class;  

  object = NULL;

  /* Invoke parent constructor. */
  klass = G_APP_LAUNCH_HANDLER_EXAMPLE_CLASS (g_type_class_peek (G_TYPE_APP_LAUNCH_HANDLER_EXAMPLE));
  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
  object = parent_class->constructor (type,
                                      n_construct_properties,
                                      construct_properties);

  return object;
}

static void
g_app_launch_handler_example_init (GAppLaunchHandlerExample *lookup)
{
}

static void
g_app_launch_handler_example_class_finalize (GAppLaunchHandlerExampleClass *klass)
{
}


static void
g_app_launch_handler_example_class_init (GAppLaunchHandlerExampleClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  gobject_class->constructor = g_app_launch_handler_example_constructor;
  gobject_class->finalize = g_app_launch_handler_example_finalize;
}

static void
on_launched (GDesktopAppInfoLaunchHandler *launch_handler,
             const char  *desktop_file_path,
             gint pid)
{
	/* Perform Magic Here */
}

static void
launch_handler_iface_init (GDesktopAppInfoLaunchHandlerIface *iface)
{
  iface->on_launched = on_launched;
}

void 
g_app_launch_handler_example_register (GIOModule *module)
{
  g_app_launch_handler_example_register_type (G_TYPE_MODULE (module));
  g_io_extension_point_implement (G_DESKTOP_APP_INFO_LAUNCH_HANDLER_EXTENSION_POINT_NAME,
				  G_TYPE_APP_LAUNCH_HANDLER_EXAMPLE,
				  "example",
				  10);
}
//  
//  Copyright (C) 2009 Canonical Ltd.
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
// 
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 

#ifndef __G_APP_LAUNCH_HANDLER_EXAMPLE_H__
#define __G_APP_LAUNCH_HANDLER_EXAMPLE_H__

#include <glib-object.h>
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>


G_BEGIN_DECLS

#define G_TYPE_APP_LAUNCH_HANDLER_EXAMPLE        (g_app_launch_handler_example_get_type ())
#define G_APP_LAUNCH_HANDLER_EXAMPLE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_APP_LAUNCH_HANDLER_EXAMPLE, GAppLaunchHandlerExample))
#define G_APP_LAUNCH_HANDLER_EXAMPLE_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_APP_LAUNCH_HANDLER_EXAMPLE, GAppLaunchHandlerExampleClass))
#define G_IS_APP_LAUNCH_HANDLER_EXAMPLE(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_APP_LAUNCH_HANDLER_EXAMPLE))
#define G_IS_APP_LAUNCH_HANDLER_EXAMPLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_APP_LAUNCH_HANDLER_EXAMPLE))

typedef struct _GAppLaunchHandlerExample GAppLaunchHandlerExample;
typedef struct _GAppLaunchHandlerExampleClass GAppLaunchHandlerExampleClass;

struct _GAppLaunchHandlerExampleClass {
  GObjectClass parent_class;
};

GType g_app_launch_handler_example_get_type (void) G_GNUC_CONST;
void  g_app_launch_handler_example_register (GIOModule *module);

G_END_DECLS

#endif /* __G_APP_LAUNCH_HANDLER_EXAMPLE_H__ */

Follow ups