File: gfam.c

package info (click to toggle)
gentoo 0.11.46-1
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 5,648 kB
  • ctags: 4,313
  • sloc: ansic: 33,912; sh: 3,903; makefile: 649; yacc: 316; sed: 16
file content (245 lines) | stat: -rw-r--r-- 7,282 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/*
** 2002-07-22 -	Finally, gentoo has learned to use FAM (File Alteration Monitor) if available. FAM,
**		from SGI, provides an application with the ability to be notified when a file or
**		directory changes. We use this to trigger pane reloads, so gentoo knows what's going
**		on in the filesystem.
**
**		FAM support is entierly optional: if you don't want it, or you system doesn't have it,
**		it will be built as stubs that do nothing. Use --disable-fam to manually turn it off.
**
**		Btw, this module is named "gfam" because plain "fam.h" collided with <fam.h>. Weird.
*/

#include "gentoo.h"

#if defined HAVE_FAM
#include <fam.h>
#endif

#include "dialog.h"
#include "dirpane.h"

#include "gfam.h"

/* If we don't have FAM, or it has been disabled, simply define the necessary functions
** as do-nothing stubs. Since the interface is (still?) pretty simple, it's easier and
** cleaner to do it in this all-at-once way, rather than scattering #if defined HAVE_FAMs
** all over the place. Perhaps the compiler can even optimize these stubs out?
*/
#if !defined HAVE_FAM

gboolean fam_initialize(MainInfo *min)
{
	return TRUE;
}

gboolean fam_is_active(void)
{
	return FALSE;
}

void fam_monitor(const DirPane *dp)
{
}

void fam_rescan_block(void)
{
}

void fam_rescan_unblock(void)
{
}

void fam_shutdown(MainInfo *min)
{
}

#else	/* If HAVE_FAM is actually defined, provide functional functions. */

/* ----------------------------------------------------------------------------------------- */

#define	MAGIC_OFFSET	16

static struct {
	gboolean	ok;
	MainInfo	*min;
	FAMConnection	conn;
	FAMRequest	req[2];
	guint		block_cnt;		/* If set, don't rescan. */
	guint		block_rescan;		/* Used to track what needs to be reloaded on unblock(). */
	guint		timeout[2];		/* Used to notify of pending refresh after rate limiting. */
} the_faminfo = { FALSE };

/* ----------------------------------------------------------------------------------------- */

/* 2003-09-30 -	Timeout handler to catch rate limited refreshes. Set when a refresh is denied
**		by the rate limiter, to do a final refresh since one is known to be needed.
*/
static gboolean evt_fam_timeout(gpointer data)
{
	guint index = GPOINTER_TO_UINT(data);

	if(the_faminfo.block_cnt)
		the_faminfo.block_rescan |= (1 << index);
	else
		dp_rescan(&the_faminfo.min->gui->pane[index]);

	the_faminfo.timeout[index] = 0U;
	return FALSE;
}

/* 2003-09-30 -	Rate limit pane rescans. Returns TRUE if pane <index> can do a rescan, FALSE
**		if it cannot.
*/
static gboolean rescan_rate_limit(guint index)
{
	const gfloat LIMIT = 0.300f;			/* Lower limit on period between refreshes. */
	static GTimeVal stamp[2] = { { 0 }, { 0 } };
	static gfloat elapsed[2] = { 0.0f, 0.0f};		/* Time since last rescan allowed. */
	GTimeVal now;
	gboolean ret = TRUE;

	g_get_current_time(&now);
	if(stamp[index].tv_sec && stamp[index].tv_usec)
	{
		gfloat dt = 1E-6f * (now.tv_usec - stamp[index].tv_usec) + (now.tv_sec - stamp[index].tv_sec);
		if(dt < LIMIT)
		{
			elapsed[index] += dt;
			if(elapsed[index] < LIMIT)	/* When blocking has reached limit, let a request through. */
			{
				if(the_faminfo.timeout[index])
					gtk_timeout_remove(the_faminfo.timeout[index]);
				the_faminfo.timeout[index] = gtk_timeout_add(1000.0f * LIMIT, evt_fam_timeout, GUINT_TO_POINTER(index));
				ret = FALSE;
			}
		}
	}
	stamp[index] = now;

	if(ret)	/* If we're allowing refresh, cancel any outstanding timer and reset bookkeeping. */
	{
		elapsed[index] = 0.0f;
		if(the_faminfo.timeout[index])
		{
			gtk_timeout_remove(the_faminfo.timeout[index]);
			the_faminfo.timeout[index] = 0U;
		}
	}
	return ret;
}

