nautilus/libnautilus-extensions/nautilus-monitor.c
Darin Adler 1d27a4336d reviewed by: Robin Slomkowski <rslomkow@eazel.com>
Update FAM support so it works fine on systems with and without
	libfam installed. So if you build Nautilus with FAM, then you can
	run it on a system with or without FAM.

	* configure.in:
	* libnautilus-extensions/Makefile.am:
	Remove the FAM_LIBS part.

	* libnautilus-extensions/nautilus-monitor.c: (get_fam_connection):
	Load the module before the FAMOpen2 call, and get all the function
	pointers.
	(get_event_uri), (process_fam_notifications),
	(nautilus_monitor_file), (nautilus_monitor_directory),
	(nautilus_monitor_cancel): Use the new CALL_FAM macro so we can
	use the function pointers when we call FAM.
2001-04-28 01:51:32 +00:00

396 lines
10 KiB
C

/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
nautilus-monitor.c: file and directory change monitoring for nautilus
Copyright (C) 2000, 2001 Eazel, Inc.
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 2 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, write to the
Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Authors: Seth Nickell <seth@eazel.com>
Darin Adler <darin@eazel.com>
*/
#include <config.h>
#include "nautilus-monitor.h"
#include <eel/eel-glib-extensions.h>
#ifdef HAVE_FAM_H
#include "nautilus-file-changes-queue.h"
#include <fam.h>
#include <gdk/gdk.h>
#include <gmodule.h>
#include <libgnome/gnome-defs.h>
#include <libgnome/gnome-util.h>
#include <libgnomevfs/gnome-vfs-utils.h>
/* Turn off this to make FAM calls the normal way rather than using
* GModule. This can be useful to check that the parameters to all the
* functions are still correct, but it won't link without configure
* and makefile changes.
*/
#define USE_FAM_AS_MODULE
#ifndef USE_FAM_AS_MODULE
#define CALL_FAM(f) FAM##f
#else /* USE_FAM_AS_MODULE */
typedef struct {
const char *name;
gpointer *function;
} ModuleSymbolPair;
static int (* pointer_FAMCancelMonitor) (FAMConnection *fc,
const FAMRequest *fr);
static int (* pointer_FAMClose) (FAMConnection *fc);
static int (* pointer_FAMMonitorDirectory) (FAMConnection *fc,
const char *filename,
FAMRequest *fr,
void *user_data);
static int (* pointer_FAMMonitorFile) (FAMConnection *fc,
const char *filename,
FAMRequest *fr,
void *user_data);
static int (* pointer_FAMOpen2) (FAMConnection *connection,
const char *name);
static int (* pointer_FAMNextEvent) (FAMConnection *fc,
FAMEvent *fe);
static int (* pointer_FAMPending) (FAMConnection *fc);
static const ModuleSymbolPair fam_symbols[] = {
#define IMPORT_FAM(f) { "FAM" #f, (gpointer *) &pointer_FAM##f },
IMPORT_FAM (CancelMonitor)
IMPORT_FAM (Close)
IMPORT_FAM (MonitorDirectory)
IMPORT_FAM (MonitorFile)
IMPORT_FAM (NextEvent)
IMPORT_FAM (Open2)
IMPORT_FAM (Pending)
#undef IMPORT_FAM
};
#define CALL_FAM(f) (* pointer_FAM##f)
#endif /* USE_FAM_AS_MODULE */
struct NautilusMonitor {
FAMRequest request;
};
static gboolean got_connection;
static void process_fam_notifications (gpointer callback_data,
int fd,
GdkInputCondition condition);
/* singleton object, instantiate and connect if it doesn't already exist */
static FAMConnection *
get_fam_connection (void)
{
static gboolean tried_connection;
static FAMConnection connection;
#ifdef USE_FAM_AS_MODULE
char *path;
GModule *module;
guint i;
#endif
/* Only try once. */
if (tried_connection) {
if (!got_connection) {
return NULL;
}
} else {
tried_connection = TRUE;
#ifdef USE_FAM_AS_MODULE
path = g_module_build_path (NULL, "fam");
module = g_module_open (path, 0);
g_free (path);
if (module == NULL) {
return NULL;
}
for (i = 0; i < EEL_N_ELEMENTS (fam_symbols); i++) {
if (!g_module_symbol (module,
fam_symbols[i].name,
fam_symbols[i].function)) {
return NULL;
}
}
#endif
if (CALL_FAM (Open2) (&connection, "Nautilus") != 0) {
return NULL;
}
/* Make the main loop's select function watch the FAM
* connection's file descriptor for us.
*/
gdk_input_add (FAMCONNECTION_GETFD (&connection),
GDK_INPUT_READ,
process_fam_notifications,
NULL);
got_connection = TRUE;
}
return &connection;
}
static GHashTable *
get_request_hash_table (void)
{
static GHashTable *table;
if (table == NULL) {
table = eel_g_hash_table_new_free_at_exit
(NULL, NULL, "nautilus-monitor.c: FAM requests");
}
return table;
}
static char *
get_event_uri (const FAMEvent *event)
{
const char *base_path;
char *path, *uri;
/* FAM doesn't tell us when something is a full path and when
* it's just partial so we have to look and see if it starts
* with a /.
*/
if (event->filename[0] == '/') {
return gnome_vfs_get_uri_from_local_path (event->filename);
}
/* Look up the directory registry that was used for this file
* notification and tack that on.
*/
base_path = g_hash_table_lookup (get_request_hash_table (),
GINT_TO_POINTER (FAMREQUEST_GETREQNUM (&event->fr)));
g_return_val_if_fail (base_path != NULL, NULL);
path = g_concat_dir_and_file (base_path, event->filename);
uri = gnome_vfs_get_uri_from_local_path (path);
g_free (path);
return uri;
}
static void
process_fam_notifications (gpointer callback_data, int fd, GdkInputCondition condition)
{
FAMConnection *connection;
FAMEvent event;
char *uri;
connection = get_fam_connection ();
g_return_if_fail (connection != NULL);
/* Process all the pending events right now. */
while (CALL_FAM (Pending) (connection)) {
if (CALL_FAM (NextEvent) (connection, &event) != 1) {
g_warning ("connection to FAM died");
gdk_input_remove (fd);
CALL_FAM (Close) (connection);
got_connection = FALSE;
return;
}
switch (event.code) {
case FAMChanged:
uri = get_event_uri (&event);
if (uri == NULL) {
break;
}
nautilus_file_changes_queue_file_changed (uri);
g_free (uri);
break;
case FAMDeleted:
uri = get_event_uri (&event);
if (uri == NULL) {
break;
}
nautilus_file_changes_queue_file_removed (uri);
g_free (uri);
break;
case FAMCreated:
uri = get_event_uri (&event);
if (uri == NULL) {
break;
}
nautilus_file_changes_queue_file_added (uri);
g_free (uri);
break;
case FAMStartExecuting:
/* Emitted when a file you are monitoring is
* executed. This should work for both
* binaries and shell scripts. Nautilus is not
* doing anything with this yet.
*/
break;
case FAMStopExecuting:
/* Emitted when a file you are monitoring
* ceases execution. Nautilus is not doing
* anything with this yet.
*/
break;
case FAMAcknowledge:
/* Called in response to a successful
* CancelMonitor. We don't need to do anything
* with this information.
*/
break;
case FAMExists:
/* Emitted when you start monitoring a
* directory. It tells you what's in the
* directory. Unhandled because Nautilus
* already handles this by calling
* gnome_vfs_directory_load, which gives us
* more information than merely the file name.
*/
break;
case FAMEndExist:
/* Emitted at the end of a FAMExists stream. */
break;
case FAMMoved:
/* FAMMoved doesn't need to be handled because
* FAM never seems to generate this event on
* Linux systems (w/ or w/o IMON). Instead it
* generates a FAMDeleted followed by a
* FAMCreated.
*/
g_warning ("unexpected FAMMoved notification");
break;
}
}
nautilus_file_changes_consume_changes (TRUE);
}
#endif /* HAVE_FAM_H */
gboolean
nautilus_monitor_active (void)
{
#ifndef HAVE_FAM_H
return FALSE;
#else
return get_fam_connection () != NULL;
#endif
}
NautilusMonitor *
nautilus_monitor_file (const char *uri)
{
#ifndef HAVE_FAM_H
return NULL;
#else
FAMConnection *connection;
char *path;
NautilusMonitor *monitor;
connection = get_fam_connection ();
if (connection == NULL) {
return NULL;
}
path = gnome_vfs_get_local_path_from_uri (uri);
if (path == NULL) {
return NULL;
}
monitor = g_new0 (NautilusMonitor, 1);
CALL_FAM (MonitorFile) (connection, path, &monitor->request, NULL);
g_free (path);
return monitor;
#endif
}
NautilusMonitor *
nautilus_monitor_directory (const char *uri)
{
#ifndef HAVE_FAM_H
return NULL;
#else
FAMConnection *connection;
char *path;
NautilusMonitor *monitor;
connection = get_fam_connection ();
if (connection == NULL) {
return NULL;
}
path = gnome_vfs_get_local_path_from_uri (uri);
if (path == NULL) {
return NULL;
}
monitor = g_new0 (NautilusMonitor, 1);
CALL_FAM (MonitorDirectory) (connection, path, &monitor->request, NULL);
g_assert (g_hash_table_lookup (get_request_hash_table (),
GINT_TO_POINTER (FAMREQUEST_GETREQNUM (&monitor->request))) == NULL);
g_hash_table_insert (get_request_hash_table (),
GINT_TO_POINTER (FAMREQUEST_GETREQNUM (&monitor->request)),
path);
return monitor;
#endif
}
void
nautilus_monitor_cancel (NautilusMonitor *monitor)
{
#ifndef HAVE_FAM_H
g_return_if_fail (monitor == NULL);
#else
FAMConnection *connection;
int reqnum;
char *path;
if (monitor == NULL) {
return;
}
reqnum = FAMREQUEST_GETREQNUM (&monitor->request);
path = g_hash_table_lookup (get_request_hash_table (),
GINT_TO_POINTER (reqnum));
g_hash_table_remove (get_request_hash_table (),
GINT_TO_POINTER (reqnum));
g_free (path);
connection = get_fam_connection ();
g_return_if_fail (connection != NULL);
CALL_FAM (CancelMonitor) (connection, &monitor->request);
g_free (monitor);
#endif
}