Commit e5ff300e authored by Benedikt Meurer's avatar Benedikt Meurer

2005-08-02 Benedikt Meurer <benny@xfce.org>

	* configure.in.in: Add check for the FAM/Gamin library.
	* thunar-vfs/Makefile.am, thunar-vfs/thunar-vfs-monitor.{c,h}: Redesign
	  the VFS monitor to use FAM if available. It also provides an interface
	  to feed the monitor with external events, which will be used by the
	  VFS jobs, which know for sure that they changed/created/deleted a
	  file. The interface is not yet implemented.
	* thunar/thunar-file.{c,h}: Add a virtual method reload(), which allows
	  external entities to trigger a reload on a ThunarFile.
	* thunar/thunar-local-file.c: Implement the reload() method.
	* thunar/thunar-local-file.c, thunar/thunar-local-folder.c: Add support
	  for the new VFS monitor.




(Old svn revision: 16432)
parent 0c77a5e2
2005-08-02 Benedikt Meurer <benny@xfce.org>
* configure.in.in: Add check for the FAM/Gamin library.
* thunar-vfs/Makefile.am, thunar-vfs/thunar-vfs-monitor.{c,h}: Redesign
the VFS monitor to use FAM if available. It also provides an interface
to feed the monitor with external events, which will be used by the
VFS jobs, which know for sure that they changed/created/deleted a
file. The interface is not yet implemented.
* thunar/thunar-file.{c,h}: Add a virtual method reload(), which allows
external entities to trigger a reload on a ThunarFile.
* thunar/thunar-local-file.c: Implement the reload() method.
* thunar/thunar-local-file.c, thunar/thunar-local-folder.c: Add support
for the new VFS monitor.
2005-08-01 Benedikt Meurer <benny@xfce.org>
* thunar/thunar-list-model.{c,h}: Add new method
......
......@@ -6,7 +6,9 @@ dnl
dnl Written for Thunar by Benedikt Meurer <benny@xfce.org>.
dnl
dnl Version information
dnl ***************************
dnl *** Version information ***
dnl ***************************
m4_define([thunar_version_major], [0])
m4_define([thunar_version_minor], [0])
m4_define([thunar_version_micro], [2])
......@@ -14,7 +16,9 @@ m4_define([thunar_version_build], [@REVISION@])
m4_define([thunar_version_tag], [svn])
m4_define([thunar_version], [thunar_version_major().thunar_version_minor().thunar_version_micro()ifelse(thunar_version_tag(), [], [], [thunar_version_tag()-thunar_version_build()])])
dnl Initialize autoconf
dnl ***************************
dnl *** Initialize autoconf ***
dnl ***************************
AC_COPYRIGHT([Copyright (c) 2004-2005
The Thunar development team. All rights reserved.
......@@ -24,39 +28,78 @@ AC_PREREQ([2.50])
AC_CANONICAL_TARGET()
AC_REVISION([$Id$])
dnl Initialize automake
dnl ***************************
dnl *** Initialize automake ***
dnl ***************************
AM_INIT_AUTOMAKE([AC_PACKAGE_TARNAME()], [AC_PACKAGE_VERSION()])
AM_CONFIG_HEADER([config.h])
AM_MAINTAINER_MODE()
dnl check for UNIX variants
dnl *******************************
dnl *** Check for UNIX variants ***
dnl *******************************
AC_AIX()
AC_ISC_POSIX()
AC_MINIX()
dnl check for basic programs
dnl ********************************
dnl *** Check for basic programs ***
dnl ********************************
AC_PROG_CC()
AC_PROG_LD()
AC_PROG_INSTALL()
AC_PROG_LIBTOOL()
dnl Check for standard headers
dnl **********************************
dnl *** Check for standard headers ***
dnl **********************************
AC_CHECK_HEADERS([dirent.h errno.h fcntl.h fstab.h grp.h locale.h \
math.h memory.h pwd.h \
stdlib.h string.h sys/cdio.h sys/event.h \
sys/mount.h sys/stat.h sys/time.h sys/param.h time.h])
dnl Check for standard functions
dnl ************************************
dnl *** Check for standard functions ***
dnl ************************************
AC_CHECK_FUNCS([kqueue lchmod localtime_r readdir_r setgroupent setpassent])
dnl Check for required packages
dnl ***********************************
dnl *** Check for required packages ***
dnl ***********************************
XDT_CHECK_PACKAGE([EXO], [exo-0.3], [0.3.1])
XDT_CHECK_PACKAGE([GTHREAD], [gthread-2.0], [2.6.0])
dnl Check for optional packages
dnl ***********************************
dnl *** Check for optional packages ***
dnl ***********************************
XDT_CHECK_OPTIONAL_PACKAGE([CAIRO], [cairo], [0.5], [cairo], [Cairo])
dnl Check for the system flavour
dnl *********************
dnl *** Check for FAM ***
dnl *********************
LIBFAM_CFLAGS=""
LIBFAM_LIBS=""
AC_CHECK_HEADERS([fam.h],
[
AC_CHECK_LIB([fam], [FAMOpen],
[
dnl Nice, we can use FAM
AC_DEFINE([HAVE_LIBFAM], [1], [Define if the File Alteration Monitor is available])
LIBFAM_LIBS="-lfam"
dnl Check for FAMNoExists (gamin only)
save_LIBS="$LIBS"
LIBS="$LIBS $LIBFAM_LIBS"
AC_CHECK_FUNCS([FAMNoExists])
LIBS="$save_LIBS"
])
])
AC_SUBST([LIBFAM_CFLAGS])
AC_SUBST([LIBFAM_LIBS])
dnl ************************************
dnl *** Check for the system flavour ***
dnl ************************************
AC_MSG_CHECKING([for system flavour])
case "$target_os" in
*bsd*)
......@@ -68,14 +111,18 @@ case "$target_os" in
esac
AC_MSG_RESULT([$FLAVOUR])
dnl Create links for the volume manager implementation
dnl **********************************************************
dnl *** Create links for the volume manager implementation ***
dnl **********************************************************
AC_CONFIG_LINKS(
[
thunar-vfs/thunar-vfs-volume-impl.c:thunar-vfs/thunar-vfs-volume-$FLAVOUR.c
thunar-vfs/thunar-vfs-volume-impl.h:thunar-vfs/thunar-vfs-volume-$FLAVOUR.h
])
dnl Check for debugging support
dnl ***********************************
dnl *** Check for debugging support ***
dnl ***********************************
AC_ARG_ENABLE([debug], AC_HELP_STRING([--disable-debug], [Disable debugging support]), [], [enable_debug=yes])
AC_MSG_CHECKING([whether to enable debugging support])
if test x"$enable_debug" != x"no"; then
......
......@@ -63,7 +63,8 @@ libthunar_vfs_la_SOURCES = \
libthunar_vfs_la_CFLAGS = \
$(EXO_CFLAGS) \
$(GTHREAD_CFLAGS)
$(GTHREAD_CFLAGS) \
$(LIBFAM_CFLAGS)
libthunar_vfs_la_LDFLAGS = \
-no-undefined
......@@ -74,7 +75,8 @@ libthunar_vfs_la_DEPENDENCIES = \
libthunar_vfs_la_LIBADD = \
$(top_builddir)/thunar-vfs/xdgmime/libxdgmime.la \
$(EXO_LIBS) \
$(GTHREADS_LIBS)
$(GTHREADS_LIBS) \
$(LIBFAM_LIBS)
EXTRA_DIST = \
thunar-vfs-marshal.list \
......
......@@ -22,47 +22,27 @@
#include <config.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_EVENT_H
#include <sys/event.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#ifdef HAVE_FAM_H
#include <fam.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <gdk/gdk.h>
#include <thunar-vfs/thunar-vfs-monitor.h>
/* the monitor timer interval */
#define THUNAR_VFS_MONITOR_INTERVAL (4 * 1000)
typedef struct _ThunarVfsWatch ThunarVfsWatch;
static void thunar_vfs_monitor_class_init (ThunarVfsMonitorClass *klass);
static void thunar_vfs_monitor_init (ThunarVfsMonitor *monitor);
static void thunar_vfs_monitor_finalize (GObject *object);
static gboolean thunar_vfs_monitor_event (GIOChannel *source,
GIOCondition condition,
gpointer user_data);
static gboolean thunar_vfs_monitor_timer (gpointer user_data);
static void thunar_vfs_monitor_timer_destroy (gpointer user_data);
static void thunar_vfs_monitor_class_init (ThunarVfsMonitorClass *klass);
static void thunar_vfs_monitor_init (ThunarVfsMonitor *monitor);
static void thunar_vfs_monitor_finalize (GObject *object);
#ifdef HAVE_LIBFAM
static void thunar_vfs_monitor_fam_cancel (ThunarVfsMonitor *monitor);
static void thunar_vfs_monitor_fam_event (ThunarVfsMonitor *monitor,
const FAMEvent *fe);
static gboolean thunar_vfs_monitor_fam_watch (GIOChannel *channel,
GIOCondition condition,
gpointer user_data);
#endif
......@@ -75,21 +55,25 @@ struct _ThunarVfsMonitor
{
GObject __parent__;
GList *watches;
gint current_id;
gint timer_id;
GMemChunk *handle_chunk;
GList *handles;
gint kq;
gint kq_watch_id;
#ifdef HAVE_LIBFAM
FAMConnection fc;
gint fc_watch_id;
gint fc_sequence;
#endif
};
struct _ThunarVfsWatch
struct _ThunarVfsMonitorHandle
{
gint id;
gint fd;
ThunarVfsInfo *info;
ThunarVfsMonitorCallback callback;
gpointer user_data;
ThunarVfsURI *uri;
#ifdef HAVE_LIBFAM
FAMRequest fr;
#endif
};
......@@ -112,35 +96,29 @@ thunar_vfs_monitor_class_init (ThunarVfsMonitorClass *klass)
static void
thunar_vfs_monitor_init (ThunarVfsMonitor *monitor)
{
GIOChannel *channel;
monitor->current_id = 0;
monitor->timer_id = g_timeout_add_full (G_PRIORITY_LOW, THUNAR_VFS_MONITOR_INTERVAL,
thunar_vfs_monitor_timer, monitor,
thunar_vfs_monitor_timer_destroy);
/* open a connection to the system's event queue */
#ifdef HAVE_KQUEUE
monitor->kq = kqueue ();
#else
monitor->kq = -1;
#endif
if (G_LIKELY (monitor->kq >= 0))
#ifdef HAVE_LIBFAM
if (FAMOpen2 (&monitor->fc, PACKAGE_NAME) == 0)
{
/* make sure the connection is not passed on to child processes */
fcntl (monitor->kq, F_SETFD, fcntl (monitor->kq, F_GETFD, 0) | FD_CLOEXEC);
GIOChannel *channel;
/* setup an io channel for the connection */
channel = g_io_channel_unix_new (monitor->kq);
monitor->kq_watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP | G_IO_IN,
thunar_vfs_monitor_event, monitor);
channel = g_io_channel_unix_new (FAMCONNECTION_GETFD (&monitor->fc));
monitor->fc_watch_id = g_io_add_watch (channel, G_IO_ERR | G_IO_HUP | G_IO_IN,
thunar_vfs_monitor_fam_watch, monitor);
g_io_channel_unref (channel);
#ifdef HAVE_FAMNOEXISTS
/* luckily gamin offers a way to avoid the FAMExists events */
FAMNoExists (&monitor->fc);
#endif
}
else
{
monitor->kq_watch_id = -1;
monitor->fc_watch_id = -1;
}
#endif
/* allocate the memory chunk for the handles */
monitor->handle_chunk = g_mem_chunk_create (ThunarVfsMonitorHandle, 64, G_ALLOC_AND_FREE);
}
......@@ -149,240 +127,158 @@ static void
thunar_vfs_monitor_finalize (GObject *object)
{
ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (object);
ThunarVfsWatch *watch;
GList *lp;
/* drop the still present (previously deleted) watches */
for (lp = monitor->watches; lp != NULL; lp = lp->next)
{
watch = lp->data;
if (G_UNLIKELY (watch->callback != NULL))
{
g_error ("Tried to finalize a ThunarVfsMonitor, which has "
"atleast one active watch monitoring \"%s\"",
thunar_vfs_uri_to_string (watch->info->uri, 0));
}
#ifdef HAVE_KQUEUE
if (G_LIKELY (watch->fd >= 0))
{
/* prepare the delete command */
struct kevent ev;
ev.ident = watch->fd;
ev.filter = EVFILT_VNODE;
ev.flags = EV_DELETE;
ev.fflags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE;
/* perform the delete operation */
if (kevent (monitor->kq, &ev, 1, NULL, 0, NULL) < 0)
g_error ("Failed to remove watch %d: %s", watch->id, g_strerror (errno));
/* drop the file descriptor */
close (watch->fd);
}
#ifdef HAVE_LIBFAM
if (monitor->fc_watch_id >= 0)
thunar_vfs_monitor_fam_cancel (monitor);
#endif
g_free (watch);
}
g_list_free (monitor->watches);
/* drop all handles */
for (lp = monitor->handles; lp != NULL; lp = lp->next)
thunar_vfs_uri_unref (((ThunarVfsMonitorHandle *) lp->data)->uri);
g_list_free (monitor->handles);
/* release the memory chunk */
g_mem_chunk_destroy (monitor->handle_chunk);
(*G_OBJECT_CLASS (thunar_vfs_monitor_parent_class)->finalize) (object);
}
/* stop the timer */
if (G_LIKELY (monitor->timer_id >= 0))
g_source_remove (monitor->timer_id);
/* remove the main loop watch for the event loop */
if (G_LIKELY (monitor->kq_watch_id >= 0))
g_source_remove (monitor->kq_watch_id);
#ifdef HAVE_LIBFAM
static void
thunar_vfs_monitor_fam_cancel (ThunarVfsMonitor *monitor)
{
g_return_if_fail (THUNAR_VFS_IS_MONITOR (monitor));
g_return_if_fail (monitor->fc_watch_id >= 0);
/* destroy the event queue */
if (G_LIKELY (monitor->kq >= 0))
close (monitor->kq);
/* close the FAM connection */
FAMClose (&monitor->fc);
G_OBJECT_CLASS (thunar_vfs_monitor_parent_class)->finalize (object);
/* remove the I/O watch */
g_source_remove (monitor->fc_watch_id);
monitor->fc_watch_id = -1;
}
static gboolean
thunar_vfs_monitor_event (GIOChannel *source,
GIOCondition condition,
gpointer user_data)
static void
thunar_vfs_monitor_fam_event (ThunarVfsMonitor *monitor,
const FAMEvent *fe)
{
#if 0
#ifdef HAVE_KQUEUE
ThunarVfsInfoResult result;
ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (user_data);
ThunarVfsWatch *watch;
struct kevent events[16];
gint n;
GDK_THREADS_ENTER ();
n = kevent (monitor->kq, NULL, 0, events, G_N_ELEMENTS (events), NULL);
while (--n >= 0)
ThunarVfsMonitorHandle *handle;
ThunarVfsMonitorEvent event;
ThunarVfsURI *uri;
GList *lp;
/* lookup the handle for the request that caused the event */
for (lp = monitor->handles; lp != NULL; lp = lp->next)
if (((ThunarVfsMonitorHandle *) lp->data)->fr.reqnum == fe->fr.reqnum)
break;
/* yes, this can really happen, see
* the FAM documentation.
*/
if (G_UNLIKELY (lp == NULL))
return;
else
handle = lp->data;
/* translate the event code */
switch (fe->code)
{
watch = events[n].udata;
if (G_UNLIKELY (watch->callback == NULL))
continue;
case FAMChanged:
event = THUNAR_VFS_MONITOR_EVENT_CHANGED;
break;
case FAMCreated:
event = THUNAR_VFS_MONITOR_EVENT_CREATED;
break;
/* force an update on the info */
result = 0; /*thunar_vfs_info_update (watch->info, NULL);*/
switch (result)
{
case THUNAR_VFS_INFO_RESULT_ERROR:
watch->callback (monitor, THUNAR_VFS_MONITOR_DELETED, watch->info, watch->user_data);
break;
case THUNAR_VFS_INFO_RESULT_CHANGED:
watch->callback (monitor, THUNAR_VFS_MONITOR_CHANGED, watch->info, watch->user_data);
break;
case THUNAR_VFS_INFO_RESULT_NOCHANGE:
break;
default:
g_assert_not_reached ();
break;
}
case FAMDeleted:
event = THUNAR_VFS_MONITOR_EVENT_DELETED;
break;
default:
g_assert_not_reached ();
return;
}
GDK_THREADS_LEAVE ();
#endif
#endif
/* determine the URI */
if (G_UNLIKELY (*fe->filename != '/'))
uri = thunar_vfs_uri_relative (handle->uri, fe->filename);
else if (strcmp (thunar_vfs_uri_get_path (handle->uri), fe->filename) == 0)
uri = thunar_vfs_uri_ref (handle->uri);
else
uri = thunar_vfs_uri_new_for_path (fe->filename);
/* keep the kqueue monitor going */
return TRUE;
/* invoke the callback, fortunately - due to our design - we
* have no reentrancy issues here. ;-)
*/
(*handle->callback) (monitor, handle, event, handle->uri, uri, handle->user_data);
thunar_vfs_uri_unref (uri);
}
static gboolean
thunar_vfs_monitor_timer (gpointer user_data)
thunar_vfs_monitor_fam_watch (GIOChannel *channel,
GIOCondition condition,
gpointer user_data)
{
#if 0
ThunarVfsInfoResult result;
ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (user_data);
ThunarVfsWatch *watch;
GList *changed = NULL;
GList *deleted = NULL;
GList *next;
GList *lp;
GDK_THREADS_ENTER ();
for (lp = monitor->watches; lp != NULL; lp = next)
{
watch = lp->data;
next = lp->next;
/* handle deletion of watches */
if (G_UNLIKELY (watch->callback == NULL))
{
#ifdef HAVE_KQUEUE
if (G_LIKELY (watch->fd >= 0))
{
/* prepare the delete command */
struct kevent ev;
ev.ident = watch->fd;
ev.filter = EVFILT_VNODE;
ev.flags = EV_DELETE;
ev.fflags = NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_RENAME | NOTE_REVOKE;
/* perform the delete operation */
if (kevent (monitor->kq, &ev, 1, NULL, 0, NULL) < 0)
g_error ("Failed to remove watch %d: %s", watch->id, g_strerror (errno));
/* drop the file descriptor */
close (watch->fd);
}
#endif
monitor->watches = g_list_delete_link (monitor->watches, lp);
g_free (watch);
continue;
}
#ifdef HAVE_KQUEUE
if (G_LIKELY (watch->fd >= 0))
continue;
#endif
ThunarVfsMonitor *monitor = THUNAR_VFS_MONITOR (user_data);
FAMEvent fe;
/* force an update on the info */
result = thunar_vfs_info_update (watch->info, NULL);
switch (result)
{
case THUNAR_VFS_INFO_RESULT_ERROR:
deleted = g_list_prepend (deleted, watch);
break;
case THUNAR_VFS_INFO_RESULT_CHANGED:
changed = g_list_prepend (changed, watch);
break;
case THUNAR_VFS_INFO_RESULT_NOCHANGE:
break;
default:
g_assert_not_reached ();
break;
}
}
/* notify all files that have been changed */
if (G_UNLIKELY (changed != NULL))
/* check for an error on the FAM connection */
if (G_UNLIKELY ((condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) != 0))
{
for (lp = changed; lp != NULL; lp = lp->next)
{
watch = lp->data;
if (G_LIKELY (watch->callback != NULL))
watch->callback (monitor, THUNAR_VFS_MONITOR_CHANGED, watch->info, watch->user_data);
}
g_list_free (changed);
error:
/* terminate the FAM connection */
GDK_THREADS_ENTER ();
thunar_vfs_monitor_fam_cancel (monitor);
GDK_THREADS_LEAVE ();
/* thats it, no more FAM */
return FALSE;
}
/* notify all files that have been deleted */
if (G_UNLIKELY (deleted != NULL))
/* process all pending FAM events */
while (FAMPending (&monitor->fc))
{
for (lp = deleted; lp != NULL; lp = lp->next)
{
watch = lp->data;
if (G_LIKELY (watch->callback != NULL))
watch->callback (monitor, THUNAR_VFS_MONITOR_DELETED, watch->info, watch->user_data);
}
g_list_free (deleted);
}
GDK_THREADS_LEAVE ();
#endif
return TRUE;
}
/* query the next pending event */
if (G_UNLIKELY (FAMNextEvent (&monitor->fc, &fe) < 0))
goto error;
/* we ignore all events other than changed/created/deleted */
if (G_UNLIKELY (fe.code != FAMChanged && fe.code != FAMCreated && fe.code != FAMDeleted))
continue;
/* process the event */
GDK_THREADS_ENTER ();
thunar_vfs_monitor_fam_event (monitor, &fe);
GDK_THREADS_LEAVE ();
}
static void
thunar_vfs_monitor_timer_destroy (gpointer user_data)
{
GDK_THREADS_ENTER ();
THUNAR_VFS_MONITOR (user_data)->timer_id = -1;
GDK_THREADS_LEAVE ();