/* 2002-07-22 -	Rescan all panes whose index bit is set in <rescan>. Overkill. */
static void rescan_issue(guint rescan)
{
	guint	i, f = 0;

	for(i = 0; i < sizeof the_faminfo.req / sizeof *the_faminfo.req; i++)
	{
		if(rescan & (1 << i) && rescan_rate_limit(i))
		{
			dp_rescan(&the_faminfo.min->gui->pane[i]);
			f |= the_faminfo.min->gui->cur_pane == &the_faminfo.min->gui->pane[i];
		}
	}
	if(f)
		dp_show_stats(the_faminfo.min->gui->cur_pane);
}

/* 2002-07-22 -	GTK+ calls this whenever a FAM event is reported. We filter out the interesting
**		ones, and issue rescans on the affected panes, either now or buffered for later.
*/
static void evt_fam_monitor(gpointer data, gint source, GdkInputCondition cond)
{
	FAMEvent	evt;
	guint		rescan = 0U;

	while(FAMPending(&the_faminfo.conn))
	{
		if(FAMNextEvent(&the_faminfo.conn, &evt) == 1)	/* Returns -1 on error, annoyingly enough. */
		{
			if(evt.code < FAMAcknowledge)		/* Filter out events that wouldn't show up anyway. */
				rescan |= 1 << (evt.fr.reqnum - MAGIC_OFFSET);
		}
	}
	if(the_faminfo.block_cnt)
		the_faminfo.block_rescan |= rescan;	/* Non-destructive, important. */
	else if(rescan)
		rescan_issue(rescan);
}

/* 2002-07-22 -	Initialize FAM subsystem, by opening a connection and hooking up with GTK+'s event loop. */
gboolean fam_initialize(MainInfo *min)
{
	the_faminfo.min = min;
	if(FAMOpen2(&the_faminfo.conn, PACKAGE) == 0)
	{
		guint	i;

		for(i = 0; i < sizeof the_faminfo.req / sizeof *the_faminfo.req; i++)
			the_faminfo.req[i].reqnum = 0;

		/* Hook us into the main GTK+ event detection system. Simple. */
		gtk_input_add_full(FAMCONNECTION_GETFD(&the_faminfo.conn), GDK_INPUT_READ, evt_fam_monitor, NULL, NULL, NULL);
		the_faminfo.ok = TRUE;
		the_faminfo.block_cnt = 0;
	}
	else
		g_warning(_("FAM open failed, error %d--FAM will not be used"), FAMErrno);

	return the_faminfo.ok;
}

/* 2002-07-22 -	Just tell a caller if FAM is indeed in use. Says "FALSE" if built without FAM. */
gboolean fam_is_active(void)
{
	return the_faminfo.ok;
}

/* 2002-07-22 -	Start monitoring the directory of <dp>. Can't take path only, need to key it to pane index for rescan. */
void fam_monitor(const DirPane *dp)
{
	if(!the_faminfo.ok)
		return;
	if(the_faminfo.req[dp->index].reqnum > 0)
		FAMCancelMonitor(&the_faminfo.conn, &the_faminfo.req[dp->index]);
	the_faminfo.req[dp->index].reqnum = MAGIC_OFFSET + dp->index;
	if(FAMMonitorDirectory2(&the_faminfo.conn, dp->dir.path, &the_faminfo.req[dp->index]) != 0)
	{
		the_faminfo.req[dp->index].reqnum = 0;
		g_warning(_("Couldn't add FAM monitor on \"%s\", error %s (restart with --no-fam to go around, perhaps)"), dp->dir.path, FamErrlist[FAMErrno]);
	}
}

/* 2002-07-22 -	Caller is about to start doing stuff for which no rescans are allowed until unblock() is called.
**		This prevents dp_rescan() from reallocating the core buffers, and thus rendering the caller's
**		dp_get_selection() list to change meaning, which can be *very* devastating.
*/
void fam_rescan_block(void)
{
	if(the_faminfo.block_cnt == 0)
		the_faminfo.block_rescan = 0U;
	the_faminfo.block_cnt++;
}

/* 2002-07-22 -	Unblock rescans. If any FAM events occured during block, issue the rescans now. */
void fam_rescan_unblock(void)
{
	if(the_faminfo.block_cnt)
	{
		the_faminfo.block_cnt--;
		if(the_faminfo.block_cnt == 0U)
			rescan_issue(the_faminfo.block_rescan);
	}
	else
		g_warning("Non-balanced fam_rescan_unblock() call detected; underflow");
}

/* 2002-07-22 -	Shut down FAM system, by closing the connection. */
void fam_shutdown(MainInfo *min)
{
	if(the_faminfo.ok)
		FAMClose(&the_faminfo.conn);
}

#endif		/* HAVE_FAM */